Source for gnu.java.nio.channels.FileChannelImpl

   1: /* FileChannelImpl.java -- 
   2:    Copyright (C) 2002, 2004, 2005, 2006  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:   /* This is a static factory method, so that VM implementors can decide
 107:    * substitute subclasses of FileChannelImpl. */
 108:   public static FileChannelImpl create(File file, int mode)
 109:     throws FileNotFoundException
 110:   {
 111:     return new FileChannelImpl(file, mode);
 112:   }
 113: 
 114:   /* Open a file.  MODE is a combination of the above mode flags. */
 115:   private FileChannelImpl (File file, int mode) throws FileNotFoundException
 116:   {
 117:     final String path = file.getPath();
 118:     fd = open (path, mode);
 119:     this.mode = mode;
 120: 
 121:     // First open the file and then check if it is a a directory
 122:     // to avoid race condition.
 123:     if (file.isDirectory())
 124:       {
 125:     try 
 126:       {
 127:           close();
 128:       }
 129:     catch (IOException e)
 130:       {
 131:           /* ignore it */
 132:       }
 133: 
 134:     throw new FileNotFoundException(path + " is a directory");
 135:       }
 136:   }
 137: 
 138:   /* Used by init() (native code) */
 139:   FileChannelImpl (int fd, int mode)
 140:   {
 141:     this.fd = fd;
 142:     this.mode = mode;
 143:   }
 144: 
 145:   public static FileChannelImpl in;
 146:   public static FileChannelImpl out;
 147:   public static FileChannelImpl err;
 148: 
 149:   private native int open (String path, int mode) throws FileNotFoundException;
 150: 
 151:   public native int available () throws IOException;
 152:   private native long implPosition () throws IOException;
 153:   private native void seek (long newPosition) throws IOException;
 154:   private native void implTruncate (long size) throws IOException;
 155:   
 156:   public native void unlock (long pos, long len) throws IOException;
 157: 
 158:   public native long size () throws IOException;
 159:     
 160:   protected native void implCloseChannel() throws IOException;
 161: 
 162:   /**
 163:    * Makes sure the Channel is properly closed.
 164:    */
 165:   protected void finalize() throws IOException
 166:   {
 167:     this.close();
 168:   }
 169: 
 170:   public int read (ByteBuffer dst) throws IOException
 171:   {
 172:     int result;
 173:     byte[] buffer = new byte [dst.remaining ()];
 174:     
 175:     result = read (buffer, 0, buffer.length);
 176: 
 177:     if (result > 0)
 178:       dst.put (buffer, 0, result);
 179: 
 180:     return result;
 181:   }
 182: 
 183:   public int read (ByteBuffer dst, long position)
 184:     throws IOException
 185:   {
 186:     if (position < 0)
 187:       throw new IllegalArgumentException ();
 188:     long oldPosition = implPosition ();
 189:     position (position);
 190:     int result = read(dst);
 191:     position (oldPosition);
 192:     
 193:     return result;
 194:   }
 195: 
 196:   public native int read ()
 197:     throws IOException;
 198: 
 199:   public native int read (byte[] buffer, int offset, int length)
 200:     throws IOException;
 201: 
 202:   public long read (ByteBuffer[] dsts, int offset, int length)
 203:     throws IOException
 204:   {
 205:     long result = 0;
 206: 
 207:     for (int i = offset; i < offset + length; i++)
 208:       {
 209:         result += read (dsts [i]);
 210:       }
 211: 
 212:     return result;
 213:   }
 214: 
 215:   public int write (ByteBuffer src) throws IOException
 216:   {
 217:     int len = src.remaining ();
 218:     if (src.hasArray())
 219:       {
 220:     byte[] buffer = src.array();
 221:     write(buffer, src.arrayOffset() + src.position(), len);
 222:     src.position(src.position() + len);
 223:       }
 224:     else
 225:       {
 226:     // Use a more efficient native method! FIXME!
 227:     byte[] buffer = new byte [len];
 228:         src.get (buffer, 0, len);
 229:     write (buffer, 0, len);
 230:       }
 231:     return len;
 232:   }
 233:     
 234:   public int write (ByteBuffer src, long position)
 235:     throws IOException
 236:   {
 237:     if (position < 0)
 238:       throw new IllegalArgumentException ();
 239: 
 240:     if (!isOpen ())
 241:       throw new ClosedChannelException ();
 242:     
 243:     if ((mode & WRITE) == 0)
 244:        throw new NonWritableChannelException ();
 245: 
 246:     int result;
 247:     long oldPosition;
 248: 
 249:     oldPosition = implPosition ();
 250:     seek (position);
 251:     result = write(src);
 252:     seek (oldPosition);
 253:     
 254:     return result;
 255:   }
 256: 
 257:   public native void write (byte[] buffer, int offset, int length)
 258:     throws IOException;
 259:   
 260:   public native void write (int b) throws IOException;
 261: 
 262:   public long write(ByteBuffer[] srcs, int offset, int length)
 263:     throws IOException
 264:   {
 265:     long result = 0;
 266: 
 267:     for (int i = offset;i < offset + length;i++)
 268:       {
 269:         result += write (srcs[i]);
 270:       }
 271:     
 272:     return result;
 273:   }
 274:                    
 275:   public native MappedByteBuffer mapImpl (char mode, long position, int size)
 276:     throws IOException;
 277: 
 278:   public MappedByteBuffer map (FileChannel.MapMode mode,
 279:                    long position, long size)
 280:     throws IOException
 281:   {
 282:     char nmode = 0;
 283:     if (mode == MapMode.READ_ONLY)
 284:       {
 285:     nmode = 'r';
 286:     if ((this.mode & READ) == 0)
 287:       throw new NonReadableChannelException();
 288:       }
 289:     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
 290:       {
 291:     nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
 292:     if ((this.mode & (READ|WRITE)) != (READ|WRITE))
 293:       throw new NonWritableChannelException();
 294:       }
 295:     else
 296:       throw new IllegalArgumentException ();
 297:     
 298:     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
 299:       throw new IllegalArgumentException ();
 300:     return mapImpl(nmode, position, (int) size);
 301:   }
 302: 
 303:   /**
 304:    * msync with the disk
 305:    */
 306:   public void force (boolean metaData) throws IOException
 307:   {
 308:     if (!isOpen ())
 309:       throw new ClosedChannelException ();
 310:   }
 311: 
 312:   // like transferTo, but with a count of less than 2Gbytes
 313:   private int smallTransferTo (long position, int count, 
 314:                    WritableByteChannel target)
 315:     throws IOException
 316:   {
 317:     ByteBuffer buffer;
 318:     try
 319:       {
 320:     // Try to use a mapped buffer if we can.  If this fails for
 321:     // any reason we'll fall back to using a ByteBuffer.
 322:     buffer = map (MapMode.READ_ONLY, position, count);
 323:       }
 324:     catch (IOException e)
 325:       {
 326:     buffer = ByteBuffer.allocate (count);
 327:     read (buffer, position);
 328:     buffer.flip();
 329:       }
 330: 
 331:     return target.write (buffer);
 332:   }
 333: 
 334:   public long transferTo (long position, long count, 
 335:               WritableByteChannel target)
 336:     throws IOException
 337:   {
 338:     if (position < 0
 339:         || count < 0)
 340:       throw new IllegalArgumentException ();
 341: 
 342:     if (!isOpen ())
 343:       throw new ClosedChannelException ();
 344: 
 345:     if ((mode & READ) == 0)
 346:        throw new NonReadableChannelException ();
 347:    
 348:     final int pageSize = 65536;
 349:     long total = 0;
 350: 
 351:     while (count > 0)
 352:       {
 353:     int transferred 
 354:       = smallTransferTo (position, (int)Math.min (count, pageSize), 
 355:                  target);
 356:     if (transferred < 0)
 357:       break;
 358:     total += transferred;
 359:     position += transferred;
 360:     count -= transferred;
 361:       }
 362: 
 363:     return total;
 364:   }
 365: 
 366:   // like transferFrom, but with a count of less than 2Gbytes
 367:   private int smallTransferFrom (ReadableByteChannel src, long position, 
 368:                  int count)
 369:     throws IOException
 370:   {
 371:     ByteBuffer buffer = null;
 372: 
 373:     if (src instanceof FileChannel)
 374:       {
 375:     try
 376:       {
 377:         // Try to use a mapped buffer if we can.  If this fails
 378:         // for any reason we'll fall back to using a ByteBuffer.
 379:         buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position, 
 380:                          count);
 381:       }
 382:     catch (IOException e)
 383:       {
 384:       }
 385:       }
 386: 
 387:     if (buffer == null)
 388:       {
 389:     buffer = ByteBuffer.allocate ((int) count);
 390:     src.read (buffer);
 391:     buffer.flip();
 392:       }
 393: 
 394:     return write (buffer, position);
 395:   }
 396: 
 397:   public long transferFrom (ReadableByteChannel src, long position, 
 398:                 long count)
 399:     throws IOException
 400:   {
 401:     if (position < 0
 402:         || count < 0)
 403:       throw new IllegalArgumentException ();
 404: 
 405:     if (!isOpen ())
 406:       throw new ClosedChannelException ();
 407: 
 408:     if ((mode & WRITE) == 0)
 409:        throw new NonWritableChannelException ();
 410: 
 411:     final int pageSize = 65536;
 412:     long total = 0;
 413: 
 414:     while (count > 0)
 415:       {
 416:     int transferred = smallTransferFrom (src, position, 
 417:                          (int)Math.min (count, pageSize));
 418:     if (transferred < 0)
 419:       break;
 420:     total += transferred;
 421:     position += transferred;
 422:     count -= transferred;
 423:       }
 424: 
 425:     return total;
 426:   }
 427: 
 428:   public FileLock tryLock (long position, long size, boolean shared)
 429:     throws IOException
 430:   {
 431:     if (position < 0
 432:         || size < 0)
 433:       throw new IllegalArgumentException ();
 434: 
 435:     if (!isOpen ())
 436:       throw new ClosedChannelException ();
 437: 
 438:     if (shared && (mode & READ) == 0)
 439:       throw new NonReadableChannelException ();
 440:     
 441:     if (!shared && (mode & WRITE) == 0)
 442:       throw new NonWritableChannelException ();
 443:     
 444:     boolean completed = false;
 445:     
 446:     try
 447:       {
 448:     begin();
 449:     boolean lockable = lock(position, size, shared, false);
 450:     completed = true;
 451:     return (lockable
 452:         ? new FileLockImpl(this, position, size, shared)
 453:         : null);
 454:       }
 455:     finally
 456:       {
 457:     end(completed);
 458:       }
 459:   }
 460: 
 461:   /** Try to acquire a lock at the given position and size.
 462:    * On success return true.
 463:    * If wait as specified, block until we can get it.
 464:    * Otherwise return false.
 465:    */
 466:   private native boolean lock(long position, long size,
 467:                   boolean shared, boolean wait) throws IOException;
 468:   
 469:   public FileLock lock (long position, long size, boolean shared)
 470:     throws IOException
 471:   {
 472:     if (position < 0
 473:         || size < 0)
 474:       throw new IllegalArgumentException ();
 475: 
 476:     if (!isOpen ())
 477:       throw new ClosedChannelException ();
 478: 
 479:     boolean completed = false;
 480: 
 481:     try
 482:       {
 483:     boolean lockable = lock(position, size, shared, true);
 484:     completed = true;
 485:     return (lockable
 486:         ? new FileLockImpl(this, position, size, shared)
 487:         : null);
 488:       }
 489:     finally
 490:       {
 491:     end(completed);
 492:       }
 493:   }
 494: 
 495:   public long position ()
 496:     throws IOException
 497:   {
 498:     if (!isOpen ())
 499:       throw new ClosedChannelException ();
 500: 
 501:     return implPosition ();
 502:   }
 503:   
 504:   public FileChannel position (long newPosition)
 505:     throws IOException
 506:   {
 507:     if (newPosition < 0)
 508:       throw new IllegalArgumentException ();
 509: 
 510:     if (!isOpen ())
 511:       throw new ClosedChannelException ();
 512: 
 513:     // FIXME note semantics if seeking beyond eof.
 514:     // We should seek lazily - only on a write.
 515:     seek (newPosition);
 516:     return this;
 517:   }
 518:   
 519:   public FileChannel truncate (long size)
 520:     throws IOException
 521:   {
 522:     if (size < 0)
 523:       throw new IllegalArgumentException ();
 524: 
 525:     if (!isOpen ())
 526:       throw new ClosedChannelException ();
 527: 
 528:     if ((mode & WRITE) == 0)
 529:        throw new NonWritableChannelException ();
 530: 
 531:     if (size < size ())
 532:       implTruncate (size);
 533: 
 534:     return this;
 535:   }
 536: 
 537:   /**
 538:    * @return The native file descriptor.
 539:    */
 540:   public int getNativeFD()
 541:   {
 542:     return fd;
 543:   }
 544: }