Source for gnu.java.nio.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;
  40: 
  41: import gnu.classpath.Configuration;
  42: import gnu.java.nio.FileLockImpl;
  43: import gnu.java.nio.VMChannel;
  44: 
  45: import java.io.File;
  46: import java.io.FileNotFoundException;
  47: import java.io.IOException;
  48: import java.nio.ByteBuffer;
  49: import java.nio.MappedByteBuffer;
  50: import java.nio.channels.ClosedChannelException;
  51: import java.nio.channels.FileChannel;
  52: import java.nio.channels.FileLock;
  53: import java.nio.channels.NonReadableChannelException;
  54: import java.nio.channels.NonWritableChannelException;
  55: import java.nio.channels.ReadableByteChannel;
  56: import java.nio.channels.WritableByteChannel;
  57: 
  58: /**
  59:  * This file is not user visible !
  60:  * But alas, Java does not have a concept of friendly packages
  61:  * so this class is public.
  62:  * Instances of this class are created by invoking getChannel
  63:  * Upon a Input/Output/RandomAccessFile object.
  64:  */
  65: public final class FileChannelImpl extends FileChannel
  66: {
  67:   // These are mode values for open().
  68:   public static final int READ   = 1;
  69:   public static final int WRITE  = 2;
  70:   public static final int APPEND = 4;
  71: 
  72:   // EXCL is used only when making a temp file.
  73:   public static final int EXCL   = 8;
  74:   public static final int SYNC   = 16;
  75:   public static final int DSYNC  = 32;
  76: 
  77:   public static final FileChannelImpl in;
  78:   public static final FileChannelImpl out;
  79:   public static final FileChannelImpl err;
  80: 
  81:   //private static native void init();
  82: 
  83:   static
  84:   {
  85:     if (Configuration.INIT_LOAD_LIBRARY)
  86:       {
  87:         System.loadLibrary("javanio");
  88:       }
  89: 
  90:     //init();
  91: 
  92:     FileChannelImpl ch = null;
  93:     try
  94:       {
  95:         ch = new FileChannelImpl(VMChannel.getStdin(), READ);
  96:       }
  97:     catch (IOException ioe)
  98:       {
  99:         throw new Error(ioe);
 100:       }
 101:     in = ch;
 102: 
 103:     ch = null;
 104:     try
 105:       {
 106:         ch = new FileChannelImpl(VMChannel.getStdout(), WRITE);
 107:       }
 108:     catch (IOException ioe)
 109:       {
 110:         throw new Error(ioe);
 111:       }
 112:     out = ch;
 113: 
 114:     ch = null;
 115:     try
 116:       {
 117:         ch = new FileChannelImpl(VMChannel.getStderr(), WRITE);
 118:       }
 119:     catch (IOException ioe)
 120:       {
 121:         throw new Error(ioe);
 122:       }
 123:     err = ch;
 124:   }
 125: 
 126:   /**
 127:    * This is the actual native file descriptor value
 128:    */
 129:   private VMChannel ch;
 130: 
 131:   private int mode;
 132: 
 133:   final String description;
 134: 
 135:   /* Open a file.  MODE is a combination of the above mode flags. */
 136:   /* This is a static factory method, so that VM implementors can decide
 137:    * substitute subclasses of FileChannelImpl. */
 138:   public static FileChannelImpl create(File file, int mode)
 139:     throws IOException
 140:   {
 141:     return new FileChannelImpl(file, mode);
 142:   }
 143: 
 144:   private FileChannelImpl(File file, int mode)
 145:     throws IOException
 146:   {
 147:     String path = file.getPath();
 148:     description = path;
 149:     this.mode = mode;
 150:     this.ch = new VMChannel();
 151:     ch.openFile(path, mode);
 152: 
 153:     // First open the file and then check if it is a a directory
 154:     // to avoid race condition.
 155:     if (file.isDirectory())
 156:       {
 157:         try
 158:           {
 159:             close();
 160:           }
 161:         catch (IOException e)
 162:           {
 163:             /* ignore it */
 164:           }
 165: 
 166:         throw new FileNotFoundException(description + " is a directory");
 167:       }
 168:   }
 169: 
 170:   /**
 171:    * Constructor for default channels in, out and err.
 172:    *
 173:    * Used by init() (native code).
 174:    *
 175:    * @param fd the file descriptor (0, 1, 2 for stdin, stdout, stderr).
 176:    *
 177:    * @param mode READ or WRITE
 178:    */
 179:   FileChannelImpl (VMChannel ch, int mode)
 180:   {
 181:     this.mode = mode;
 182:     this.description = "descriptor(" + ch.getState() + ")";
 183:     this.ch = ch;
 184:   }
 185: 
 186:   public int available() throws IOException
 187:   {
 188:     return ch.available();
 189:   }
 190: 
 191:   private long implPosition() throws IOException
 192:   {
 193:     return ch.position();
 194:   }
 195: 
 196:   private void seek(long newPosition) throws IOException
 197:   {
 198:     ch.seek(newPosition);
 199:   }
 200: 
 201:   private void implTruncate(long size) throws IOException
 202:   {
 203:     ch.truncate(size);
 204:   }
 205: 
 206:   public void unlock(long pos, long len) throws IOException
 207:   {
 208:     ch.unlock(pos, len);
 209:   }
 210: 
 211:   public long size () throws IOException
 212:   {
 213:     return ch.size();
 214:   }
 215: 
 216:   protected void implCloseChannel() throws IOException
 217:   {
 218:     ch.close();
 219:   }
 220: 
 221:   /**
 222:    * Makes sure the Channel is properly closed.
 223:    */
 224:   protected void finalize() throws IOException
 225:   {
 226:     if (ch.getState().isValid())
 227:       close();
 228:   }
 229: 
 230:   public int read (ByteBuffer dst) throws IOException
 231:   {
 232:     return ch.read(dst);
 233:   }
 234: 
 235:   public int read (ByteBuffer dst, long position)
 236:     throws IOException
 237:   {
 238:     if (position < 0)
 239:       throw new IllegalArgumentException ("position: " + position);
 240:     long oldPosition = implPosition ();
 241:     position (position);
 242:     int result = read(dst);
 243:     position (oldPosition);
 244: 
 245:     return result;
 246:   }
 247: 
 248:   public int read() throws IOException
 249:   {
 250:     return ch.read();
 251:   }
 252: 
 253:   public long read (ByteBuffer[] dsts, int offset, int length)
 254:     throws IOException
 255:   {
 256:     return ch.readScattering(dsts, offset, length);
 257:   }
 258: 
 259:   public int write (ByteBuffer src) throws IOException
 260:   {
 261:     return ch.write(src);
 262:   }
 263: 
 264:   public int write (ByteBuffer src, long position)
 265:     throws IOException
 266:   {
 267:     if (position < 0)
 268:       throw new IllegalArgumentException ("position: " + position);
 269: 
 270:     if (!isOpen ())
 271:       throw new ClosedChannelException ();
 272: 
 273:     if ((mode & WRITE) == 0)
 274:        throw new NonWritableChannelException ();
 275: 
 276:     int result;
 277:     long oldPosition;
 278: 
 279:     oldPosition = implPosition ();
 280:     seek (position);
 281:     result = write(src);
 282:     seek (oldPosition);
 283: 
 284:     return result;
 285:   }
 286: 
 287:   public void write (int b) throws IOException
 288:   {
 289:     ch.write(b);
 290:   }
 291: 
 292:   public long write(ByteBuffer[] srcs, int offset, int length)
 293:     throws IOException
 294:   {
 295:     return ch.writeGathering(srcs, offset, length);
 296:   }
 297: 
 298:   public MappedByteBuffer map (FileChannel.MapMode mode,
 299:                                long position, long size)
 300:     throws IOException
 301:   {
 302:     char nmode = 0;
 303:     if (mode == MapMode.READ_ONLY)
 304:       {
 305:         nmode = 'r';
 306:         if ((this.mode & READ) == 0)
 307:           throw new NonReadableChannelException();
 308:       }
 309:     else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE)
 310:       {
 311:         nmode = mode == MapMode.READ_WRITE ? '+' : 'c';
 312:         if ((this.mode & WRITE) != WRITE)
 313:           throw new NonWritableChannelException();
 314:         if ((this.mode & READ) != READ)
 315:           throw new NonReadableChannelException();
 316:       }
 317:     else
 318:       throw new IllegalArgumentException ("mode: " + mode);
 319: 
 320:     if (position < 0 || size < 0 || size > Integer.MAX_VALUE)
 321:       throw new IllegalArgumentException ("position: " + position
 322:                                           + ", size: " + size);
 323:     return ch.map(nmode, position, (int) size);
 324:   }
 325: 
 326:   /**
 327:    * msync with the disk
 328:    */
 329:   public void force (boolean metaData) throws IOException
 330:   {
 331:     if (!isOpen ())
 332:       throw new ClosedChannelException ();
 333: 
 334:     ch.flush(metaData);
 335:   }
 336: 
 337:   // like transferTo, but with a count of less than 2Gbytes
 338:   private int smallTransferTo (long position, int count,
 339:                                WritableByteChannel target)
 340:     throws IOException
 341:   {
 342:     ByteBuffer buffer;
 343:     try
 344:       {
 345:         // Try to use a mapped buffer if we can.  If this fails for
 346:         // any reason we'll fall back to using a ByteBuffer.
 347:         buffer = map (MapMode.READ_ONLY, position, count);
 348:       }
 349:     catch (IOException e)
 350:       {
 351:         buffer = ByteBuffer.allocate (count);
 352:         read (buffer, position);
 353:         buffer.flip();
 354:       }
 355: 
 356:     return target.write (buffer);
 357:   }
 358: 
 359:   public long transferTo (long position, long count,
 360:                           WritableByteChannel target)
 361:     throws IOException
 362:   {
 363:     if (position < 0
 364:         || count < 0)
 365:       throw new IllegalArgumentException ("position: " + position
 366:                                           + ", count: " + count);
 367: 
 368:     if (!isOpen ())
 369:       throw new ClosedChannelException ();
 370: 
 371:     if ((mode & READ) == 0)
 372:        throw new NonReadableChannelException ();
 373: 
 374:     final int pageSize = 65536;
 375:     long total = 0;
 376: 
 377:     while (count > 0)
 378:       {
 379:         int transferred
 380:           = smallTransferTo (position, (int)Math.min (count, pageSize),
 381:                              target);
 382:         if (transferred < 0)
 383:           break;
 384:         total += transferred;
 385:         position += transferred;
 386:         count -= transferred;
 387:       }
 388: 
 389:     return total;
 390:   }
 391: 
 392:   // like transferFrom, but with a count of less than 2Gbytes
 393:   private int smallTransferFrom (ReadableByteChannel src, long position,
 394:                                  int count)
 395:     throws IOException
 396:   {
 397:     ByteBuffer buffer = null;
 398: 
 399:     if (src instanceof FileChannel)
 400:       {
 401:         try
 402:           {
 403:             // Try to use a mapped buffer if we can.  If this fails
 404:             // for any reason we'll fall back to using a ByteBuffer.
 405:             buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position,
 406:                                              count);
 407:           }
 408:         catch (IOException e)
 409:           {
 410:           }
 411:       }
 412: 
 413:     if (buffer == null)
 414:       {
 415:         buffer = ByteBuffer.allocate (count);
 416:         src.read (buffer);
 417:         buffer.flip();
 418:       }
 419: 
 420:     return write (buffer, position);
 421:   }
 422: 
 423:   public long transferFrom (ReadableByteChannel src, long position,
 424:                             long count)
 425:     throws IOException
 426:   {
 427:     if (position < 0
 428:         || count < 0)
 429:       throw new IllegalArgumentException ("position: " + position
 430:                                           + ", count: " + count);
 431: 
 432:     if (!isOpen ())
 433:       throw new ClosedChannelException ();
 434: 
 435:     if ((mode & WRITE) == 0)
 436:        throw new NonWritableChannelException ();
 437: 
 438:     final int pageSize = 65536;
 439:     long total = 0;
 440: 
 441:     while (count > 0)
 442:       {
 443:         int transferred = smallTransferFrom (src, position,
 444:                                              (int)Math.min (count, pageSize));
 445:         if (transferred < 0)
 446:           break;
 447:         total += transferred;
 448:         position += transferred;
 449:         count -= transferred;
 450:       }
 451: 
 452:     return total;
 453:   }
 454: 
 455:   // Shared sanity checks between lock and tryLock methods.
 456:   private void lockCheck(long position, long size, boolean shared)
 457:     throws IOException
 458:   {
 459:     if (position < 0
 460:         || size < 0)
 461:       throw new IllegalArgumentException ("position: " + position
 462:                                           + ", size: " + size);
 463: 
 464:     if (!isOpen ())
 465:       throw new ClosedChannelException();
 466: 
 467:     if (shared && ((mode & READ) == 0))
 468:       throw new NonReadableChannelException();
 469: 
 470:     if (!shared && ((mode & WRITE) == 0))
 471:       throw new NonWritableChannelException();
 472:   }
 473: 
 474:   public FileLock tryLock (long position, long size, boolean shared)
 475:     throws IOException
 476:   {
 477:     lockCheck(position, size, shared);
 478: 
 479:     boolean completed = false;
 480:     try
 481:       {
 482:         begin();
 483:         boolean lockable = ch.lock(position, size, shared, false);
 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 FileLock lock (long position, long size, boolean shared)
 496:     throws IOException
 497:   {
 498:     lockCheck(position, size, shared);
 499: 
 500:     boolean completed = false;
 501:     try
 502:       {
 503:         boolean lockable = ch.lock(position, size, shared, true);
 504:         completed = true;
 505:         return (lockable
 506:                 ? new FileLockImpl(this, position, size, shared)
 507:                 : null);
 508:       }
 509:     finally
 510:       {
 511:         end(completed);
 512:       }
 513:   }
 514: 
 515:   public long position ()
 516:     throws IOException
 517:   {
 518:     if (!isOpen ())
 519:       throw new ClosedChannelException ();
 520: 
 521:     return implPosition ();
 522:   }
 523: 
 524:   public FileChannel position (long newPosition)
 525:     throws IOException
 526:   {
 527:     if (newPosition < 0)
 528:       throw new IllegalArgumentException ("newPosition: " + newPosition);
 529: 
 530:     if (!isOpen ())
 531:       throw new ClosedChannelException ();
 532: 
 533:     // FIXME note semantics if seeking beyond eof.
 534:     // We should seek lazily - only on a write.
 535:     seek (newPosition);
 536:     return this;
 537:   }
 538: 
 539:   public FileChannel truncate (long size)
 540:     throws IOException
 541:   {
 542:     if (size < 0)
 543:       throw new IllegalArgumentException ("size: " + size);
 544: 
 545:     if (!isOpen ())
 546:       throw new ClosedChannelException ();
 547: 
 548:     if ((mode & WRITE) == 0)
 549:        throw new NonWritableChannelException ();
 550: 
 551:     if (size < size ())
 552:       implTruncate (size);
 553: 
 554:     return this;
 555:   }
 556: 
 557:   public String toString()
 558:   {
 559:     return (super.toString()
 560:             + "[ fd: " + ch.getState()
 561:             + "; mode: " + Integer.toOctalString(mode)
 562:             + "; " + description + " ]");
 563:   }
 564: 
 565:   /**
 566:    * @return The native file descriptor.
 567:    * /
 568:   public int getNativeFD()
 569:   {
 570:     return fd;
 571:   }*/
 572: }