FileChannelFactory.java
package emissary.core.channels;
import org.apache.commons.lang3.Validate;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.Set;
/**
* Provide a file-backed implementation for streaming data to a consumer
*/
public final class FileChannelFactory {
private FileChannelFactory() {}
/**
* <p>
* Create a new instance of the factory using the provided file reference. Ultimately, wraps a standard FileChannel with
* immutability
* </p>
*
* @param path containing a reference to the file
* @return a new instance
* @see SeekableByteChannelHelper#file(Path)
*/
public static SeekableByteChannelFactory create(final Path path) {
return ImmutableChannelFactory.create(new FileChannelFactoryImpl(path));
}
/**
* Private class to hide implementation details from callers
*/
private static final class FileChannelFactoryImpl implements SeekableByteChannelFactory {
private final Path path;
private FileChannelFactoryImpl(final Path path) {
Validate.notNull(path, "Required: path not null");
this.path = path;
}
/**
* Creates a {@link FileChannel} instance with the configured options to the configured file.
*
* @return the new channel instance
*/
@Override
public SeekableByteChannel create() {
return new LazyFileChannelImpl(path);
}
}
private static final class LazyFileChannelImpl extends AbstractSeekableByteChannel {
private static final Set<StandardOpenOption> OPTIONS = Collections.singleton(StandardOpenOption.READ);
private final Path path;
private FileChannel channel;
private LazyFileChannelImpl(final Path path) {
this.path = path;
}
void initialiseChannel() throws IOException {
if (channel == null) {
channel = FileChannel.open(path, OPTIONS);
}
}
@Override
protected void closeImpl() throws IOException {
if (channel != null) {
channel.close();
}
}
@Override
protected int readImpl(final ByteBuffer byteBuffer) throws IOException {
initialiseChannel();
// Set position for underlying channel, otherwise this won't match the outer channel position
channel.position(position());
return channel.read(byteBuffer);
}
@Override
protected long sizeImpl() throws IOException {
initialiseChannel();
return channel.size();
}
}
}