All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.org.jets3t.service.io.RepeatableInputStream Maven / Gradle / Ivy

/*
 * jets3t : Java Extra-Tasty S3 Toolkit (for Amazon S3 online storage service)
 * This is a java.net project, see https://jets3t.dev.java.net/
 * 
 * Copyright 2008 James Murty
 * 
 * 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 org.jets3t.service.io;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jets3t.service.Constants;
import org.jets3t.service.Jets3tProperties;

/**
 * A repeatable input stream wrapper for any input stream. This input stream relies on buffered
 * data to repeat, and can therefore only be repeated when less data has been read than this
 * buffer can hold.
 * 

* Note: Always use a {@link RepeatableFileInputStream} instead of this class if you are * sourcing data from a file, as the file-based repeatable input stream can be repeated without * any limitations. * *

* This class uses properties obtained through {@link Jets3tProperties}. For more information on * these properties please refer to * JetS3t Configuration *

* * @author James Murty */ public class RepeatableInputStream extends InputStream implements InputStreamWrapper { private static final Log log = LogFactory.getLog(RepeatableInputStream.class); private InputStream is = null; private int bufferSize = 0; private int bufferOffset = 0; private long bytesReadPastMark = 0; private byte[] buffer = null; /** * Creates a repeatable input stream based on another input stream. * * @param inputStream * an input stream to wrap. The data read from the wrapped input stream is buffered as it is * read, up to the buffer limit specified. * @param bufferSize * the number of bytes buffered by this class. * * @throws FileNotFoundException */ public RepeatableInputStream(InputStream inputStream, int bufferSize) { if (inputStream == null) { throw new IllegalArgumentException("InputStream cannot be null"); } this.is = inputStream; this.bufferSize = bufferSize; this.buffer = new byte[this.bufferSize]; if (log.isDebugEnabled()) { log.debug("Underlying input stream will be repeatable up to " + this.buffer.length + " bytes"); } } /** * Creates a repeatable input stream based on another input stream. * * @param inputStream * an input stream to wrap. The data read from the wrapped input stream is buffered as it is * read, up to the buffer limit as set by the JetS3t property * s3service.stream-retry-buffer-size. * * @throws FileNotFoundException */ public RepeatableInputStream(InputStream inputStream) { this(inputStream, Jets3tProperties.getInstance(Constants.JETS3T_PROPERTIES_FILENAME) .getIntProperty("s3service.stream-retry-buffer-size", 131072)); } /** * Resets the input stream to the beginning by pointing the buffer offset to the beginning of the * available data buffer. * * @throws UnrecoverableIOException * when the available buffer size has been exceeded, in which case the input stream data cannot * be repeated. */ public void reset() throws IOException { if (bytesReadPastMark <= bufferSize) { if (log.isDebugEnabled()) { log.debug("Reset after reading " + bytesReadPastMark + " bytes."); } bufferOffset = 0; } else { throw new UnrecoverableIOException( "Input stream cannot be reset as " + this.bytesReadPastMark + " bytes have been written, exceeding the available buffer size of " + this.bufferSize); } } public boolean markSupported() { return true; } /** * This method can only be used while less data has been read from the input * stream than fits into the buffer. The readLimit parameter is ignored entirely. */ public synchronized void mark(int readlimit) { if (log.isDebugEnabled()) { log.debug("Input stream marked at " + bytesReadPastMark + " bytes"); } if (bytesReadPastMark <= bufferSize && buffer != null) { // Clear buffer of already-read data to make more space. // it is safe to cast bytesReadPastMark to an int because it is known to be less than bufferSize, which is an int byte[] newBuffer = new byte[this.bufferSize]; System.arraycopy(buffer, bufferOffset, newBuffer, 0, (int)(bytesReadPastMark - bufferOffset)); this.buffer = newBuffer; this.bytesReadPastMark -= bufferOffset; this.bufferOffset = 0; } else { // If mark is called after the buffer was already exceeded, create a new buffer. this.bufferOffset = 0; this.bytesReadPastMark = 0; this.buffer = new byte[this.bufferSize]; } } public int available() throws IOException { return is.available(); } public void close() throws IOException { is.close(); } public int read(byte[] out, int outOffset, int outLength) throws IOException { byte[] tmp = new byte[outLength]; // Check whether we already have buffered data. if (bufferOffset < bytesReadPastMark && buffer != null) { // Data is being repeated, so read from buffer instead of wrapped input stream. int bytesFromBuffer = tmp.length; if (bufferOffset + bytesFromBuffer > bytesReadPastMark) { bytesFromBuffer = (int) bytesReadPastMark - bufferOffset; } // Write to output. System.arraycopy(buffer, bufferOffset, out, outOffset, bytesFromBuffer); bufferOffset += bytesFromBuffer; return bytesFromBuffer; } // Read data from input stream. int count = is.read(tmp); if (count <= 0) { return count; } // Fill the buffer with data, as long as we won't exceed its capacity. if (bytesReadPastMark + count <= bufferSize) { System.arraycopy(tmp, 0, buffer, (int) bytesReadPastMark, count); bufferOffset += count; } else if (buffer != null) { // We have exceeded the buffer capacity, after which point it is of no use. Free the memory. if (log.isDebugEnabled()) { log.debug("Buffer size " + bufferSize + " has been exceeded and the input stream " + "will not be repeatable until the next mark. Freeing buffer memory"); } buffer = null; } // Write to output byte array. System.arraycopy(tmp, 0, out, outOffset, count); bytesReadPastMark += count; return count; } public int read() throws IOException { byte[] tmp = new byte[1]; int count = read(tmp); if (count != -1) { return tmp[0]; } else { return count; } } public InputStream getWrappedInputStream() { return is; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy