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. */ create(File file, int mode)138 public static FileChannelImpl create(File file, int mode) 139 throws IOException 140 { 141 return new FileChannelImpl(file, mode); 142 } 143 FileChannelImpl(File file, int mode)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 */ FileChannelImpl(VMChannel ch, int mode)179 FileChannelImpl (VMChannel ch, int mode) 180 { 181 this.mode = mode; 182 this.description = "descriptor(" + ch.getState() + ")"; 183 this.ch = ch; 184 } 185 available()186 public int available() throws IOException 187 { 188 return ch.available(); 189 } 190 implPosition()191 private long implPosition() throws IOException 192 { 193 return ch.position(); 194 } 195 seek(long newPosition)196 private void seek(long newPosition) throws IOException 197 { 198 ch.seek(newPosition); 199 } 200 implTruncate(long size)201 private void implTruncate(long size) throws IOException 202 { 203 ch.truncate(size); 204 } 205 unlock(long pos, long len)206 public void unlock(long pos, long len) throws IOException 207 { 208 ch.unlock(pos, len); 209 } 210 size()211 public long size () throws IOException 212 { 213 return ch.size(); 214 } 215 implCloseChannel()216 protected void implCloseChannel() throws IOException 217 { 218 ch.close(); 219 } 220 221 /** 222 * Makes sure the Channel is properly closed. 223 */ finalize()224 protected void finalize() throws IOException 225 { 226 if (ch.getState().isValid()) 227 close(); 228 } 229 read(ByteBuffer dst)230 public int read (ByteBuffer dst) throws IOException 231 { 232 return ch.read(dst); 233 } 234 read(ByteBuffer dst, long position)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 read()248 public int read() throws IOException 249 { 250 return ch.read(); 251 } 252 read(ByteBuffer[] dsts, int offset, int length)253 public long read (ByteBuffer[] dsts, int offset, int length) 254 throws IOException 255 { 256 return ch.readScattering(dsts, offset, length); 257 } 258 write(ByteBuffer src)259 public int write (ByteBuffer src) throws IOException 260 { 261 return ch.write(src); 262 } 263 write(ByteBuffer src, long position)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 write(int b)287 public void write (int b) throws IOException 288 { 289 ch.write(b); 290 } 291 write(ByteBuffer[] srcs, int offset, int length)292 public long write(ByteBuffer[] srcs, int offset, int length) 293 throws IOException 294 { 295 return ch.writeGathering(srcs, offset, length); 296 } 297 map(FileChannel.MapMode mode, long position, long size)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 */ force(boolean metaData)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 smallTransferTo(long position, int count, WritableByteChannel target)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 transferTo(long position, long count, WritableByteChannel target)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 smallTransferFrom(ReadableByteChannel src, long position, int count)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 transferFrom(ReadableByteChannel src, long position, long count)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. lockCheck(long position, long size, boolean shared)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 tryLock(long position, long size, boolean shared)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 lock(long position, long size, boolean shared)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 position()515 public long position () 516 throws IOException 517 { 518 if (!isOpen ()) 519 throw new ClosedChannelException (); 520 521 return implPosition (); 522 } 523 position(long newPosition)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 truncate(long size)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 toString()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 } 573