standardReadWrite = new HashSet<>(Arrays.asList(StandardOpenOption.CREATE,
+ StandardOpenOption.READ,
+ StandardOpenOption.WRITE,
+ StandardOpenOption.DSYNC));
+
+ /**
+ * The reference to the file
+ */
+ private NfsFile, ?> _nfsFile;
+
+ /**
+ * The current value of the file position pointer.
+ */
+ private long _currentPosition = 0;
+
+ /**
+ * Lock for operations involving position and size
+ */
+ private final Object positionLock = new Object();
+
+ /**
+ * The current value of the file size.
+ */
+ private long _currentSize;
+
+ /**
+ * The NFS server's preferred write size.
+ */
+ private int _wtpref;
+
+ /**
+ * The NFS server's preferred read size.
+ */
+ private int _rtpref;
+
+ /**
+ * How the NFS server should write to the file.
+ *
+ * - UNSTABLE = 0 - Best effort, no promises.
+ * - DATA_SYNC = 1 - Commit all data to stable storage, plus enough
+ * metadata for retrieval, before returning.
+ * - FILE_SYNC = 2 - Commit all data and metadata to stable storage before
+ * returning.
+ *
+ */
+ private final int _syncType;
+
+ /**
+ * Instance of Flags class from the OpenOptions
+ */
+ private final Flags _flags;
+
+ /**
+ * Creates a file channel to read from an existing file represented by the
+ * specified NfsFile object, starting at position 0.
+ *
+ * @param nfsFile
+ * The file to be opened for reading.
+ * @throws IOException
+ * If the file exists but is a directory rather than a regular
+ * file or does not exist, or cannot be opened for any other reason.
+ */
+ public NfsFileChannel(NfsFile, ?> nfsFile) throws IOException {
+ this(nfsFile, StandardOpenOption.READ, StandardOpenOption.SYNC);
+ }
+
+ /**
+ * Creates a file channel to read, write and/or delete a file represented by the
+ * specified NfsFile object, starting at position 0.
+ *
+ * The options control behavior of this channel. The model of option handling was taken from
+ * {@link java.nio.channels.FileChannel#open(Path, Set, FileAttribute...) FileChannel.open()}.
+ *
+ * DSYNC corresponds to NfsWriteRequest.FILE_SYNC, SYNC corresponds to NfsWriteRequest.FILE_SYNC,
+ * and the absence of either corresponds to NfsWriteRequest.UNSTABLE.
+ *
+ * StandardOpenOption.SPARSE and NO_FOLLOW_LINKS are both ignored.
+ *
+ * @param nfsFile
+ * The file to be opened for reading, writing, or deleting
+ * @param options
+ * The OpenOptions
+ * @throws IOException
+ * If the file exists but is a directory rather than a regular
+ * file, does not exist but cannot be created, or cannot be
+ * opened for any other reason
+ * @see java.nio.channels.FileChannel#open(Path, Set, FileAttribute...)
+ */
+ public NfsFileChannel(NfsFile, ?> nfsFile, OpenOption... options) throws IOException {
+
+ _nfsFile = nfsFile;
+ Set set = new HashSet(options.length);
+ Collections.addAll(set, options);
+ _flags = Flags.toFlags(set);
+
+ if (_flags.dsync) {
+ _syncType = NfsWriteRequest.DATA_SYNC;
+ } else if (_flags.sync) {
+ _syncType = NfsWriteRequest.FILE_SYNC;
+ } else {
+ _syncType = NfsWriteRequest.UNSTABLE;
+ }
+ open();
+ }
+
+ /**
+ * Creates a file output stream to write to the file represented by the
+ * specified NfsFile object, starting at offset
+ * and using syncType behavior.
+ *
+ * If the file does not exist, it will first be created.
+ *
+ * @param nfsFile
+ * The file to be opened for writing.
+ * @param offset
+ * Where to start writing to the file.
+ * @param syncType
+ * One of the values below.
+ *
+ * - UNSTABLE = 0 - Best effort, no promises.
+ * - DATA_SYNC = 1 - Commit all data to stable storage, plus
+ * enough metadata for retrieval, before returning.
+ * - FILE_SYNC = 2 - Commit all data and metadata to stable
+ * storage before returning.
+ *
+ * @throws IOException
+ * If the file exists but is a directory rather than a regular
+ * file, does not exist but cannot be created, or cannot be
+ * opened for any other reason
+ */
+ public NfsFileChannel(NfsFile, ?> nfsFile, Set set) throws IOException {
+
+ _nfsFile = nfsFile;
+ _flags = Flags.toFlags(set);
+ if (_flags.dsync) {
+ _syncType = NfsWriteRequest.DATA_SYNC;
+ } else if (_flags.sync) {
+ _syncType = NfsWriteRequest.FILE_SYNC;
+ } else {
+ _syncType = NfsWriteRequest.UNSTABLE;
+ }
+ open();
+ }
+
+ /**
+ * Opens the file channel using the passed in parameters
+ * @throws IOException
+ */
+ private void open() throws IOException {
+
+ if (_flags.append && _flags.read)
+ throw new IllegalArgumentException(StandardOpenOption.APPEND.name()
+ + " + "
+ + StandardOpenOption.READ.name()
+ + " not allowed");
+
+ if (_flags.append && _flags.truncateExisting)
+ throw new IllegalArgumentException(StandardOpenOption.APPEND.name()
+ + " + "
+ + StandardOpenOption.TRUNCATE_EXISTING.name()
+ + " not allowed");
+
+ if (_nfsFile.exists()) {
+ _currentSize = _nfsFile.length();
+
+ if (_nfsFile.isDirectory())
+ throw new IOException("Entry is a directory");
+
+ if (_flags.createNew)
+ throw new IOException("File already exists");
+
+ if ((_flags.write) || (_flags.append)) {
+ // Validate the file.
+ if (!(_nfsFile.canExtend() && _nfsFile.canModify())) {
+ throw new IllegalArgumentException(
+ "The file must be writable by the client: " + _nfsFile.getAbsolutePath());
+ }
+
+ if (_flags.truncateExisting) {
+ NfsSetAttributes attributes = new NfsSetAttributes();
+ attributes.setSize(0L);
+ _nfsFile.setAttributes(attributes);
+ }
+ }
+ } else {
+ // If not opening for write or append, its read-only and file doesn't exist
+ if ((!_flags.write) && (!_flags.append))
+ throw new IOException("File does not exist");
+
+ NfsCreateMode createMode;
+ if (_flags.createNew)
+ createMode = NfsCreateMode.GUARDED;
+ else if (_flags.create)
+ createMode = NfsCreateMode.UNCHECKED;
+ else
+ throw new IOException("File does not exist"); // Not asked to create and the file doesn't exist
+
+ // Create the file.
+ NfsSetAttributes attributes = new NfsSetAttributes();
+ attributes.setMode(NfsFile.ownerReadModeBit
+ | NfsFile.ownerWriteModeBit
+ | NfsFile.ownerExecuteModeBit
+ | NfsFile.groupReadModeBit
+ | NfsFile.groupWriteModeBit
+ | NfsFile.othersReadModeBit
+ | NfsFile.othersWriteModeBit);
+ NfsCreateResponse response = _nfsFile.create(createMode, attributes, null);
+ if (!response.stateIsOk())
+ throw new IOException("Error " + response.getState() + " from create");
+ }
+
+ // Advance the position to the end
+ if (_flags.append) {
+ _currentPosition = _currentSize;
+ }
+
+ _wtpref = (int) Math.min(_nfsFile.fsinfo().getFsInfo().wtpref, Integer.MAX_VALUE);
+ _rtpref = (int) Math.min(_nfsFile.fsinfo().getFsInfo().rtpref, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Flush any uncommitted data to the file.
+ *
+ * Procedure COMMIT forces or flushes data to stable storage that was
+ * previously written with a WRITE procedure call with the stable field set
+ * to UNSTABLE.
+ *
+ *
+ * @param offsetToCommit
+ * The position within the file at which the flush is to begin.
+ * An offset of 0 means to flush data starting at the beginning
+ * of the file.
+ * @param dataSizeToCommit
+ * The number of bytes of data to flush. If count is 0, a flush
+ * from offset to the end of file is done.
+ * @throws IOException
+ * If some other I/O error occurs
+ * @throws ClosedChannelException
+ * If this channel is closed
+ */
+ public void commit(long offsetToCommit, int dataSizeToCommit) throws IOException, ClosedChannelException {
+ checkForClosed();
+ _nfsFile.commit(offsetToCommit, dataSizeToCommit);
+ }
+
+ /**
+ * NOTE: NfsFileChannel implementation is to call commit on the entire file range
+ * @see java.nio.channels.FileChannel#force(boolean)
+ */
+ public void force(boolean metaData) throws ClosedChannelException, IOException {
+ this.commit(0, 0);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#read(java.nio.ByteBuffer)
+ */
+ @Override
+ public int read(ByteBuffer dst) throws IOException, ClosedChannelException {
+ checkForClosed();
+
+ if (!_flags.read) {
+ throw new NonReadableChannelException();
+ } else if (!dst.hasRemaining()) {
+ return 0;
+ } else if (_currentPosition > _currentSize) {
+ return EOF;
+ }
+
+ synchronized (positionLock) {
+ int bytesRead = 0;
+ while (dst.remaining() > 0) {
+ NfsReadResponse response;
+ if (dst.hasArray()) {
+ response = _nfsFile.read(_currentPosition, Math.min(dst.remaining(), _rtpref), dst.array(), dst.position());
+ // Repositioning ByteBuffer because the call to read() does not.
+ if (response.getBytesRead() > 0)
+ dst.position(dst.position() + response.getBytesRead());
+
+ } else {
+ byte[] src = new byte[Math.min(dst.remaining(), _rtpref)];
+ response = _nfsFile.read(_currentPosition, src.length, src, dst.position());
+ if (response.getBytesRead() > 0)
+ dst.put(src, 0, response.getBytesRead());
+ }
+
+ if (!response.stateIsOk()) {
+ throw new IOException("Error " + response.getState() + " from read");
+ }
+
+ bytesRead += response.getBytesRead();
+ _currentPosition += response.getBytesRead();
+
+ if (response.isEof()) {
+ if (bytesRead > 0)
+ return bytesRead;
+ return EOF;
+ }
+
+ if (response.getBytesRead() == 0) {
+ throw new IOException("Returned zero bytes read");
+ }
+ }
+ return bytesRead;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#read(java.nio.ByteBuffer, long)
+ */
+ @Override
+ public int read(ByteBuffer dst, long position) throws IOException
+ {
+ checkForClosed();
+
+ if (!_flags.read) {
+ throw new NonReadableChannelException();
+ } else if (position < 0) {
+ throw new IllegalArgumentException("Negative position");
+ } else if (!dst.hasRemaining()) {
+ return 0;
+ } else if (position > _currentSize) {
+ return EOF;
+ }
+
+ synchronized (positionLock) {
+ int bytesRead = 0;
+ while (dst.remaining() > 0) {
+ NfsReadResponse response;
+ if (dst.hasArray()) {
+ response = _nfsFile.read(position, Math.min(dst.remaining(), _rtpref), dst.array(), dst.position());
+ // Repositioning ByteBuffer because the call to read() does not.
+ if (response.getBytesRead() > 0)
+ dst.position(dst.position() + response.getBytesRead());
+
+ } else {
+ byte[] src = new byte[Math.min(dst.remaining(), _rtpref)];
+ response = _nfsFile.read(position, src.length, src, dst.position());
+ if (response.getBytesRead() > 0)
+ dst.put(src, 0, response.getBytesRead());
+ }
+
+ if (!response.stateIsOk()) {
+ throw new IOException("Error " + response.getState() + " from read");
+ }
+
+ bytesRead += response.getBytesRead();
+ position += response.getBytesRead();
+
+ if (response.isEof()) {
+ if (bytesRead > 0)
+ return bytesRead;
+ return EOF;
+ }
+
+ if (response.getBytesRead() == 0) {
+ throw new IOException("Returned zero bytes read");
+ }
+ }
+ return bytesRead;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#read(java.nio.ByteBuffer[], int, int)
+ */
+ @Override
+ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException
+ {
+ checkForClosed();
+ if (!_flags.read) {
+ throw new NonReadableChannelException();
+ } else if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ long bytesRead = 0;
+ for (int i = offset; i < length; i++) {
+ bytesRead += this.read(dsts[i]);
+ }
+ return bytesRead;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#write(java.nio.ByteBuffer)
+ */
+ @Override
+ public int write(ByteBuffer src) throws IOException, ClosedChannelException {
+ checkForClosed();
+ if ((!_flags.write) && (!_flags.append)) {
+ throw new NonWritableChannelException();
+ } else if (!src.hasRemaining()) {
+ return 0;
+ }
+
+ synchronized (positionLock) {
+ long offsetWrite;
+ if (_flags.append)
+ offsetWrite = size();
+ else
+ offsetWrite = _currentPosition;
+
+ int bytesWritten = 0;
+ while (src.remaining() > 0) {
+ List payload = new ArrayList(1);
+ if (src.remaining() > _wtpref) {
+ if (src.hasArray()) {
+ payload.add(ByteBuffer.wrap(src.array(), bytesWritten, _wtpref));
+ } else {
+ ByteBuffer srcWrite = ByteBuffer.allocate(src.capacity());
+ src.mark();
+ srcWrite.put(src);
+ src.reset();
+ srcWrite.position(_wtpref);
+ srcWrite.flip();
+ payload.add(srcWrite);
+ }
+ } else {
+ payload.add(src);
+ }
+ /*
+ * Since there is no analog in NFSv3 to write to the end of a file atomically,
+ * this is a best guess effort by
+ */
+ NfsWriteResponse response = _nfsFile.write(offsetWrite, payload, _syncType);
+ if (!response.stateIsOk()) {
+ throw new IOException("Error " + response.getState() + " from write");
+ }
+ if (response.getCount() == 0) {
+ throw new IOException("Write return zero bytes written");
+ }
+ // Repositioning ByteBuffer because the call to write() does not.
+ src.position(src.position() + response.getCount());
+ bytesWritten += response.getCount();
+ offsetWrite += response.getCount();
+ _currentPosition = offsetWrite;
+ if (_currentPosition > _currentSize)
+ _currentSize = _currentPosition;
+ }
+ if (_flags.append) {
+ _currentPosition = size();
+ }
+ return bytesWritten;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#write(java.nio.ByteBuffer, long)
+ */
+ @Override
+ public int write(ByteBuffer src, long position) throws IOException
+ {
+ checkForClosed();
+ if ((!_flags.write) && (!_flags.append)) {
+ throw new NonWritableChannelException();
+ } else if (!src.hasRemaining()) {
+ return 0;
+ }
+
+ synchronized (positionLock) {
+ long offsetWrite;
+ if (_flags.append)
+ offsetWrite = size();
+ else
+ offsetWrite = position;
+
+ int bytesWritten = 0;
+ while (src.remaining() > 0) {
+ List payload = new ArrayList(1);
+ if (src.remaining() > _wtpref) {
+ if (src.hasArray()) {
+ payload.add(ByteBuffer.wrap(src.array(), bytesWritten, _wtpref));
+ } else {
+ ByteBuffer srcWrite = ByteBuffer.allocate(src.capacity());
+ src.mark();
+ srcWrite.put(src);
+ src.reset();
+ srcWrite.position(_wtpref);
+ srcWrite.flip();
+ payload.add(srcWrite);
+ }
+ } else {
+ payload.add(src);
+ }
+ /*
+ * Since there is no analog in NFSv3 to write to the end of a file atomically,
+ * this is a best guess effort by
+ */
+ NfsWriteResponse response = _nfsFile.write(offsetWrite, payload, _syncType);
+ if (!response.stateIsOk()) {
+ throw new IOException("Error " + response.getState() + " from write");
+ }
+ if (response.getCount() == 0) {
+ throw new IOException("Write return zero bytes written");
+ }
+ // Repositioning ByteBuffer because the call to write() does not.
+ src.position(src.position() + response.getCount());
+ bytesWritten += response.getCount();
+ offsetWrite += response.getCount();
+ position = offsetWrite;
+ if (position > _currentSize)
+ _currentSize = position;
+ }
+ if (_flags.append) {
+ position = size();
+ }
+ return bytesWritten;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#write(java.nio.ByteBuffer[], int, int)
+ */
+ @Override
+ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException, ClosedChannelException
+ {
+ checkForClosed();
+ if (!_flags.write && !_flags.append) {
+ throw new NonWritableChannelException();
+ } else if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ long bytesRead = 0;
+ for (int i = offset; i < length; i++) {
+ bytesRead += this.write(srcs[i]);
+ }
+ return bytesRead;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#position()
+ */
+ @Override
+ public long position() throws IOException {
+ synchronized (positionLock) {
+ // in append-mode, the position is advanced to end before writing
+ if (_flags.append) {
+ return size();
+ }
+ return _currentPosition;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#position(long)
+ */
+ @Override
+ public NfsFileChannel position(long newPosition) throws IOException, ClosedChannelException {
+ checkForClosed();
+ if (newPosition < 0)
+ throw new IllegalArgumentException("Negative size");
+
+ synchronized (positionLock) {
+ /*
+ * JDK8 allows the position to move even in append mode, which might be why it reports
+ * current file size from position(). There doesn't seem any value in doing so.
+ */
+ if (!_flags.append) {
+ _currentPosition = newPosition;
+ }
+ return this;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#size()
+ */
+ @Override
+ public long size() throws IOException {
+ synchronized (positionLock) {
+ NfsGetAttrResponse response = _nfsFile.getattr();
+ if (!response.stateIsOk()) {
+ throw new IOException("Unable to get size");
+ }
+ _currentSize = response.getAttributes().getSize();
+ return _currentSize;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#transferTo(long, long, java.nio.channels.WritableByteChannel)
+ */
+ @Override
+ public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
+ checkForClosed();
+ if (!_flags.read) {
+ throw new NonReadableChannelException();
+ } else if ((position < 0) || (count < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ ByteBuffer src = ByteBuffer.allocate(_rtpref);
+ long position0 = position;
+ long bytesToTransfer = count;
+ long bytesTransferred = 0;
+ while (bytesToTransfer > 0) {
+ int bytesRead = this.read(src, position0);
+ if ((bytesRead == 0) || (bytesRead == EOF)) {
+ return bytesTransferred;
+ }
+ src.flip();
+ int bytesToWrite = bytesRead;
+ long positionWriting = position0;
+ while (bytesToWrite > 0) {
+ int bytesWritten = target.write(src);
+ if (bytesWritten == 0) {
+ throw new IOException("Failed to write bytes starting at offset " + positionWriting);
+ }
+ bytesToWrite -= bytesToWrite;
+ positionWriting += bytesToWrite;
+ }
+ src.clear();
+ bytesTransferred += bytesRead;
+ bytesToTransfer -= bytesRead;
+ position0 += bytesRead;
+ }
+ return bytesTransferred;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.FileChannel#transferFrom(java.nio.channels.ReadableByteChannel, long, long)
+ */
+ @Override
+ public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
+ checkForClosed();
+ if (!_flags.write && !_flags.append) {
+ throw new NonWritableChannelException();
+ } else if ((position < 0) || (count < 0)) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ ByteBuffer dst = ByteBuffer.allocate(_rtpref);
+ long position0 = position;
+ long bytesToTransfer = count;
+ long bytesTransferred = 0;
+ while (bytesToTransfer > 0) {
+ int bytesRead = src.read(dst);
+ if ((bytesRead == 0) || (bytesRead == EOF)) {
+ return bytesTransferred;
+ }
+ dst.flip();
+ int bytesToWrite = bytesRead;
+ long positionWriting = position0;
+ while (bytesToWrite > 0) {
+ int bytesWritten = this.write(dst, position0);
+ if (bytesWritten == 0) {
+ throw new IOException("Failed to write bytes starting at offset " + positionWriting);
+ }
+ bytesToWrite -= bytesToWrite;
+ positionWriting += bytesToWrite;
+ }
+ dst.clear();
+ bytesTransferred += bytesRead;
+ bytesToTransfer -= bytesRead;
+ position0 += bytesRead;
+ }
+ return bytesTransferred;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.SeekableByteChannel#truncate(long)
+ */
+ @Override
+ public FileChannel truncate(long size) throws IOException, ClosedChannelException {
+ checkForClosed();
+
+ if ((!_flags.write) && (!_flags.append))
+ throw new NonWritableChannelException();
+ else if (size < 0)
+ throw new IllegalArgumentException("Negative size");
+ else if (size > size())
+ return this;
+ synchronized (positionLock) {
+ if (size < _currentSize) {
+ NfsSetAttributes setAttr = new NfsSetAttributes();
+ setAttr.setSize(size);
+ _nfsFile.setAttributes(setAttr);
+ _currentSize = size;
+ }
+ if (_currentPosition > _currentSize) {
+ _currentPosition = _currentSize;
+ }
+ return this;
+ }
+ }
+
+ /**
+ * NOTE: Mapping not implemented for NfsFileChannel
+ */
+ @Override
+ public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException
+ {
+ throw new IllegalArgumentException("Mapping not implemented for class");
+ }
+
+ /**
+ * NOTE: Locking not implemented for NfsFileChannel
+ */
+ @Override
+ public FileLock lock(long position, long size, boolean shared) throws IOException {
+ throw new IllegalArgumentException("Locking not implemented for class");
+ }
+
+ /**
+ * NOTE: Locking not implemented for NfsFileChannel
+ */
+ @Override
+ public FileLock tryLock(long position, long size, boolean shared) throws IOException {
+ throw new IllegalArgumentException("Locking not implemented for class");
+ }
+
+ /**
+ * Convenience function.
+ *
+ * @throws ClosedChannelException
+ * If the channel has been closed.
+ */
+ private void checkForClosed() throws ClosedChannelException {
+ if (!isOpen()) {
+ throw new ClosedChannelException();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel()
+ */
+ @Override
+ protected void implCloseChannel() throws IOException {
+ synchronized (_flags) {
+ if (!isOpen())
+ return;
+ if (_flags.deleteOnClose) {
+ _nfsFile.delete();
+ }
+ }
+ }
+
+ /**
+ * Convenience class mapping OpenOptions to boolean flags
+ */
+ private static class Flags {
+ boolean read = false;
+ boolean write = false;
+ boolean append = false;
+ boolean truncateExisting = false;
+ boolean create = false;
+ boolean createNew = false;
+ boolean deleteOnClose = false;
+ boolean sync = false;
+ boolean dsync = false;
+
+ static Flags toFlags(Set extends OpenOption> options) {
+ Flags flags = new Flags();
+ for (OpenOption option: options) {
+ if (option instanceof StandardOpenOption) {
+ switch ((StandardOpenOption)option) {
+ case READ : flags.read = true; break;
+ case WRITE : flags.write = true; break;
+ case APPEND : flags.append = true; break;
+ case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
+ case CREATE : flags.create = true; break;
+ case CREATE_NEW : flags.createNew = true; break;
+ case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
+ case SYNC : flags.sync = true; break;
+ case DSYNC : flags.dsync = true; break;
+ default: throw new UnsupportedOperationException();
+ }
+ continue;
+ }
+
+ if (option == null)
+ throw new NullPointerException();
+ throw new UnsupportedOperationException();
+ }
+ return flags;
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/emc/ecs/nfsclient/nfs/io/Test_Channel.java b/src/test/java/com/emc/ecs/nfsclient/nfs/io/Test_Channel.java
new file mode 100644
index 0000000..78d0378
--- /dev/null
+++ b/src/test/java/com/emc/ecs/nfsclient/nfs/io/Test_Channel.java
@@ -0,0 +1,703 @@
+/**
+ * Copyright 2016-2018 Dell Inc. or its subsidiaries. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.txt
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.emc.ecs.nfsclient.nfs.io;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel.MapMode;
+import java.nio.channels.NonReadableChannelException;
+import java.nio.channels.NonWritableChannelException;
+import java.nio.file.StandardOpenOption;
+import java.util.Calendar;
+
+import org.junit.Test;
+
+import com.emc.ecs.nfsclient.NfsTestBase;
+import com.emc.ecs.nfsclient.nfs.nfs3.Nfs3;
+import com.emc.ecs.nfsclient.rpc.CredentialUnix;
+import com.emc.ecs.nfsclient.rpc.RpcRequest;
+
+/**
+ * @author amarcionek[at]seven10storage.com
+ */
+public class Test_Channel extends NfsTestBase {
+
+ /**
+ * @throws IOException
+ */
+ public Test_Channel() throws IOException {
+ super();
+ }
+
+ @Test
+ public void testReadingAndWriting() throws Exception {
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+
+ NfsFileChannel channel;
+ byte[] expectedData = new byte[] { 1, 2, 3, 127, -1, -128, 0, 1, 32 };
+ ByteBuffer dst = ByteBuffer.allocate(expectedData.length);
+ int bytesRead = 0;
+
+ Nfs3File test1 = new Nfs3File(nfs3, "/testIoSmall");
+ Nfs3File test2 = new Nfs3File(nfs3, "/testIoLarge1");
+ try
+ {
+ // Test write
+ channel = new NfsFileChannel(test1, NfsFileChannel.standardReadWrite);
+ assertTrue(test1.exists());
+ assertTrue(test1.canModify());
+ assertTrue(test1.canRead());
+ channel.write(ByteBuffer.wrap(expectedData));
+ channel.close();
+
+ // Test read and reposition
+ channel = new NfsFileChannel(test1);
+ assertEquals(expectedData.length, channel.size());
+ // Test read at EOF
+ channel.position(channel.size());
+ bytesRead = (int) channel.read(dst);
+ assertEquals(NfsFileChannel.EOF, bytesRead);
+
+ // Test single byte read
+ channel.position(0);
+ dst.limit(1);
+ for (int i = 0; i < expectedData.length; i++) {
+ bytesRead = channel.read(dst);
+ dst.flip();
+ assertNotEquals(NfsFileChannel.EOF, bytesRead);
+ assertEquals(expectedData[i], dst.get());
+ dst.rewind();
+ }
+ assertEquals(NfsFileChannel.EOF, channel.read(dst));
+ channel.close();
+ dst.clear();
+
+ // Test entire read and overflow
+ channel = new NfsFileChannel(test1);
+ dst = ByteBuffer.wrap(new byte[1000]);
+ assertEquals(expectedData.length, channel.size());
+ bytesRead = channel.read(dst);
+ dst.flip();
+ assertEquals(NfsFileChannel.EOF, channel.read(dst));
+ channel.close();
+ assertEquals(expectedData.length, bytesRead);
+ for (int i = 0; i < bytesRead; ++i) {
+ assertEquals(expectedData[i], dst.get(i));
+ }
+
+ test1.delete();
+ assertFalse(test1.exists());
+ }
+ finally {
+ if (test1.exists())
+ test1.delete();
+ }
+
+ try {
+ assertFalse(test2.exists());
+
+ // Test big chunks of data, will cross rsize/wsize boundaries
+ byte[] dataChunk = "another chunk of data!".getBytes(RpcRequest.CHARSET);
+ int longerSize = 100000000 + dataChunk.length;
+ channel = new NfsFileChannel(test2, NfsFileChannel.standardReadWrite);
+ assertTrue(test2.exists());
+ assertTrue(test2.canModify());
+ assertTrue(test2.canRead());
+ expectedData = new byte[longerSize];
+ int chunkStart = 0;
+ while (chunkStart + dataChunk.length < longerSize) {
+ System.arraycopy(dataChunk, 0, expectedData, chunkStart, dataChunk.length);
+ chunkStart += dataChunk.length;
+ }
+
+ long timeInMillis = Calendar.getInstance().getTimeInMillis();
+ int bytesWritten = channel.write(ByteBuffer.wrap(expectedData));
+ channel.close();
+ assertEquals(expectedData.length, bytesWritten);
+ timeInMillis = Calendar.getInstance().getTimeInMillis() - timeInMillis;
+ System.out.println("Milliseconds to write 100 MB: " + timeInMillis);
+
+ channel = new NfsFileChannel(test2);
+ dst = ByteBuffer.wrap(new byte[longerSize]);
+ timeInMillis = Calendar.getInstance().getTimeInMillis();
+ bytesRead = channel.read(dst);
+ channel.close();
+ dst.flip();
+ timeInMillis = Calendar.getInstance().getTimeInMillis() - timeInMillis;
+ System.out.println("Milliseconds to read 100 MB: " + timeInMillis);
+ assertEquals(expectedData.length, bytesRead);
+ for (int i = 0; i < bytesRead; ++i) {
+ assertEquals(expectedData[i], dst.get());
+ }
+
+ dst.clear();
+ int bytesToTest = 1024;
+ int offsetManipulate = longerSize / 2; // Choose this to manipulate half way through the file
+
+ // Test read with position parameters
+ channel = new NfsFileChannel(test2);
+ int offsetRead = offsetManipulate;
+ dst.limit(bytesToTest);
+ bytesRead = channel.read(dst, longerSize); // Read past EOF
+ assertEquals(NfsFileChannel.EOF, bytesRead);
+ assertEquals(longerSize, channel.size());
+ bytesRead = channel.read(dst, offsetRead); // Read at some offset
+ assertEquals(bytesToTest, bytesRead);
+ assertEquals(0, channel.position());
+ dst.flip();
+ assertEquals(bytesToTest, bytesRead);
+ for (int i = bytesToTest; i < bytesRead; ++i) {
+ assertEquals(expectedData[i], dst.get());
+ }
+ channel.close();
+
+ dst.clear();
+ dst.limit(bytesToTest);
+
+ // Test write with position parameters
+ channel = new NfsFileChannel(test2, NfsFileChannel.standardReadWrite);
+ int offsetWrite = offsetManipulate;
+ bytesWritten = channel.write(ByteBuffer.wrap(expectedData, offsetManipulate, bytesToTest), offsetWrite); // Write at some offset
+ assertEquals(bytesToTest, bytesWritten);
+ assertEquals(0, channel.position());
+
+ long size = channel.size();
+ bytesWritten = channel.write(ByteBuffer.wrap(new byte[bytesToTest]), size); // Write past EOF
+ assertEquals(bytesToTest, bytesWritten);
+ assertEquals(size + bytesToTest, channel.size());
+ assertEquals(0, channel.position());
+ channel.truncate(size); // To original size
+ channel.close();
+ channel = new NfsFileChannel(test2, NfsFileChannel.standardReadWrite);
+ assertEquals(size, channel.size());
+ channel.close();
+ }
+ finally {
+ if (test2.exists())
+ test2.delete();
+ }
+ }
+
+ @Test
+ public void testReadWriteMultiple() throws Exception {
+
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+
+ NfsFileChannel channel;
+ byte[] expectedData = new byte[] { 1, 2, 3, 127, -1, -128, 0, 1, 32, -64 };
+
+ Nfs3File test = new Nfs3File(nfs3, "/testReadWriteMultiple");
+
+ assertFalse(test.exists());
+
+ try {
+ // Test read and write with multiple buffers
+ channel = new NfsFileChannel(test, NfsFileChannel.standardReadWrite);
+ ByteBuffer[] dsts = new ByteBuffer[2];
+ dsts[0] = ByteBuffer.wrap(expectedData);
+ dsts[1] = ByteBuffer.wrap(expectedData);
+
+ long totalBytesWritten = channel.write(dsts, 0, 2);
+ assertEquals(dsts.length * expectedData.length, totalBytesWritten);
+ assertEquals(dsts.length * expectedData.length, channel.position());
+
+ try {
+ channel.write(dsts, 1, 2);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+
+ try {
+ channel.write(dsts, -1, 0);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+
+ try {
+ channel.write(dsts, 0, -1);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+ assertEquals(0, channel.write(dsts, 2, 0));
+ assertEquals(0, channel.write(dsts, 0, 0));
+
+ channel.close();
+
+ channel = new NfsFileChannel(test);
+ dsts = new ByteBuffer[2];
+ dsts[0] = ByteBuffer.wrap(new byte[expectedData.length]);
+ dsts[1] = ByteBuffer.wrap(new byte[expectedData.length]);
+
+ long totalBytesRead = channel.read(dsts, 0, 2);
+ assertEquals(dsts.length * expectedData.length, totalBytesRead);
+ assertEquals(dsts.length * expectedData.length, channel.position());
+ dsts[0].flip();
+ for (int i = 0; i < expectedData.length; ++i) {
+ assertEquals(expectedData[i], dsts[0].get());
+ }
+ dsts[1].flip();
+ for (int i = 0; i < expectedData.length; ++i) {
+ assertEquals(expectedData[i], dsts[1].get());
+ }
+
+ try {
+ channel.read(dsts, 1, 2);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+
+ try {
+ channel.read(dsts, -1, 0);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+
+ try {
+ channel.read(dsts, 0, -1);
+ fail("This should throw IndexOutOfBoundsException");
+ }
+ catch (IndexOutOfBoundsException ex) {
+ // Expected
+ }
+ assertEquals(0, channel.read(dsts, 2, 0));
+ assertEquals(0, channel.read(dsts, 0, 0));
+
+ channel.close();
+ }
+ finally {
+ if (test.exists())
+ test.delete();
+ }
+ }
+
+ @Test
+ public void testTransferTo() throws Exception {
+
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+
+ NfsFileChannel channel;
+ byte[] expectedData = new byte[] { 1, 2, 3, 127, -1, -128, 0, 1, 32, -64 };
+
+ Nfs3File test1 = new Nfs3File(nfs3, "/testTransferTo1");
+ Nfs3File test2 = new Nfs3File(nfs3, "/testTransferTo2");
+
+ assertFalse(test1.exists());
+ assertFalse(test2.exists());
+
+ try {
+ int numberOfWrites = 10;
+ channel = new NfsFileChannel(test1, NfsFileChannel.standardReadWrite);
+ for (int i = 0; i < numberOfWrites; i++) {
+ channel.write(ByteBuffer.wrap(expectedData));
+ }
+ channel.position(0);
+ ByteBuffer src = ByteBuffer.wrap(new byte[expectedData.length]);
+ for (int i = 0; i < numberOfWrites; i++) {
+ src.clear();
+ int bytesRead = channel.read(src);
+ src.flip();
+ assertEquals(bytesRead, src.limit());
+ for (int j = 0; j < bytesRead; ++j) {
+ assertEquals(expectedData[j], src.get());
+ }
+ }
+ channel.close();
+
+ // Test transfer to
+ channel = new NfsFileChannel(test1, NfsFileChannel.standardReadWrite);
+ NfsFileChannel channelTransfer = new NfsFileChannel(test2, NfsFileChannel.standardReadWrite);
+ long bytesTransferred = channel.transferTo(0, channel.size(), channelTransfer);
+ assertEquals(bytesTransferred, channel.size());
+ assertEquals(channel.size(), channelTransfer.size());
+ channelTransfer.position(0);
+
+ ByteBuffer dst = ByteBuffer.allocate(expectedData.length * numberOfWrites);
+ int bytesRead = channelTransfer.read(dst);
+ dst.flip();
+ for (int i = 0; i < bytesRead; ++i) {
+ int index = i % expectedData.length;
+ assertEquals(expectedData[index], dst.get());
+ }
+ channel.close();
+ channelTransfer.close();
+
+ channelTransfer = new NfsFileChannel(test2, StandardOpenOption.WRITE);
+ try {
+ channelTransfer.transferTo(0, channel.size(), channel);
+ fail ("This should throw NonReadableChannelException");
+ }
+ catch (NonReadableChannelException ex) {
+ // Expected
+ }
+ channelTransfer.close();
+ }
+ finally {
+ if (test1.exists())
+ test1.delete();
+ if (test2.exists())
+ test2.delete();
+ }
+ }
+
+ @Test
+ public void testTransferFrom() throws Exception {
+
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+
+ NfsFileChannel channel;
+ byte[] expectedData = new byte[] { 1, 2, 3, 127, -1, -128, 0, 1, 32, -64 };
+
+ Nfs3File test1 = new Nfs3File(nfs3, "/testTransferFrom1");
+ Nfs3File test2 = new Nfs3File(nfs3, "/testTransferFrom2");
+
+ assertFalse(test1.exists());
+ assertFalse(test2.exists());
+
+ try {
+ int numberOfWrites = 10;
+ channel = new NfsFileChannel(test1, NfsFileChannel.standardReadWrite);
+ for (int i = 0; i < numberOfWrites; i++) {
+ channel.write(ByteBuffer.wrap(expectedData));
+ }
+ channel.position(0);
+ ByteBuffer src = ByteBuffer.wrap(new byte[expectedData.length]);
+ for (int i = 0; i < numberOfWrites; i++) {
+ src.clear();
+ int bytesRead = channel.read(src);
+ src.flip();
+ assertEquals(bytesRead, src.limit());
+ for (int j = 0; j < bytesRead; ++j) {
+ assertEquals(expectedData[j], src.get());
+ }
+ }
+ channel.close();
+
+ // Test transfer from
+ channel = new NfsFileChannel(test1, NfsFileChannel.standardReadWrite);
+ NfsFileChannel channelTransfer = new NfsFileChannel(test2, NfsFileChannel.standardReadWrite);
+ long bytesTransferred = channelTransfer.transferFrom(channel, 0, channel.size());
+ assertEquals(bytesTransferred, channelTransfer.size());
+ assertEquals(channel.size(), channelTransfer.size());
+ channelTransfer.position(0);
+
+ ByteBuffer dst = ByteBuffer.allocate(expectedData.length * numberOfWrites);
+ int bytesRead = channelTransfer.read(dst);
+ dst.flip();
+ for (int i = 0; i < bytesRead; ++i) {
+ int index = i % expectedData.length;
+ assertEquals(expectedData[index], dst.get());
+ }
+ channel.close();
+ channelTransfer.close();
+
+ channelTransfer = new NfsFileChannel(test2, StandardOpenOption.READ);
+ try {
+ channelTransfer.transferFrom(channel, 0, channel.size());
+ fail ("This should throw NonWritableChannelException");
+ }
+ catch (NonWritableChannelException ex) {
+ // Expected
+ }
+ channelTransfer.close();
+ }
+ finally {
+ if (test1.exists())
+ test1.delete();
+ if (test2.exists())
+ test2.delete();
+ }
+ }
+
+ @Test
+ public void testClosing() throws Exception {
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+ Nfs3File test = new Nfs3File(nfs3, "/testClosing");
+ try {
+ NfsFileChannel channel = new NfsFileChannel(test, NfsFileChannel.standardReadWrite);
+ assertEquals(true, channel.isOpen());
+ channel.close();
+ assertEquals(false, channel.isOpen());
+
+ try {
+ channel.close();
+ } catch (ClosedChannelException e) {
+ fail("This should not throw an ClosedChannelException");
+ }
+
+ try {
+ channel.commit(0, 0);
+ fail("This should throw a ClosedChannelException");
+ } catch (ClosedChannelException e) {
+ // Do nothing, this was expected.
+ }
+
+ try {
+ channel.write(ByteBuffer.wrap(new byte[1]));
+ fail("This should throw a ClosedChannelException");
+ } catch (ClosedChannelException e) {
+ // Do nothing, this was expected.
+ }
+
+ try {
+ channel.read(ByteBuffer.wrap(new byte[1]));
+ fail("This should throw a ClosedChannelException");
+ } catch (ClosedChannelException e) {
+ // Do nothing, this was expected.
+ }
+
+ try {
+ channel.position(-1);
+ fail("This should throw a ClosedChannelException");
+ } catch (ClosedChannelException e) {
+ // Do nothing, this was expected.
+ }
+ try {
+ channel.truncate(0);
+ fail("This should throw a ClosedChannelException");
+ } catch (ClosedChannelException e) {
+ // Do nothing, this was expected.
+ }
+
+ try {
+ long size = channel.size();
+ assertEquals(0, size);
+ } catch (IOException e) {
+ fail("This should not throw a ClosedChannelException");
+ }
+ }
+ finally {
+ try {
+ test.delete();
+ } catch (Exception ex) {
+ // Do nothing, this could happen
+ }
+ }
+ }
+
+ @Test
+ public void testUnsupported() throws Exception {
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+ Nfs3File test = new Nfs3File(nfs3, "/testUnsupported");
+ test.createNewFile();
+ try {
+
+ NfsFileChannel channel = new NfsFileChannel(test, NfsFileChannel.standardReadWrite);
+ try {
+ channel.map(MapMode.PRIVATE, 0, 0);
+ fail ("This should throw IllegalArgumentException");
+ }
+ catch (IllegalArgumentException ex ) {
+ // Expected
+ }
+
+ try {
+ channel.lock();
+ fail ("This should throw IllegalArgumentException");
+ }
+ catch (IllegalArgumentException ex ) {
+ // Expected
+ }
+
+ try {
+ channel.lock(0, 0, false);
+ fail ("This should throw IllegalArgumentException");
+ }
+ catch (IllegalArgumentException ex ) {
+ // Expected
+ }
+
+ try {
+ channel.tryLock(0, 0, false);
+ fail ("This should throw IllegalArgumentException");
+ }
+ catch (IllegalArgumentException ex ) {
+ // Expected
+ }
+ channel.close();
+ }
+ finally {
+ if (test.exists())
+ test.delete();
+ }
+ }
+
+ @Test
+ public void testFlags() throws Exception {
+ Nfs3 nfs3 = new Nfs3(getAbsolutePath(), new CredentialUnix(0, 0, null), 3);
+ Nfs3File test = new Nfs3File(nfs3, "/testFlags1");
+ try {
+ NfsFileChannel channel = null;
+
+ try {
+ channel = new NfsFileChannel(test);
+ fail("This should throw an IOException");
+ } catch (IOException e) {
+ // Do nothing, this was expected, because file doesn't exist.
+ }
+
+ try {
+ channel = new NfsFileChannel(test, NfsFileChannel.standardReadWrite);
+ } catch (IOException e) {
+ fail("This should not throw an IOException");
+ }
+
+ try {
+ channel.write(ByteBuffer.wrap(new byte[10]));
+ } catch (IOException e) {
+ fail("This should not throw an IOException");
+ }
+
+ channel.close();
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.READ);
+ } catch (IOException e) {
+ fail("This should not throw an IOException");
+ }
+ try {
+ channel.write(ByteBuffer.wrap(new byte[10]));
+ fail("This should throw a NonWritableChannelException");
+ } catch (NonWritableChannelException e) {
+ // Do nothing, this was expected, channel not opened for write
+ }
+ try {
+ channel.truncate(0);
+ fail("This should throw a NonWritableChannelException");
+ } catch (NonWritableChannelException e) {
+ // Do nothing, this was expected, channel not opened for write
+ }
+ // Test zero byte read
+ int bytesRead = channel.read(ByteBuffer.wrap(new byte[0]));
+ assertEquals(0, bytesRead);
+
+ channel.close();
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.WRITE);
+ } catch (IOException e) {
+ fail("This should not throw an IOException");
+ }
+ try {
+ channel.read(ByteBuffer.wrap(new byte[10]));
+ fail("This should throw a NonReadableChannelException");
+ } catch (NonReadableChannelException e) {
+ // Do nothing, this was expected, channel not opened for read
+ }
+ // Test zero byte write
+ int bytesWritten = channel.write(ByteBuffer.wrap(new byte[0]));
+ assertEquals(0, bytesWritten);
+ channel.truncate(0);
+ assertEquals(0, channel.size());
+ channel.close();
+
+ // Test basic open option conflicts
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.APPEND);
+ fail("This should throw an IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Do nothing, this was expected
+ }
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.APPEND);
+ fail("This should throw an IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // Do nothing, this was expected
+ }
+
+ // Do some operations on existing files
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.CREATE_NEW);
+ fail("This should throw an IOException");
+ } catch (IOException e) {
+ // Do nothing, this was expected
+ }
+
+ // Test truncating on open, first we must write 1 byte to the file
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.WRITE);
+ channel.write(ByteBuffer.wrap(new byte[1]));
+ assertEquals(1, channel.size());
+ channel.close();
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
+ assertEquals(0, channel.size());
+ channel.close();
+
+ test.delete();
+
+ // Test some operations on non-existing files
+ try {
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.WRITE);
+ fail("This should throw an IOException");
+ } catch (IOException e) {
+ // Do nothing, this was expected
+ }
+
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
+ channel.close();
+ assertTrue(test.exists());
+ test.delete();
+
+ channel = new NfsFileChannel(test, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+ channel.close();
+ assertTrue(test.exists());
+ test.delete();
+
+ // Test append
+
+ // First write some data
+ channel = new NfsFileChannel(test, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+ assertTrue(test.exists());
+ assertEquals(0, channel.size());
+ bytesWritten = channel.write(ByteBuffer.wrap(new byte[10]));
+ assertEquals(10, bytesWritten);
+ assertEquals(10, channel.size());
+ channel.close();
+ assertTrue(test.exists());
+ channel = new NfsFileChannel(test, StandardOpenOption.APPEND);
+ assertTrue(test.exists());
+ assertEquals(10, channel.size());
+ assertEquals(10, channel.position());
+ bytesWritten = channel.write(ByteBuffer.wrap(new byte[10]));
+ assertEquals(10, bytesWritten);
+ assertEquals(20, channel.size());
+ channel.truncate(21);
+ assertEquals(20, channel.size());
+ channel.position(10);
+ assertEquals(20, channel.size());
+ channel.close();
+
+ // Test delete on close
+ channel = new NfsFileChannel(test, StandardOpenOption.DELETE_ON_CLOSE);
+ assertTrue(test.exists());
+ channel.close();
+ assertTrue(!test.exists());
+ }
+ finally {
+ try {
+ test.delete();
+ } catch (Exception ex) {
+ // Do nothing, this could happen
+ }
+ }
+ }
+}
\ No newline at end of file