Source for gnu.java.nio.channels.FileChannelImpl

   1: /* FileChannelImpl.java -- 
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.nio.channels;
  40: 
  41: import gnu.classpath.Configuration;
  42: import gnu.java.nio.FileLockImpl;
  43: 
  44: import java.io.File;
  45: import java.io.FileNotFoundException;
  46: import java.io.IOException;
  47: import java.nio.ByteBuffer;
  48: import java.nio.MappedByteBuffer;
  49: import java.nio.channels.ClosedChannelException;
  50: import java.nio.channels.FileChannel;
  51: import java.nio.channels.FileLock;
  52: import java.nio.channels.NonReadableChannelException;
  53: import java.nio.channels.NonWritableChannelException;
  54: import java.nio.channels.ReadableByteChannel;
  55: import java.nio.channels.WritableByteChannel;
  56: 
  57: /**
  58:  * This file is not user visible !
  59:  * But alas, Java does not have a concept of friendly packages
  60:  * so this class is public. 
  61:  * Instances of this class are created by invoking getChannel
  62:  * Upon a Input/Output/RandomAccessFile object.
  63:  */
  64: public final class FileChannelImpl extends FileChannel
  65: {
  66:   // These are mode values for open().
  67:   public static final int READ   = 1;
  68:   public static final int WRITE  = 2;
  69:   public static final int APPEND = 4;
  70: 
  71:   // EXCL is used only when making a temp file.
  72:   public static final int EXCL   = 8;
  73:   public static final int SYNC   = 16;
  74:   public static final int DSYNC  = 32;
  75: 
  76:   private static native void init();
  77: 
  78:   static
  79:   {
  80:     if (Configuration.INIT_LOAD_LIBRARY)
  81:       {
  82:         System.loadLibrary("javanio");
  83:       }
  84:     
  85:     init();
  86:   }
  87: 
  88:   /**
  89:    * This is the actual native file descriptor value
  90:    */
  91:   // System's notion of file descriptor.  It might seem redundant to
  92:   // initialize this given that it is reassigned in the constructors.
  93:   // However, this is necessary because if open() throws an exception
  94:   // we want to make sure this has the value -1.  This is the most
  95:   // efficient way to accomplish that.
  96:   private int fd = -1;
  97: 
  98:   private long pos;
  99:   private int mode;
 100: 
 101:   public FileChannelImpl ()
 102:   {
 103:   }
 104: 
 105:   /* Open a file.  MODE is a combination of the above mode flags. */
 106:   public FileChannelImpl (File file, int mode) throws FileNotFoundException
 107:   {
 108:     final String path = file.getPath();
 109:     fd = open (path, mode);
 110:     this.mode = mode;
 111: 
 112:     // First open the file and then check if it is a a directory
 113:     // to avoid race condition.
 114:     if (file.isDirectory())
 115:       {
 116:     try 
 117:       {
 118:           close();
 119:       }
 120:     catch (IOException e)
 121:       {
 122:           /* ignore it */
 123:       }
 124: 
 125:     throw new FileNotFoundException(path + " is a directory");
 126:       }
 127:   }
 128: 
 129:   /* Used by init() (native code) */
 130:   FileChannelImpl (int fd, int mode)
 131:   {
 132:     this.fd = fd;
 133:     this.mode = mode;
 134:   }
 135: 
 136:   public static FileChannelImpl in;
 137:   public static FileChannelImpl out;
 138:   public static FileChannelImpl err;
 139: 
 140:   private native int open (String path, int mode) throws FileNotFoundException;
 141: 
 142:   public native int available () throws IOException;
 143:   private native long implPosition () throws IOException;
 144:   private native void seek (long newPosition) throws IOException;
 145:   private native void implTruncate (long size) throws IOException;
 146:   
 147:   public native void unlock (long pos, long len) throws IOException;
 148: 
 149:   public native long size () throws IOException;
 150:     
 151:   protected native void implCloseChannel() throws IOException;
 152: 
 153:   /**
 154:    * Makes sure the Channel is properly closed.
 155:    */
 156:   protected void finalize() throws IOException
 157:   {
 158:     this.close();
 159:   }
 160: 
 161:   public int read (ByteBuffer dst) throws IOException
 162:   {
 163:     int result;
 164:     byte[] buffer = new byte [dst.remaining ()];
 165:     
 166:     result = read (buffer, 0, buffer.length);
 167: 
 168:     if (result > 0)
 169:       dst.put (buffer, 0, result);
 170: 
 171:     return result;
 172:   }
 173: 
 174:   public int read (ByteBuffer dst, long position)
 175:     throws IOException
 176:   {
 177:     if (position < 0)
 178:       throw new IllegalArgumentException ();
 179:     long oldPosition = implPosition ();
 180:     position (position);
 181:     int result = read(dst);
 182:     position (oldPosition);
 183:     
 184:     return result;
 185:   }
 186: 
 187:   public native int read ()
 188:     throws IOException;
 189: 
 190:   public native int read (byte[] buffer, int offset, int length)
 191:     throws IOException;
 192: 
 193:   public long read (ByteBuffer[] dsts, int offset, int length)
 194:     throws IOException
 195:   {
 196:     long result = 0;
 197: 
 198:     for (int i = offset; i < offset + length; i++)
 199:       {
 200:         result += read (dsts [i]);
 201:       }
 202: 
 203:     return result;
 204:   }
 205: 
 206:   public int write (ByteBuffer src) throws IOException
 207:   {
 208:     int len = src.remaining ();
 209:     if (src.hasArray())
 210:       {
 211:     byte[] buffer = src.array();
 212:     write(buffer, src.arrayOffset() + src.position(), len);
 213:     src.position(src.position() + len);
 214:       }
 215:     else
 216:       {
 217:     // Use a more efficient native method! FIXME!
 218:     byte[] buffer = new byte [len];
 219:         src.get (buffer, 0, len);
 220:     write (buffer, 0, len);
 221:       }
 222:     return len;
 223:   }
 224:     
 225:   public int write (ByteBuffer src, long position)
 226:     throws IOException
 227:   {
 228:     if (position < 0)
 229:       throw new IllegalArgumentException ();
 230: 
 231:     if (!isOpen ())
 232:       throw new ClosedChannelException ();
 233:     
 234:     if ((mode & WRITE) == 0)
 235:        throw new NonWritableChannelException ();
 236: 
 237:     int result;
 238:     long oldPosition;
 239: 
 240:     oldPosition = implPosition ();
 241:     seek (position);
 242:     result = write(src);
 243:     seek (oldPosition);
 244:     
 245:     return result;
 246:   }
 247: 
 248:   public native void write (byte[] buffer, int offset, int length)
 249:     throws IOException;
 250:   
 251:   public native void write (int b) throws IOException;
 252: 
 253:   public long write(ByteBuffer[] srcs, int offset, int length)
 254:     throws IOException
 255:   {
 256:     long result = 0;
 257: 
 258:     for (int i = offset;i < offset + length;i++)
 259:       {
 260:         result += write (srcs[i]);
 261:       }
 262:     
 263:     return result;
 264:   }
 265:                    
 266:   public native MappedByteBuffer mapImpl (char mode, long position, int size)
 267:     throws IOException;
 268: 
 269:   public MappedByteBuffer map (FileChannel.MapMode mode,
 270:                    long position, long size)
 271:     throws IOException
 272:   {
 273:     char nmode = 0;
 274:     if (mode == MapMode.READ_ONLY)
 275:       {
 276:     nmode = 'r';
 277:     if ((this.mode & READ) == 0)
 278:       throw new NonReadableChannelException();
 279:       }
 280:     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
 281:       {
 282:     nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
 283:     if ((this.mode & (READ|WRITE)) != (READ|WRITE))
 284:       throw new NonWritableChannelException();
 285:       }
 286:     else
 287:       throw new IllegalArgumentException ();
 288:     
 289:     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
 290:       throw new IllegalArgumentException ();
 291:     return mapImpl(nmode, position, (int) size);
 292:   }
 293: 
 294:   /**
 295:    * msync with the disk
 296:    */
 297:   public void force (boolean metaData) throws IOException
 298:   {
 299:     if (!isOpen ())
 300:       throw new ClosedChannelException ();
 301:   }
 302: 
 303:   // like transferTo, but with a count of less than 2Gbytes
 304:   private int smallTransferTo (long position, int count, 
 305:                    WritableByteChannel target)
 306:     throws IOException
 307:   {
 308:     ByteBuffer buffer;
 309:     try
 310:       {
 311:     // Try to use a mapped buffer if we can.  If this fails for
 312:     // any reason we'll fall back to using a ByteBuffer.
 313:     buffer = map (MapMode.READ_ONLY, position, count);
 314:       }
 315:     catch (IOException e)
 316:       {
 317:     buffer = ByteBuffer.allocate (count);
 318:     read (buffer, position);
 319:     buffer.flip();
 320:       }
 321: 
 322:     return target.write (buffer);
 323:   }
 324: 
 325:   public long transferTo (long position, long count, 
 326:               WritableByteChannel target)
 327:     throws IOException
 328:   {
 329:     if (position < 0
 330:         || count < 0)
 331:       throw new IllegalArgumentException ();
 332: 
 333:     if (!isOpen ())
 334:       throw new ClosedChannelException ();
 335: 
 336:     if ((mode & READ) == 0)
 337:        throw new NonReadableChannelException ();
 338:    
 339:     final int pageSize = 65536;
 340:     long total = 0;
 341: 
 342:     while (count > 0)
 343:       {
 344:     int transferred 
 345:       = smallTransferTo (position, (int)Math.min (count, pageSize), 
 346:                  target);
 347:     if (transferred < 0)
 348:       break;
 349:     total += transferred;
 350:     position += transferred;
 351:     count -= transferred;
 352:       }
 353: 
 354:     return total;
 355:   }
 356: 
 357:   // like transferFrom, but with a count of less than 2Gbytes
 358:   private int smallTransferFrom (ReadableByteChannel src, long position, 
 359:                  int count)
 360:     throws IOException
 361:   {
 362:     ByteBuffer buffer = null;
 363: 
 364:     if (src instanceof FileChannel)
 365:       {
 366:     try
 367:       {
 368:         // Try to use a mapped buffer if we can.  If this fails
 369:         // for any reason we'll fall back to using a ByteBuffer.
 370:         buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position, 
 371:                          count);
 372:       }
 373:     catch (IOException e)
 374:       {
 375:       }
 376:       }
 377: 
 378:     if (buffer == null)
 379:       {
 380:     buffer = ByteBuffer.allocate ((int) count);
 381:     src.read (buffer);
 382:     buffer.flip();
 383:       }
 384: 
 385:     return write (buffer, position);
 386:   }
 387: 
 388:   public long transferFrom (ReadableByteChannel src, long position, 
 389:                 long count)
 390:     throws IOException
 391:   {
 392:     if (position < 0
 393:         || count < 0)
 394:       throw new IllegalArgumentException ();
 395: 
 396:     if (!isOpen ())
 397:       throw new ClosedChannelException ();
 398: 
 399:     if ((mode & WRITE) == 0)
 400:        throw new NonWritableChannelException ();
 401: 
 402:     final int pageSize = 65536;
 403:     long total = 0;
 404: 
 405:     while (count > 0)
 406:       {
 407:     int transferred = smallTransferFrom (src, position, 
 408:                          (int)Math.min (count, pageSize));
 409:     if (transferred < 0)
 410:       break;
 411:     total += transferred;
 412:     position += transferred;
 413:     count -= transferred;
 414:       }
 415: 
 416:     return total;
 417:   }
 418: 
 419:   public FileLock tryLock (long position, long size, boolean shared)
 420:     throws IOException
 421:   {
 422:     if (position < 0
 423:         || size < 0)
 424:       throw new IllegalArgumentException ();
 425: 
 426:     if (!isOpen ())
 427:       throw new ClosedChannelException ();
 428: 
 429:     if (shared && (mode & READ) == 0)
 430:       throw new NonReadableChannelException ();
 431:     
 432:     if (!shared && (mode & WRITE) == 0)
 433:       throw new NonWritableChannelException ();
 434:     
 435:     boolean completed = false;
 436:     
 437:     try
 438:       {
 439:     begin();
 440:     boolean lockable = lock(position, size, shared, false);
 441:     completed = true;
 442:     return (lockable
 443:         ? new FileLockImpl(this, position, size, shared)
 444:         : null);
 445:       }
 446:     finally
 447:       {
 448:     end(completed);
 449:       }
 450:   }
 451: 
 452:   /** Try to acquire a lock at the given position and size.
 453:    * On success return true.
 454:    * If wait as specified, block until we can get it.
 455:    * Otherwise return false.
 456:    */
 457:   private native boolean lock(long position, long size,
 458:                   boolean shared, boolean wait) throws IOException;
 459:   
 460:   public FileLock lock (long position, long size, boolean shared)
 461:     throws IOException
 462:   {
 463:     if (position < 0
 464:         || size < 0)
 465:       throw new IllegalArgumentException ();
 466: 
 467:     if (!isOpen ())
 468:       throw new ClosedChannelException ();
 469: 
 470:     boolean completed = false;
 471: 
 472:     try
 473:       {
 474:     boolean lockable = lock(position, size, shared, true);
 475:     completed = true;
 476:     return (lockable
 477:         ? new FileLockImpl(this, position, size, shared)
 478:         : null);
 479:       }
 480:     finally
 481:       {
 482:     end(completed);
 483:       }
 484:   }
 485: 
 486:   public long position ()
 487:     throws IOException
 488:   {
 489:     if (!isOpen ())
 490:       throw new ClosedChannelException ();
 491: 
 492:     return implPosition ();
 493:   }
 494:   
 495:   public FileChannel position (long newPosition)
 496:     throws IOException
 497:   {
 498:     if (newPosition < 0)
 499:       throw new IllegalArgumentException ();
 500: 
 501:     if (!isOpen ())
 502:       throw new ClosedChannelException ();
 503: 
 504:     // FIXME note semantics if seeking beyond eof.
 505:     // We should seek lazily - only on a write.
 506:     seek (newPosition);
 507:     return this;
 508:   }
 509:   
 510:   public FileChannel truncate (long size)
 511:     throws IOException
 512:   {
 513:     if (size < 0)
 514:       throw new IllegalArgumentException ();
 515: 
 516:     if (!isOpen ())
 517:       throw new ClosedChannelException ();
 518: 
 519:     if ((mode & WRITE) == 0)
 520:        throw new NonWritableChannelException ();
 521: 
 522:     if (size < size ())
 523:       implTruncate (size);
 524: 
 525:     return this;
 526:   }
 527: }