/*
* Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Portions copyright 2006-2009 James Murty. Please see LICENSE.txt
* for applicable license terms and NOTICE.txt for applicable notices.
*
* 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://aws.amazon.com/apache2.0
*
* 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.amazonaws.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.amazonaws.SdkClientException;
import com.amazonaws.annotation.NotThreadSafe;
/**
* 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.
* <p>
* The creator of this input stream should therefore always call
* {@link #release()} in a finally block to truly release the underlying
* resources.
*
* @see Releasable
*/
@NotThreadSafe
public class ResettableInputStream extends ReleasableInputStream {
private static final Log log = LogFactory
.getLog(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.
* <p>
* 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.
* <p>
* 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 <code>reset</code> 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.
* <p>
* 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 SdkClientException("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
* <code>mark</code> method was last called on this input stream.
* This method works as long as the underlying file has not been closed.
* <p>
* 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 SdkClientException.
* <p>
* 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 SdkClientException
* with the given error message.
* <p>
* 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 SdkClientException(e)
: new SdkClientException(errmsg, e);
}
}
/**
* Convenient factory method to construct a new resettable input stream for
* the given file input stream, converting any IOException into
* SdkClientException.
* <p>
* 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
* SdkClientException with the given error message.
* <p>
* 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 SdkClientException(errmsg, e);
}
}
}