com.qcloud.cos.internal.ResettableInputStream Maven / Gradle / Ivy
Show all versions of cos_api Show documentation
package com.qcloud.cos.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.qcloud.cos.exception.CosClientException;
/**
* A mark-and-resettable input stream that can be used on files or file input streams.
*
* In particular, a {@link ResettableInputStream} allows the close operation to be disabled via
* {@link #disableClose()} (to avoid accidentally being closed). This is necessary when such input
* stream needs to be marked-and-reset multiple times but only as long as the stream has not been
* closed.
*
* The creator of this input stream should therefore always call {@link #release()} in a finally
* block to truly release the underlying resources.
*
* @see Releasable
*/
public class ResettableInputStream extends ReleasableInputStream {
private static final Logger log = LoggerFactory.getLogger(ResettableInputStream.class);
private final File file; // null if the file is not known
private FileInputStream fis; // never null
private FileChannel fileChannel; // never null
/**
* Marked position of the file; default to zero.
*/
private long markPos;
/**
* @param file must not be null. Upon successful construction the the file will be opened with
* an input stream automatically marked at the starting position of the given file.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a
* file. If the opened file is meant to be closed only (in a finally block) by the very
* same code block that created it, then it is necessary that the release method must not
* be called while the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close
* method of the stream, the creator of the stream would need to explicitly disable the
* accidental closing via {@link ResettableInputStream#disableClose()}, so that the
* release method becomes the only way to truly close the opened file.
*/
public ResettableInputStream(File file) throws IOException {
this(new FileInputStream(file), file);
}
/**
* @param fis file input stream; must not be null. Upon successful construction the input stream
* will be automatically marked at the current position of the given file input stream.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a
* file. If the opened file is meant to be closed only (in a finally block) by the very
* same code block that created it, then it is necessary that the release method must not
* be called while the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close
* method of the stream, the creator of the stream would need to explicitly disable the
* accidental closing via {@link ResettableInputStream#disableClose()}, so that the
* release method becomes the only way to truly close the opened file.
*/
public ResettableInputStream(FileInputStream fis) throws IOException {
this(fis, null);
}
/**
* @param file can be null if not known
*/
private ResettableInputStream(FileInputStream fis, File file) throws IOException {
super(fis);
this.file = file;
this.fis = fis;
this.fileChannel = fis.getChannel();
this.markPos = fileChannel.position();
}
@Override
public final boolean markSupported() {
return true;
}
/**
* Marks the current position in this input stream. A subsequent call to the reset
* method repositions this stream at the last marked position so that subsequent reads re-read
* the same bytes. This method works as long as the underlying file has not been closed.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*
* @param _ ignored
*/
@Override
public void mark(int _) {
abortIfNeeded();
try {
markPos = fileChannel.position();
} catch (IOException e) {
throw new CosClientException("Failed to mark the file position", e);
}
if (log.isTraceEnabled())
log.trace("File input stream marked at position " + markPos);
}
/**
* Repositions this stream to the position at the time the mark
method was last
* called on this input stream. This method works as long as the underlying file has not been
* closed.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*/
@Override
public void reset() throws IOException {
abortIfNeeded();
fileChannel.position(markPos);
if (log.isTraceEnabled())
log.trace("Reset to position " + markPos);
}
@Override
public int available() throws IOException {
abortIfNeeded();
return fis.available();
}
@Override
public int read() throws IOException {
abortIfNeeded();
return fis.read();
}
@Override
public long skip(long n) throws IOException {
abortIfNeeded();
return fis.skip(n);
}
@Override
public int read(byte[] arg0, int arg1, int arg2) throws IOException {
abortIfNeeded();
return fis.read(arg0, arg1, arg2);
}
/**
* Returns the underlying file, if known; or null if not;
*/
public File getFile() {
return file;
}
/**
* Convenient factory method to construct a new resettable input stream for the given file,
* converting any IOException into CosClientException.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*/
public static ResettableInputStream newResettableInputStream(File file) {
return newResettableInputStream(file, null);
}
/**
* Convenient factory method to construct a new resettable input stream for the given file,
* converting any IOException into CosClientException with the given error message.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*/
public static ResettableInputStream newResettableInputStream(File file, String errmsg) {
try {
return new ResettableInputStream(file);
} catch (IOException e) {
throw errmsg == null ? new CosClientException(e) : new CosClientException(errmsg, e);
}
}
/**
* Convenient factory method to construct a new resettable input stream for the given file input
* stream, converting any IOException into CosClientException.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*/
public static ResettableInputStream newResettableInputStream(FileInputStream fis) {
return newResettableInputStream(fis, null);
}
/**
* Convenient factory method to construct a new resettable input stream for the given file input
* stream, converting any IOException into CosClientException with the given error message.
*
* Note the creation of a {@link ResettableInputStream} would entail physically opening a file.
* If the opened file is meant to be closed only (in a finally block) by the very same code
* block that created it, then it is necessary that the release method must not be called while
* the execution is made in other stack frames.
*
* In such case, as other stack frames may inadvertently or indirectly call the close method of
* the stream, the creator of the stream would need to explicitly disable the accidental closing
* via {@link ResettableInputStream#disableClose()}, so that the release method becomes the only
* way to truly close the opened file.
*/
public static ResettableInputStream newResettableInputStream(FileInputStream fis,
String errmsg) {
try {
return new ResettableInputStream(fis);
} catch (IOException e) {
throw new CosClientException(errmsg, e);
}
}
}