
com.google.jimfs.JimfsFileChannel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jimfs Show documentation
Show all versions of jimfs Show documentation
Jimfs is an in-memory implementation of Java 7's java.nio.file abstract file system API.
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.google.jimfs;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkPositionIndexes;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.AsynchronousFileChannel;
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;
import java.nio.file.OpenOption;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
/**
* A {@link FileChannel} implementation that reads and writes to a {@link RegularFile} object. The
* read and write methods and other methods that read or change the position of the channel are
* locked because the {@link ReadableByteChannel} and {@link WritableByteChannel} interfaces specify
* that the read and write methods block when another thread is currently doing a read or write
* operation.
*
* @author Colin Decker
*/
final class JimfsFileChannel extends FileChannel {
/**
* Thread that is currently doing an interruptible blocking operation; that is, doing something
* that requires acquiring the file's lock. Since a thread has to already have this channel's
* lock to do that, there can only be one such thread at a time. This thread must be interrupted
* if the channel is closed by another thread.
*/
@Nullable
private volatile Thread blockingThread;
private final RegularFile file;
private final FileSystemState fileSystemState;
private final boolean read;
private final boolean write;
private final boolean append;
private long position;
public JimfsFileChannel(
RegularFile file, Set options, FileSystemState fileSystemState) {
this.file = file;
this.fileSystemState = fileSystemState;
this.read = options.contains(READ);
this.write = options.contains(WRITE);
this.append = options.contains(APPEND);
fileSystemState.register(this);
}
/**
* Returns an {@link AsynchronousFileChannel} view of this channel using the given executor for
* asynchronous operations.
*/
public AsynchronousFileChannel asAsynchronousFileChannel(ExecutorService executor) {
return new JimfsAsynchronousFileChannel(this, executor);
}
void checkReadable() {
if (!read) {
throw new NonReadableChannelException();
}
}
void checkWritable() {
if (!write) {
throw new NonWritableChannelException();
}
}
void checkOpen() throws ClosedChannelException {
if (!isOpen()) {
throw new ClosedChannelException();
}
}
/**
* Begins a blocking operation, making the operation interruptible.
*/
private void beginBlocking() {
begin();
blockingThread = Thread.currentThread();
}
/**
* Ends a blocking operation, throwing an exception if the thread was interrupted while blocking
* or if the channel was closed from another thread.
*/
private void endBlocking(boolean completed) throws AsynchronousCloseException {
blockingThread = null;
end(completed);
}
@Override
public int read(ByteBuffer dst) throws IOException {
checkNotNull(dst);
checkOpen();
checkReadable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.readLock().lockInterruptibly();
try {
int read = file.read(position, dst);
if (read != -1) {
position += read;
}
file.updateAccessTime();
completed = true;
return read;
} finally {
file.readLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
checkPositionIndexes(offset, offset + length, dsts.length);
List buffers = Arrays.asList(dsts).subList(offset, offset + length);
Util.checkNoneNull(buffers);
checkOpen();
checkReadable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.readLock().lockInterruptibly();
try {
long read = file.read(position, buffers);
if (read != -1) {
position += read;
}
file.updateAccessTime();
completed = true;
return read;
} finally {
file.readLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public int write(ByteBuffer src) throws IOException {
checkNotNull(src);
checkOpen();
checkWritable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.writeLock().lockInterruptibly();
try {
if (append) {
position = file.size();
}
int written = file.write(position, src);
position += written;
file.updateModifiedTime();
completed = true;
return written;
} finally {
file.writeLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
checkPositionIndexes(offset, offset + length, srcs.length);
List buffers = Arrays.asList(srcs).subList(offset, offset + length);
Util.checkNoneNull(buffers);
checkOpen();
checkWritable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.writeLock().lockInterruptibly();
try {
if (append) {
position = file.size();
}
long written = file.write(position, buffers);
position += written;
file.updateModifiedTime();
completed = true;
return written;
} finally {
file.writeLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public long position() throws IOException {
checkOpen();
synchronized (this) {
return position;
}
}
@Override
public FileChannel position(long newPosition) throws IOException {
Util.checkNotNegative(newPosition, "newPosition");
checkOpen();
synchronized (this) {
this.position = newPosition;
}
return this;
}
@Override
public long size() throws IOException {
checkOpen();
return file.size();
}
@Override
public FileChannel truncate(long size) throws IOException {
Util.checkNotNegative(size, "size");
checkOpen();
checkWritable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return this; // AsynchronousCloseException will be thrown
}
file.writeLock().lockInterruptibly();
try {
file.truncate(size);
if (position > size) {
position = size;
}
file.updateModifiedTime();
completed = true;
return this;
} finally {
file.writeLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public void force(boolean metaData) throws IOException {
checkOpen();
// do nothing... writes are all synchronous anyway
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
checkNotNull(target);
Util.checkNotNegative(position, "position");
Util.checkNotNegative(count, "count");
checkOpen();
checkReadable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.readLock().lockInterruptibly();
try {
long transferred = file.transferTo(position, count, target);
file.updateAccessTime();
completed = true;
return transferred;
} finally {
file.readLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
checkNotNull(src);
Util.checkNotNegative(position, "position");
Util.checkNotNegative(count, "count");
checkOpen();
checkWritable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.writeLock().lockInterruptibly();
try {
if (append) {
position = file.size();
}
long transferred = file.transferFrom(src, position, count);
if (append) {
this.position = position + transferred;
}
file.updateModifiedTime();
completed = true;
return transferred;
} finally {
file.writeLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
checkNotNull(dst);
Util.checkNotNegative(position, "position");
checkOpen();
checkReadable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.readLock().lockInterruptibly();
try {
int read = file.read(position, dst);
file.updateAccessTime();
completed = true;
return read;
} finally {
file.readLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
checkNotNull(src);
Util.checkNotNegative(position, "position");
checkOpen();
checkWritable();
synchronized (this) {
boolean completed = false;
try {
beginBlocking();
if (!isOpen()) {
return 0; // AsynchronousCloseException will be thrown
}
file.writeLock().lockInterruptibly();
try {
if (append) {
position = file.sizeWithoutLocking();
}
int written = file.write(position, src);
if (append) {
this.position = position + written;
}
file.updateModifiedTime();
completed = true;
return written;
} finally {
file.writeLock().unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
endBlocking(completed);
}
// if InterruptedException is caught, endBlocking will throw ClosedByInterruptException
throw new AssertionError();
}
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
// would like this to pretend to work, but can't create an implementation of MappedByteBuffer
// well, a direct buffer could be cast to MappedByteBuffer, but it couldn't work in general
throw new UnsupportedOperationException();
}
@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
Util.checkNotNegative(position, "position");
Util.checkNotNegative(size, "size");
checkOpen();
if (shared) {
checkReadable();
} else {
checkWritable();
}
return new FakeFileLock(this, position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
// lock() doesn't block anyway
return lock(position, size, shared);
}
@Override
protected void implCloseChannel() {
// interrupt the current blocking thread, if any, causing it to throw ClosedByInterruptException
try {
final Thread thread = blockingThread;
if (thread != null) {
thread.interrupt();
}
} finally {
fileSystemState.unregister(this);
file.closed();
}
}
/**
* A file lock that does nothing, since only one JVM process has access to this file system.
*/
static final class FakeFileLock extends FileLock {
private final AtomicBoolean valid = new AtomicBoolean(true);
public FakeFileLock(FileChannel channel, long position, long size, boolean shared) {
super(channel, position, size, shared);
}
public FakeFileLock(AsynchronousFileChannel channel, long position, long size, boolean shared) {
super(channel, position, size, shared);
}
@Override
public boolean isValid() {
return valid.get();
}
@Override
public void release() throws IOException {
valid.set(false);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy