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

com.gc.iotools.stream.is.InputStreamFromOutputStream Maven / Gradle / Ivy

Go to download

EasyStream is a small set of utilities for dealing with streams (InputStreams and OutputStreams). The aim is to ease the use of pipes when they're required. Main features are: * "Convert" an OutputStream to an InputStream. * Count the number of bytes read or wrote to a given stream. * While reading the data from an InputStream copy it to a supplied OutputStream. * Read the content of an InputStream multiple times or seek to a definite position

The newest version!
package com.gc.iotools.stream.is;

/*
 * Copyright (c) 2008, 2015 Gabriele Contini. This source code is released
 * under the BSD License.
 */
import java.io.IOException;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gc.iotools.stream.base.EasyStreamConstants;
import com.gc.iotools.stream.base.ExecutionModel;
import com.gc.iotools.stream.base.ExecutorServiceFactory;
import com.gc.iotools.stream.utils.LogUtils;

/**
 * 

* This class allow to read the data written to an OutputStream from an * InputStream. *

*

* To use this class you must subclass it and implement the abstract method * {@linkplain #produce(OutputStream)}. The data who is produced inside this * function can be written to the sink OutputStream passed as a * parameter. Later it can be read back from from the * InputStreamFromOutputStream class (whose ancestor is * java.io.InputStream ). *

* *
 * final String dataId=//id of some data.
 * final InputStreamFromOutputStream<String> isos
 *                          = new InputStreamFromOutputStream<String>() {
 *   @Override
 *   public String produce(final OutputStream dataSink) throws Exception {
 *      //call your application function who produces the data here
 *      //WARNING: we're in another thread here, so this method shouldn't
 *      //write any class field or make assumptions on the state of the class.
 *      return produceMydata(dataId,dataSink)
 *   }
 * };
 *  try {
 *    //now you can read from the InputStream the data that was written to the
 *    //dataSink OutputStream
 *     byte[] read=IOUtils.toByteArray(isos);
 *     //Use data here
 *   } catch (final IOException e) {
 *     //Handle exception here
 *   } finally {
 *   isos.close();
 * }
 * //You can get the result of produceMyData after the stream has been closed.
 * String resultOfProduction = isos.getResult();
 * 
*

* This class encapsulates a pipe and a Thread, hiding the * complexity of using them. It is possible to select different strategies for * allocating the internal thread or even specify the * {@linkplain ExecutorService} for thread execution. *

* * @param * Optional result returned by the function * {@linkplain #produce(OutputStream)} after the data has been * written. It can be obtained calling the {@linkplain #getResult()} * @see ExecutionModel * @author dvd.smnt * @since 1.0 */ public abstract class InputStreamFromOutputStream extends PipedInputStream { /** * This inner class run in another thread and calls the * {@link #produce(OutputStream)} method. * * @author dvd.smnt */ private final class DataProducer implements Callable { @Override public T call() throws Exception { final String threadName = Thread.currentThread().getName(); T result; InputStreamFromOutputStream.ACTIVE_THREAD_NAMES.add(threadName); InputStreamFromOutputStream.LOG.debug("thread [" + threadName + "] started."); try { result = produce(InputStreamFromOutputStream.this.pipedOs); } finally { closeStream(); InputStreamFromOutputStream.ACTIVE_THREAD_NAMES .remove(threadName); InputStreamFromOutputStream.LOG.debug("thread [" + threadName + "] closed."); } return result; } private void closeStream() { try { InputStreamFromOutputStream.this.pipedOs.close(); } catch (final IOException e) { if ((e.getMessage() != null) && (e.getMessage().indexOf("closed") > 0)) { InputStreamFromOutputStream.LOG .debug("Stream already closed"); } else { InputStreamFromOutputStream.LOG.error( "IOException closing OutputStream" + " Thread might be locked", e); } } catch (final Throwable t) { InputStreamFromOutputStream.LOG .error("Error closing InputStream" + " Thread might be locked", t); } } } /** * Collection for debugging purpose containing names of threads (name is * calculated from the instantiation line. See getCaller(). */ private static final List ACTIVE_THREAD_NAMES = Collections .synchronizedList(new ArrayList()); /** * The default pipe buffer size for the newly created pipes. */ private static int defaultPipeSize = EasyStreamConstants.DEFAULT_PIPE_SIZE; private static final Logger LOG = LoggerFactory .getLogger(InputStreamFromOutputStream.DataProducer.class); /** * This method can be used for debugging purposes to get a list of the * currently active threads. * * @return Array containing names of the threads currently active. */ public static final String[] getActiveThreadNames() { final String[] result; synchronized (InputStreamFromOutputStream.ACTIVE_THREAD_NAMES) { result = InputStreamFromOutputStream.ACTIVE_THREAD_NAMES .toArray(new String[0]); } return result; } /** * Set the size for the pipe buffer for the newly created * InputStreamFromOutputStream. Default is 4096 bytes. * * @since 1.2.2 * @param defaultPipeSize * the default pipe buffer size in bytes. */ public static void setDefaultPipeSize(final int defaultPipeSize) { InputStreamFromOutputStream.defaultPipeSize = defaultPipeSize; } private final String callerId; private boolean closeCalled = false; private final ExecutorService executorService; private Future futureResult; private final boolean joinOnClose; private boolean started = false; private final PipedOutputStream pipedOs = new PipedOutputStream() { private boolean outputStreamCloseCalled = false; //duplicate close hangs the pipeStream see issue #36 @Override public void close() throws IOException { synchronized (this) { if (outputStreamCloseCalled) { return; } outputStreamCloseCalled = true; } super.close(); } }; /** *

* It creates a InputStreamFromOutputStream with a * THREAD_PER_INSTANCE thread strategy. *

* * @see ExecutionModel#THREAD_PER_INSTANCE */ public InputStreamFromOutputStream() { this(ExecutionModel.THREAD_PER_INSTANCE); } public InputStreamFromOutputStream(final boolean startImmediately, final boolean joinOnClose, final ExecutorService executor, final int pipeBufferSize) { super(pipeBufferSize); this.callerId = LogUtils.getCaller(this.getClass()); this.joinOnClose = joinOnClose; this.executorService = executor; try { connect(this.pipedOs); } catch (final IOException e) { throw new RuntimeException("Error during pipe creaton", e); } if (startImmediately) { checkInitialized(); } } /** *

* It creates a InputStreamFromOutputStream and let the user * choose the thread allocation strategy he likes. *

*

* This class executes the produce method in a thread created internally. *

* * @since 1.2.2 * @see ExecutionModel * @param executionModel * Defines how the internal thread is allocated. * @param joinOnClose * If true the {@linkplain #close()} method will * also wait for the internal thread to finish. */ public InputStreamFromOutputStream(final boolean joinOnClose, final ExecutionModel executionModel) { this(joinOnClose, ExecutorServiceFactory.getExecutor(executionModel)); } /** *

* It creates a InputStreamFromOutputStream and let the user * specify the ExecutorService that will execute the * {@linkplain #produce(OutputStream)} method. *

* * @since 1.2.2 * @see ExecutorService * @param executor * Defines the ExecutorService that will allocate the the * internal thread. * @param joinOnClose * If true the {@linkplain #close()} method will * also wait for the internal thread to finish. */ public InputStreamFromOutputStream(final boolean joinOnClose, final ExecutorService executor) { this(joinOnClose, executor, defaultPipeSize); } /** *

* It creates a InputStreamFromOutputStream and let the user * specify the ExecutorService that will execute the * {@linkplain #produce(OutputStream)} method and the pipe buffer size. *

*

* Using this method the default size is ignored. *

* * @since 1.2.6 * @see ExecutorService * @param executor * Defines the ExecutorService that will allocate the the * internal thread. * @param joinOnClose * If true the {@linkplain #close()} method will * also wait for the internal thread to finish. * @param pipeBufferSize * The size of the pipe buffer to allocate. */ public InputStreamFromOutputStream(final boolean joinOnClose, final ExecutorService executor, final int pipeBufferSize) { this(false, joinOnClose, executor, pipeBufferSize); } /** *

* It creates a InputStreamFromOutputStream and let the user * choose the thread allocation strategy he likes. *

*

* This class executes the produce method in a thread created internally. *

* * @see ExecutionModel * @param executionModel * Defines how the internal thread is allocated. */ public InputStreamFromOutputStream(final ExecutionModel executionModel) { this(false, executionModel); } /** *

* It creates a InputStreamFromOutputStream and let the user * specify the ExecutorService that will execute the * {@linkplain #produce(OutputStream)} method. *

* * @see ExecutorService * @param executor * Defines the ExecutorService that will allocate the the * internal thread. */ public InputStreamFromOutputStream(final ExecutorService executor) { this(false, executor); } /** *

* This method is called just before the {@link #close()} method completes, * and after the eventual join with the internal thread. *

*

* It is an extension point designed for applications that need to perform * some operation when the InputStream is closed. *

* * @since 1.2.9 * @throws IOException * threw when the subclass wants to communicate an exception * during close. */ protected void afterClose() throws IOException { // extension point; } private void checkException() throws IOException { try { this.futureResult.get(1, TimeUnit.SECONDS); } catch (final ExecutionException e) { final Throwable t = e.getCause(); final IOException e1 = new IOException("Exception producing data"); e1.initCause(t); throw e1; } catch (final InterruptedException e) { final IOException e1 = new IOException("Thread interrupted"); e1.initCause(e); throw e1; } catch (final TimeoutException e) { LOG.error("This timeout should never happen, " + "the thread should terminate correctly. " + "Please contact io-tools support.", e); } } private synchronized void checkInitialized() { if (!this.started) { this.started = true; final Callable executingCallable = new DataProducer(); this.futureResult = this.executorService.submit(executingCallable); InputStreamFromOutputStream.LOG.debug( "thread invoked by[{}] queued for start.", this.callerId); } } /** {@inheritDoc} */ @Override public void close() throws IOException { checkInitialized(); if (!this.closeCalled) { this.closeCalled = true; super.close(); if (this.joinOnClose) { try { getResult(); } catch (final Exception e) { final IOException e1 = new IOException( "The internal stream threw exception"); e1.initCause(e); throw e1; } } afterClose(); } } /** *

* Returns the object that was previously returned by the * {@linkplain #produce(OutputStream)} method. It performs all the * synchronization operations needed to read the result and waits for the * internal thread to terminate. *

*

* This method automatically invokes the {@linkplain #close()} method on the, * stream. *

* * @since 1.2.0 * @return Object that was returned by the * {@linkplain #produce(OutputStream)} method. * @throws java.lang.Exception * If the {@linkplain #produce(OutputStream)} method threw an * java.lang.Exception this method will throw again the same * exception. */ public T getResult() throws Exception { if (!this.closeCalled) { this.closeCalled = true; super.close(); } T result; try { result = this.futureResult.get(); } catch (final ExecutionException e) { final Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } throw e; } return result; } /** *

* This method must be implemented by the user of this class to produce the * data that must be read from the external InputStream. *

*

* Special care must be paid passing arguments to this method or setting * global fields because it is executed in another thread. *

*

* The right way to set a field variable is to return a value in the * produceand retrieve it in the getResult(). *

* * @return The implementing class can use this to return a result of data * production. The result will be available through the method * {@linkplain #getResult()}. * @param sink * the implementing class should write its data to this stream. * @throws java.lang.Exception * the exception eventually thrown by the implementing class is * returned by the {@linkplain #read()} methods. * @see #getResult() */ protected abstract T produce(final OutputStream sink) throws Exception; /** {@inheritDoc} */ @Override public int read() throws IOException { checkInitialized(); final int result = super.read(); if (result < 0) { checkException(); } return result; } /** {@inheritDoc} */ @Override public int read(final byte[] b, final int off, final int len) throws IOException { checkInitialized(); final int result = super.read(b, off, len); if (result < 0) { checkException(); } return result; } /** {@inheritDoc} */ @Override public int read(byte[] b) throws IOException { checkInitialized(); final int result = super.read(b); if (result < 0) { checkException(); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy