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

net.sf.mmm.util.io.StreamUtil Maven / Gradle / Ivy

The newest version!
/* $Id: StreamUtil.java 402 2008-01-14 21:09:00Z hohwille $
 * Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.mmm.util.component.AlreadyInitializedException;
import net.sf.mmm.util.concurrent.SimpleExecutor;
import net.sf.mmm.util.concurrent.Stoppable;
import net.sf.mmm.util.pool.api.Pool;
import net.sf.mmm.util.pool.base.NoByteArrayPool;
import net.sf.mmm.util.pool.base.NoCharArrayPool;

/**
 * This class is a collection of utility functions to deal with
 * {@link InputStream}s, {@link OutputStream}s, {@link Reader}s and
 * {@link Writer}s.
* Information:
* Whenever the javadoc of a method specifies that an object (stream, reader or * writer) is closed, then this means that it will be closed on successful * return of the method as well as in an exceptional state. If it says that an * object is NOT closed then the caller is responsible to ensure that it will be * closed properly. * * @author Joerg Hohwiller (hohwille at users.sourceforge.net) */ public class StreamUtil { /** @see #getInstance() */ private static StreamUtil instance; /** @see #getLogger() */ private Log logger; /** @see #getExecutor() */ private Executor executor; /** @see #getByteArrayPool() */ private Pool byteArrayPool; /** @see #getCharArrayPool() */ private Pool charArrayPool; /** * The constructor. */ public StreamUtil() { super(); } /** * This method gets the singleton instance of this {@link StreamUtil}.
* This design is the best compromise between easy access (via this * indirection you have direct, static access to all offered functionality) * and IoC-style design which allows extension and customization.
* For IoC usage, simply ignore all static {@link #getInstance()} methods and * construct new instances via the container-framework of your choice (like * plexus, pico, springframework, etc.). To wire up the dependent components * everything is properly annotated using common-annotations (JSR-250). If * your container does NOT support this, you should consider using a better * one. * * @return the singleton instance. */ public static StreamUtil getInstance() { if (instance == null) { synchronized (StreamUtil.class) { if (instance == null) { instance = new StreamUtil(); instance.setExecutor(SimpleExecutor.INSTANCE); // INSTANCE.setLogger(new Jdk14Logger(StreamUtil.class.getName())); // even more ugly... instance.setLogger(LogFactory.getLog(StreamUtil.class)); instance.setByteArrayPool(NoByteArrayPool.INSTANCE); instance.setCharArrayPool(NoCharArrayPool.INSTANCE); } } } return instance; } /** * This method gets the logger to be used. * * @return the logger. */ protected Log getLogger() { return this.logger; } /** * This method sets the {@link #getLogger() logger}. * * @param logger the logger to set */ @Resource public void setLogger(Log logger) { if (this.logger != null) { throw new AlreadyInitializedException(); } this.logger = logger; } /** * This method gets the {@link Executor} used to run asynchronous tasks. It * may use a thread-pool. * * @return the executor. */ protected Executor getExecutor() { return this.executor; } /** * This method sets the {@link #getExecutor() executor}. * * @param executor the executor to set. */ @Resource public void setExecutor(Executor executor) { if (this.executor != null) { throw new AlreadyInitializedException(); } this.executor = executor; } /** * This method gets the byte-array {@link Pool} used to transfer streams.
* The implementation should create byte-arrays with a suitable length (at * least 512, suggested is 4096). Override this method to use a real pool * implementation. * * @return the {@link Pool} instance. */ protected Pool getByteArrayPool() { return this.byteArrayPool; } /** * This method sets the {@link #getByteArrayPool() byte-array-pool}. * * @param byteArrayPool the byteArrayPool to set */ @Resource public void setByteArrayPool(Pool byteArrayPool) { if (this.byteArrayPool != null) { throw new AlreadyInitializedException(); } this.byteArrayPool = byteArrayPool; } /** * This method gets the char-array {@link Pool} used to transfer * {@link Reader}s and {@link Writer}s. The implementation should create * char-arrays with a suitable length (at least 512, suggested is 2048).
* Override this method to use a real pool implementation. * * @return the {@link Pool} instance. */ protected Pool getCharArrayPool() { return NoCharArrayPool.INSTANCE; } /** * This method sets the {@link #getCharArrayPool() char-array-pool}. * * @param charArrayPool the charArrayPool to set */ @Resource public void setCharArrayPool(Pool charArrayPool) { if (this.charArrayPool != null) { throw new AlreadyInitializedException(); } this.charArrayPool = charArrayPool; } /** * This method reads the contents of the given reader into a * string.
* ATTENTION:
* Only use this method if you know what you are doing. This method will cause * that the complete content of the given reader is read into * memory. * * @param reader is where to read the content from. It will be * {@link Reader#close() closed} at the end. * @return the content of the given reader. * @throws IOException if an error occurred with an I/O error. */ public String read(Reader reader) throws IOException { StringWriter writer = new StringWriter(); transfer(reader, writer, false); return writer.toString(); } /** * This method transfers the contents of the given reader to * the given writer. * * @param reader is where to read the content from. Will be * {@link Reader#close() closed} at the end. * @param writer is where to write the content to. Will be * {@link Reader#close() closed} at the end if * keepWriterOpen is false. * @param keepWriterOpen if true the given writer * will remain open so that additional content can be appended. Else if * false, the writer will be * {@link Reader#close() closed}. * @return the number of bytes that have been transferred. * @throws IOException if the operation failed. Closing is guaranteed even in * exception state. */ public long transfer(Reader reader, Writer writer, boolean keepWriterOpen) throws IOException { ReaderTransferrer transferrer = new ReaderTransferrer(reader, writer, keepWriterOpen, null); long bytes = transferrer.transfer(); return bytes; } /** * This method transfers the contents of the given inStream to * the given outStream using * {@link java.nio.channels.Channel NIO-Channels}. * * @param inStream is where to read the content from. Will be * {@link InputStream#close() closed} at the end. * @param outStream is where to write the content to. Will be * {@link OutputStream#close() closed} at the end if * keepOutStreamOpen is false. * @param keepOutStreamOpen if true the given * outStream will remain open so that additional content * can be appended. Else if false, the * outStream will be {@link OutputStream#close() closed}. * @return the number of bytes that have been transferred. * @throws IOException if the operation failed. Closing is guaranteed even in * exception state. */ public long transfer(FileInputStream inStream, OutputStream outStream, boolean keepOutStreamOpen) throws IOException { FileChannel inChannel = inStream.getChannel(); WritableByteChannel outChannel = Channels.newChannel(outStream); try { return inChannel.transferTo(0, inChannel.size(), outChannel); } finally { // close(inStream); close(inChannel); if (!keepOutStreamOpen) { // close(outStream); close(outChannel); } } } /** * This method transfers the contents of the given inStream to * the given outStream using * {@link java.nio.channels.Channel NIO-Channels}. * * @param inStream is where to read the content from. Will be * {@link InputStream#close() closed} at the end. * @param outStream is where to write the content to. Will be * {@link OutputStream#close() closed} at the end if * keepOutStreamOpen is false. * @param keepOutStreamOpen if true the given * outStream will remain open so that additional content * can be appended. Else if false, the * outStream will be {@link OutputStream#close() closed}. * @param size is the number of bytes to transfer. * @return the number of bytes that have been transferred. * @throws IOException if the operation failed. Closing is guaranteed even in * exception state. */ public long transfer(InputStream inStream, FileOutputStream outStream, boolean keepOutStreamOpen, long size) throws IOException { ReadableByteChannel inChannel = Channels.newChannel(inStream); FileChannel outChannel = outStream.getChannel(); try { return outChannel.transferFrom(inChannel, 0, size); } finally { // close (inStream); close(inChannel); if (!keepOutStreamOpen) { // close(outStream); close(outChannel); } } } /** * This method transfers the contents of the given inStream to * the given outStream. * * @param inStream is where to read the content from. Will be * {@link InputStream#close() closed} at the end. * @param outStream is where to write the content to. Will be * {@link OutputStream#close() closed} at the end if * keepOutStreamOpen is false. * @param keepOutStreamOpen if true the given * outStream will remain open so that additional content * can be appended. Else if false, the * outStream will be {@link OutputStream#close() closed}. * @return the number of bytes that have been transferred. * @throws IOException if the operation failed. Closing is guaranteed even in * exception state. */ public long transfer(InputStream inStream, OutputStream outStream, boolean keepOutStreamOpen) throws IOException { StreamTransferrer transferrer = new StreamTransferrer(inStream, outStream, keepOutStreamOpen, null); long bytes = transferrer.transfer(); return bytes; } /** * This method transfers the contents of the given inStream to * the given outStream. * * @param inStream is where to read the content from. Will be * {@link InputStream#close() closed} at the end. * @param outStream is where to write the content to. Will be * {@link OutputStream#close() closed} at the end if * keepOutStreamOpen is false. * @param keepOutStreamOpen if true the given * outStream will remain open so that additional content * can be appended. Else if false, the * outStream will be {@link OutputStream#close() closed}. * @return the number of bytes that have been transferred. */ public AsyncTransferrer transferAsync(InputStream inStream, OutputStream outStream, boolean keepOutStreamOpen) { return transferAsync(inStream, outStream, keepOutStreamOpen, null); } /** * This method transfers the contents of the given inStream to * the given outStream. * * @param inStream is where to read the content from. Will be * {@link InputStream#close() closed} at the end. * @param outStream is where to write the content to. Will be * {@link OutputStream#close() closed} at the end if * keepOutStreamOpen is false. * @param keepOutStreamOpen if true the given * outStream will remain open so that additional content * can be appended. Else if false, the * outStream will be {@link OutputStream#close() closed}. * @param callback is the callback that is invoked if the transfer is done. * @return the number of bytes that have been transferred. */ public AsyncTransferrer transferAsync(InputStream inStream, OutputStream outStream, boolean keepOutStreamOpen, TransferCallback callback) { StreamTransferrer transferrer = new StreamTransferrer(inStream, outStream, keepOutStreamOpen, callback); AsyncTransferrerImpl task = new AsyncTransferrerImpl(transferrer); this.executor.execute(task); return task; } /** * This method transfers the contents of the given reader to * the given writer. * * @param reader is where to read the content from. Will be * {@link Reader#close() closed} at the end. * @param writer is where to write the content to. Will be * {@link Reader#close() closed} at the end if * keepWriterOpen is false. * @param keepWriterOpen if true the given writer * will remain open so that additional content can be appended. Else if * false, the writer will be * {@link Reader#close() closed}. * @return the number of bytes that have been transferred. */ public AsyncTransferrer transferAsync(Reader reader, Writer writer, boolean keepWriterOpen) { return transferAsync(reader, writer, keepWriterOpen, null); } /** * This method transfers the contents of the given reader to * the given writer. * * @param reader is where to read the content from. Will be * {@link Reader#close() closed} at the end. * @param writer is where to write the content to. Will be * {@link Reader#close() closed} at the end if * keepWriterOpen is false. * @param keepWriterOpen if true the given writer * will remain open so that additional content can be appended. Else if * false, the writer will be * {@link Reader#close() closed}. * @param callback is the callback that is invoked if the transfer is done. * @return the number of bytes that have been transferred. */ public AsyncTransferrer transferAsync(Reader reader, Writer writer, boolean keepWriterOpen, TransferCallback callback) { ReaderTransferrer transferrer = new ReaderTransferrer(reader, writer, keepWriterOpen, callback); AsyncTransferrerImpl task = new AsyncTransferrerImpl(transferrer); this.executor.execute(task); return task; } /** * This method closes the given inputStream without throwing an * {@link Exception}. * * @param inputStream is the input-stream to close. */ public void close(InputStream inputStream) { try { inputStream.close(); } catch (Exception e) { this.logger.warn("Failed to close stream!", e); } } /** * This method closes the given outputStream without throwing * an {@link Exception}. * * @param outputStream is the output-stream to close. */ public void close(OutputStream outputStream) { try { outputStream.close(); } catch (Exception e) { this.logger.warn("Failed to close stream!", e); } } /** * This method closes the given writer without throwing an * {@link Exception}. * * @param writer is the writer to close. */ public void close(Writer writer) { try { writer.close(); } catch (Exception e) { this.logger.warn("Failed to close writer!", e); } } /** * This method closes the given reader without throwing an * {@link Exception}. * * @param reader is the reader to close. */ public void close(Reader reader) { try { reader.close(); } catch (Exception e) { this.logger.warn("Failed to close writer!", e); } } /** * This method closes the given channel without throwing an * {@link Exception}. * * @param channel is the channel to close. */ public void close(Channel channel) { try { channel.close(); } catch (Exception e) { this.logger.warn("Failed to close writer!", e); } } /** * This is the default implementation of the {@link AsyncTransferrer} * interface. */ protected static class AsyncTransferrerImpl extends FutureTask implements AsyncTransferrer { /** the actual task. */ private final BaseTransferrer transferrer; /** * The constructor. * * @param transferrer is the actual transferrer task. */ public AsyncTransferrerImpl(BaseTransferrer transferrer) { super(transferrer); this.transferrer = transferrer; } /** * {@inheritDoc} */ @Override public boolean cancel(boolean mayInterruptIfRunning) { this.transferrer.stop(); return super.cancel(mayInterruptIfRunning); } } /** * This is the abstract base class for the {@link Callable} that transfers * data of streams or readers/writers. */ protected abstract static class AbstractAsyncTransferrer implements Callable, Stoppable { /** @see #stop() */ private volatile boolean stopped; /** @see #isCompleted() */ private volatile boolean completed; /** * {@inheritDoc} */ public void stop() { this.stopped = true; } /** * This method determines if this transferrer was {@link #stop() stopped}. * * @return true if stopped, false otherwise. */ public final boolean isStopped() { return this.stopped; } /** * This method determines if the transfer has been completed successfully. * * @return true if successfully completed, false * if still running, {@link #isStopped() stopped} or an exception * occurred. */ public final boolean isCompleted() { return this.completed; } /** * This method sets the {@link #isCompleted() completed-flag}. */ protected void setCompleted() { this.completed = true; } } /** * This is an abstract implementation of the {@link AsyncTransferrer} * interface, that implements {@link Runnable} defining the main flow. */ protected abstract class BaseTransferrer extends AbstractAsyncTransferrer { /** The callback or null. */ private final TransferCallback callback; /** * The constructor. * * @param callback is the callback or null. */ public BaseTransferrer(TransferCallback callback) { super(); this.callback = callback; } /** * This method performs the actual transfer. * * @return the number of bytes that have been transferred. * @throws IOException if the transfer failed. */ protected abstract long transfer() throws IOException; /** * {@inheritDoc} */ public Long call() throws Exception { try { long bytes = transfer(); if (this.callback != null) { if (isCompleted()) { this.callback.transferCompleted(bytes); } else { this.callback.transferStopped(bytes); } } return Long.valueOf(bytes); } catch (Exception e) { getLogger().error("Error during async transfer!", e); if (this.callback != null) { this.callback.transferFailed(e); } throw e; } } } /** * This inner class is used to transfer an {@link InputStream} to an * {@link OutputStream}. */ protected class StreamTransferrer extends BaseTransferrer { /** The source to read from (to copy). */ private final InputStream source; /** The destination to write to. */ private final OutputStream destination; /** true if {@link #destination} should be closed. */ private final boolean keepDestinationOpen; /** * The constructor. * * @see StreamUtil#transfer(InputStream, OutputStream, boolean) * * @param source is {@link InputStream} to read from. * @param destination the {@link OutputStream} to write to. * @param keepDestinationOpen true if the * destination should be closed. * @param callback is the callback or null. */ public StreamTransferrer(InputStream source, OutputStream destination, boolean keepDestinationOpen, TransferCallback callback) { super(callback); this.source = source; this.destination = destination; this.keepDestinationOpen = keepDestinationOpen; } /** * {@inheritDoc} */ @Override public long transfer() throws IOException { byte[] buffer = getByteArrayPool().borrow(); try { long bytesTransferred = 0; int count = this.source.read(buffer); while ((count > 0) && !isStopped()) { this.destination.write(buffer, 0, count); bytesTransferred += count; count = this.source.read(buffer); } if (count == -1) { setCompleted(); } return bytesTransferred; } finally { try { getByteArrayPool().release(buffer); } finally { close(this.source); if (!this.keepDestinationOpen) { close(this.destination); } } } } } /** * This inner class is used to transfer a {@link Reader} to a {@link Writer}. */ protected class ReaderTransferrer extends BaseTransferrer { /** The source to read from (to copy). */ private final Reader source; /** The destination to write to. */ private final Writer destination; /** true if {@link #destination} should be closed. */ private final boolean keepDestinationOpen; /** * The constructor. * * @see StreamUtil#transfer(Reader, Writer, boolean) * * @param source is {@link Reader} to read from. * @param destination the {@link Writer} to write to. * @param keepDestinationOpen true if the * destination should be closed. * @param callback is the callback or null. */ public ReaderTransferrer(Reader source, Writer destination, boolean keepDestinationOpen, TransferCallback callback) { super(callback); this.source = source; this.destination = destination; this.keepDestinationOpen = keepDestinationOpen; } /** * {@inheritDoc} */ @Override public long transfer() throws IOException { char[] buffer = getCharArrayPool().borrow(); try { long bytesTransferred = 0; int count = this.source.read(buffer); while ((count > 0) && !isStopped()) { this.destination.write(buffer, 0, count); bytesTransferred += count; count = this.source.read(buffer); } if (count == -1) { setCompleted(); } return bytesTransferred; } finally { try { getCharArrayPool().release(buffer); } finally { close(this.source); if (!this.keepDestinationOpen) { close(this.destination); } } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy