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