package com.firefly.codec.http2.model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* <p>A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs.</p>
* <p>It is possible to specify, at the constructor, a buffer size used to read
* content from the stream, by default 4096 bytes.</p>
*/
public class PathContentProvider extends AbstractTypedContentProvider {
private static final Logger LOG = LoggerFactory.getLogger("firefly-system");
private final Path filePath;
private final long fileSize;
private final int bufferSize;
public PathContentProvider(Path filePath) throws IOException {
this(filePath, 4096);
}
public PathContentProvider(Path filePath, int bufferSize) throws IOException {
this("application/octet-stream", filePath, bufferSize);
}
public PathContentProvider(String contentType, Path filePath) throws IOException {
this(contentType, filePath, 4096);
}
public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException {
super(contentType);
if (!Files.isRegularFile(filePath))
throw new NoSuchFileException(filePath.toString());
if (!Files.isReadable(filePath))
throw new AccessDeniedException(filePath.toString());
this.filePath = filePath;
this.fileSize = Files.size(filePath);
this.bufferSize = bufferSize;
}
@Override
public long getLength() {
return fileSize;
}
@Override
public Iterator<ByteBuffer> iterator() {
return new PathIterator();
}
private class PathIterator implements Iterator<ByteBuffer>, Closeable {
private SeekableByteChannel channel;
private long position;
@Override
public boolean hasNext() {
return position < getLength();
}
@Override
public ByteBuffer next() {
try {
if (channel == null) {
channel = Files.newByteChannel(filePath, StandardOpenOption.READ);
if (LOG.isDebugEnabled())
LOG.debug("Opened file {}", filePath);
}
ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
int read = channel.read(buffer);
if (read < 0)
throw new NoSuchElementException();
if (LOG.isDebugEnabled())
LOG.debug("Read {} bytes from {}", read, filePath);
position += read;
buffer.flip();
return buffer;
} catch (NoSuchElementException x) {
close();
throw x;
} catch (Throwable x) {
close();
throw (NoSuchElementException) new NoSuchElementException().initCause(x);
}
}
@Override
public void close() {
try {
if (channel != null)
channel.close();
} catch (Throwable x) {
LOG.error("channel close error", x);
}
}
}
}