| /* FileChannelImpl.java -- | |
| Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.nio; | |
| import gnu.classpath.Configuration; | |
| import gnu.java.nio.FileLockImpl; | |
| import gnu.java.nio.VMChannel; | |
| import java.io.File; | |
| import java.io.FileNotFoundException; | |
| import java.io.IOException; | |
| import java.nio.ByteBuffer; | |
| import java.nio.MappedByteBuffer; | |
| import java.nio.channels.ClosedChannelException; | |
| import java.nio.channels.FileChannel; | |
| import java.nio.channels.FileLock; | |
| import java.nio.channels.NonReadableChannelException; | |
| import java.nio.channels.NonWritableChannelException; | |
| import java.nio.channels.ReadableByteChannel; | |
| import java.nio.channels.WritableByteChannel; | |
| /** | |
| * This file is not user visible ! | |
| * But alas, Java does not have a concept of friendly packages | |
| * so this class is public. | |
| * Instances of this class are created by invoking getChannel | |
| * Upon a Input/Output/RandomAccessFile object. | |
| */ | |
| public final class FileChannelImpl extends FileChannel | |
| { | |
| // These are mode values for open(). | |
| public static final int READ = 1; | |
| public static final int WRITE = 2; | |
| public static final int APPEND = 4; | |
| // EXCL is used only when making a temp file. | |
| public static final int EXCL = 8; | |
| public static final int SYNC = 16; | |
| public static final int DSYNC = 32; | |
| public static final FileChannelImpl in; | |
| public static final FileChannelImpl out; | |
| public static final FileChannelImpl err; | |
| //private static native void init(); | |
| static | |
| { | |
| if (Configuration.INIT_LOAD_LIBRARY) | |
| { | |
| System.loadLibrary("javanio"); | |
| } | |
| //init(); | |
| FileChannelImpl ch = null; | |
| try | |
| { | |
| ch = new FileChannelImpl(VMChannel.getStdin(), READ); | |
| } | |
| catch (IOException ioe) | |
| { | |
| throw new Error(ioe); | |
| } | |
| in = ch; | |
| ch = null; | |
| try | |
| { | |
| ch = new FileChannelImpl(VMChannel.getStdout(), WRITE); | |
| } | |
| catch (IOException ioe) | |
| { | |
| throw new Error(ioe); | |
| } | |
| out = ch; | |
| ch = null; | |
| try | |
| { | |
| ch = new FileChannelImpl(VMChannel.getStderr(), WRITE); | |
| } | |
| catch (IOException ioe) | |
| { | |
| throw new Error(ioe); | |
| } | |
| err = ch; | |
| } | |
| /** | |
| * This is the actual native file descriptor value | |
| */ | |
| private VMChannel ch; | |
| private int mode; | |
| final String description; | |
| /* Open a file. MODE is a combination of the above mode flags. */ | |
| /* This is a static factory method, so that VM implementors can decide | |
| * substitute subclasses of FileChannelImpl. */ | |
| public static FileChannelImpl create(File file, int mode) | |
| throws IOException | |
| { | |
| return new FileChannelImpl(file, mode); | |
| } | |
| private FileChannelImpl(File file, int mode) | |
| throws IOException | |
| { | |
| String path = file.getPath(); | |
| description = path; | |
| this.mode = mode; | |
| this.ch = new VMChannel(); | |
| ch.openFile(path, mode); | |
| // First open the file and then check if it is a a directory | |
| // to avoid race condition. | |
| if (file.isDirectory()) | |
| { | |
| try | |
| { | |
| close(); | |
| } | |
| catch (IOException e) | |
| { | |
| /* ignore it */ | |
| } | |
| throw new FileNotFoundException(description + " is a directory"); | |
| } | |
| } | |
| /** | |
| * Constructor for default channels in, out and err. | |
| * | |
| * Used by init() (native code). | |
| * | |
| * @param fd the file descriptor (0, 1, 2 for stdin, stdout, stderr). | |
| * | |
| * @param mode READ or WRITE | |
| */ | |
| FileChannelImpl (VMChannel ch, int mode) | |
| { | |
| this.mode = mode; | |
| this.description = "descriptor(" + ch.getState() + ")"; | |
| this.ch = ch; | |
| } | |
| public int available() throws IOException | |
| { | |
| return ch.available(); | |
| } | |
| private long implPosition() throws IOException | |
| { | |
| return ch.position(); | |
| } | |
| private void seek(long newPosition) throws IOException | |
| { | |
| ch.seek(newPosition); | |
| } | |
| private void implTruncate(long size) throws IOException | |
| { | |
| ch.truncate(size); | |
| } | |
| public void unlock(long pos, long len) throws IOException | |
| { | |
| ch.unlock(pos, len); | |
| } | |
| public long size () throws IOException | |
| { | |
| return ch.size(); | |
| } | |
| protected void implCloseChannel() throws IOException | |
| { | |
| ch.close(); | |
| } | |
| /** | |
| * Makes sure the Channel is properly closed. | |
| */ | |
| protected void finalize() throws IOException | |
| { | |
| if (ch.getState().isValid()) | |
| close(); | |
| } | |
| public int read (ByteBuffer dst) throws IOException | |
| { | |
| return ch.read(dst); | |
| } | |
| public int read (ByteBuffer dst, long position) | |
| throws IOException | |
| { | |
| if (position < 0) | |
| throw new IllegalArgumentException ("position: " + position); | |
| long oldPosition = implPosition (); | |
| position (position); | |
| int result = read(dst); | |
| position (oldPosition); | |
| return result; | |
| } | |
| public int read() throws IOException | |
| { | |
| return ch.read(); | |
| } | |
| public long read (ByteBuffer[] dsts, int offset, int length) | |
| throws IOException | |
| { | |
| return ch.readScattering(dsts, offset, length); | |
| } | |
| public int write (ByteBuffer src) throws IOException | |
| { | |
| return ch.write(src); | |
| } | |
| public int write (ByteBuffer src, long position) | |
| throws IOException | |
| { | |
| if (position < 0) | |
| throw new IllegalArgumentException ("position: " + position); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| if ((mode & WRITE) == 0) | |
| throw new NonWritableChannelException (); | |
| int result; | |
| long oldPosition; | |
| oldPosition = implPosition (); | |
| seek (position); | |
| result = write(src); | |
| seek (oldPosition); | |
| return result; | |
| } | |
| public void write (int b) throws IOException | |
| { | |
| ch.write(b); | |
| } | |
| public long write(ByteBuffer[] srcs, int offset, int length) | |
| throws IOException | |
| { | |
| return ch.writeGathering(srcs, offset, length); | |
| } | |
| public MappedByteBuffer map (FileChannel.MapMode mode, | |
| long position, long size) | |
| throws IOException | |
| { | |
| char nmode = 0; | |
| if (mode == MapMode.READ_ONLY) | |
| { | |
| nmode = 'r'; | |
| if ((this.mode & READ) == 0) | |
| throw new NonReadableChannelException(); | |
| } | |
| else if (mode == MapMode.READ_WRITE || mode == MapMode.PRIVATE) | |
| { | |
| nmode = mode == MapMode.READ_WRITE ? '+' : 'c'; | |
| if ((this.mode & WRITE) != WRITE) | |
| throw new NonWritableChannelException(); | |
| if ((this.mode & READ) != READ) | |
| throw new NonReadableChannelException(); | |
| } | |
| else | |
| throw new IllegalArgumentException ("mode: " + mode); | |
| if (position < 0 || size < 0 || size > Integer.MAX_VALUE) | |
| throw new IllegalArgumentException ("position: " + position | |
| + ", size: " + size); | |
| return ch.map(nmode, position, (int) size); | |
| } | |
| /** | |
| * msync with the disk | |
| */ | |
| public void force (boolean metaData) throws IOException | |
| { | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| ch.flush(metaData); | |
| } | |
| // like transferTo, but with a count of less than 2Gbytes | |
| private int smallTransferTo (long position, int count, | |
| WritableByteChannel target) | |
| throws IOException | |
| { | |
| ByteBuffer buffer; | |
| try | |
| { | |
| // Try to use a mapped buffer if we can. If this fails for | |
| // any reason we'll fall back to using a ByteBuffer. | |
| buffer = map (MapMode.READ_ONLY, position, count); | |
| } | |
| catch (IOException e) | |
| { | |
| buffer = ByteBuffer.allocate (count); | |
| read (buffer, position); | |
| buffer.flip(); | |
| } | |
| return target.write (buffer); | |
| } | |
| public long transferTo (long position, long count, | |
| WritableByteChannel target) | |
| throws IOException | |
| { | |
| if (position < 0 | |
| || count < 0) | |
| throw new IllegalArgumentException ("position: " + position | |
| + ", count: " + count); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| if ((mode & READ) == 0) | |
| throw new NonReadableChannelException (); | |
| final int pageSize = 65536; | |
| long total = 0; | |
| while (count > 0) | |
| { | |
| int transferred | |
| = smallTransferTo (position, (int)Math.min (count, pageSize), | |
| target); | |
| if (transferred < 0) | |
| break; | |
| total += transferred; | |
| position += transferred; | |
| count -= transferred; | |
| } | |
| return total; | |
| } | |
| // like transferFrom, but with a count of less than 2Gbytes | |
| private int smallTransferFrom (ReadableByteChannel src, long position, | |
| int count) | |
| throws IOException | |
| { | |
| ByteBuffer buffer = null; | |
| if (src instanceof FileChannel) | |
| { | |
| try | |
| { | |
| // Try to use a mapped buffer if we can. If this fails | |
| // for any reason we'll fall back to using a ByteBuffer. | |
| buffer = ((FileChannel)src).map (MapMode.READ_ONLY, position, | |
| count); | |
| } | |
| catch (IOException e) | |
| { | |
| } | |
| } | |
| if (buffer == null) | |
| { | |
| buffer = ByteBuffer.allocate (count); | |
| src.read (buffer); | |
| buffer.flip(); | |
| } | |
| return write (buffer, position); | |
| } | |
| public long transferFrom (ReadableByteChannel src, long position, | |
| long count) | |
| throws IOException | |
| { | |
| if (position < 0 | |
| || count < 0) | |
| throw new IllegalArgumentException ("position: " + position | |
| + ", count: " + count); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| if ((mode & WRITE) == 0) | |
| throw new NonWritableChannelException (); | |
| final int pageSize = 65536; | |
| long total = 0; | |
| while (count > 0) | |
| { | |
| int transferred = smallTransferFrom (src, position, | |
| (int)Math.min (count, pageSize)); | |
| if (transferred < 0) | |
| break; | |
| total += transferred; | |
| position += transferred; | |
| count -= transferred; | |
| } | |
| return total; | |
| } | |
| // Shared sanity checks between lock and tryLock methods. | |
| private void lockCheck(long position, long size, boolean shared) | |
| throws IOException | |
| { | |
| if (position < 0 | |
| || size < 0) | |
| throw new IllegalArgumentException ("position: " + position | |
| + ", size: " + size); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException(); | |
| if (shared && ((mode & READ) == 0)) | |
| throw new NonReadableChannelException(); | |
| if (!shared && ((mode & WRITE) == 0)) | |
| throw new NonWritableChannelException(); | |
| } | |
| public FileLock tryLock (long position, long size, boolean shared) | |
| throws IOException | |
| { | |
| lockCheck(position, size, shared); | |
| boolean completed = false; | |
| try | |
| { | |
| begin(); | |
| boolean lockable = ch.lock(position, size, shared, false); | |
| completed = true; | |
| return (lockable | |
| ? new FileLockImpl(this, position, size, shared) | |
| : null); | |
| } | |
| finally | |
| { | |
| end(completed); | |
| } | |
| } | |
| public FileLock lock (long position, long size, boolean shared) | |
| throws IOException | |
| { | |
| lockCheck(position, size, shared); | |
| boolean completed = false; | |
| try | |
| { | |
| boolean lockable = ch.lock(position, size, shared, true); | |
| completed = true; | |
| return (lockable | |
| ? new FileLockImpl(this, position, size, shared) | |
| : null); | |
| } | |
| finally | |
| { | |
| end(completed); | |
| } | |
| } | |
| public long position () | |
| throws IOException | |
| { | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| return implPosition (); | |
| } | |
| public FileChannel position (long newPosition) | |
| throws IOException | |
| { | |
| if (newPosition < 0) | |
| throw new IllegalArgumentException ("newPosition: " + newPosition); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| // FIXME note semantics if seeking beyond eof. | |
| // We should seek lazily - only on a write. | |
| seek (newPosition); | |
| return this; | |
| } | |
| public FileChannel truncate (long size) | |
| throws IOException | |
| { | |
| if (size < 0) | |
| throw new IllegalArgumentException ("size: " + size); | |
| if (!isOpen ()) | |
| throw new ClosedChannelException (); | |
| if ((mode & WRITE) == 0) | |
| throw new NonWritableChannelException (); | |
| if (size < size ()) | |
| implTruncate (size); | |
| return this; | |
| } | |
| public String toString() | |
| { | |
| return (super.toString() | |
| + "[ fd: " + ch.getState() | |
| + "; mode: " + Integer.toOctalString(mode) | |
| + "; " + description + " ]"); | |
| } | |
| /** | |
| * @return The native file descriptor. | |
| * / | |
| public int getNativeFD() | |
| { | |
| return fd; | |
| }*/ | |
| } |