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

com.gc.iotools.stream.is.inspection.StatsInputStream 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.inspection;

/*
 * Copyright (c) 2008, 2015 Gabriele Contini. This source code is released
 * under the BSD License.
 */

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

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

import com.gc.iotools.stream.base.EasyStreamConstants;
import com.gc.iotools.stream.utils.LogUtils;
import com.gc.iotools.stream.utils.StreamUtils;

/**
 * 

* Gather some statistics of the InputStream passed in the * constructor. *

*

* It can be used to read: *

    *
  • The size of the internal stream.
  • *
  • The time spent reading the bytes.
  • *
  • The raw bandwidth of the underlying stream, calculated excluding the * time spent by the external process to elaborate the data.
  • *
  • The average bytes read each read() call.
  • *
*

*

* Full statistics are available after the stream has been fully processed (by * other parts of the application), or after invoking the method * {@linkplain #close()} while partial statistics are available on the fly. *

*

* Usage: *

* *
 * StatsInputStream srIstream = new StatsInputStream(originalStream);
 * //performs all the application operation on stream
 * performTasksOnStream(srIstream);
 * srIstream.close();
 * long size = srIstream.getSize();
 * 
* * @author dvd.smnt * @since 1.2.1 * @version $Id: StatsInputStream.java 576 2015-03-28 00:03:33Z gcontini $ */ public class StatsInputStream extends InputStream { private static Map instanceNumber = new HashMap(); private static final Logger LOGGER = LoggerFactory .getLogger(StatsInputStream.class); private static Map totalBytes = new HashMap(); private static Map totalRead = new HashMap(); private static Map totalTime = new HashMap(); private final boolean automaticLog; private final String callerId; private final StatsInputStream chainStream; private boolean closeCalled = false; private final boolean fullReadOnClose; private final InputStream innerStream; private long markPosition = 0; private long numberRead = 0; private long size = 0; private long time = 0; /** *

* Constructs an SizeReaderInputStream. When * {@linkplain #close()} is called the underlying stream will be closed. * No further read will be done. *

* * @param source * Stream whose statistics must be calculated. */ public StatsInputStream(final InputStream source) { this(source, false); } /** * Constructs an SizeReaderInputStream and allow to specify * actions to do on close. * * @param istream * Stream whose bytes must be counted. * @param fullReadOnClose * if true after the close the inner stream is read * completely and the effective size of the inner stream is * calculated. */ public StatsInputStream(final InputStream istream, final boolean fullReadOnClose) { this(istream, fullReadOnClose, false); } /** *

* Constructs an SizeReaderInputStream and allow to specify * actions to do on close. *

*

* If automaticLog is true the statistics will be written * when the StatsInputStream is closed or finalized. *

* * @param istream * Stream whose bytes must be counted. * @param fullReadOnClose * if true after the close the inner stream is read * completely and the effective size of the inner stream is * calculated. * @param automaticLog * if true statistics will be automatically * written when the stream is closed or finalized. * @since 1.2.7 */ public StatsInputStream(final InputStream istream, final boolean fullReadOnClose, final boolean automaticLog) { this(istream, fullReadOnClose, automaticLog, null); } /** *

* Constructs an SizeReaderInputStream and allow to specify * actions to do on close. *

*

* If automaticLog is true the statistics will be written * when the StatsInputStream is closed or finalized. *

*

* Indicates another StatsInputStream to chain with. The aim * is to test performances of a single InputStream in a chain * of multiple InputStreams. You should put the * InputStream to be tested between two * StatsInputStream and chain the two together. *

* * InputStream source = //source of data * StatsInputStream sourceStats = new StatsInputStream(source); * InputStream toBeTested = new InputStreamToBeTested(stis); * StatsInputStream wrapperStis=new StatsInputStream(toBeTested, false, false, sourceStats); * *

* This will allow to produce statistics of the single * InputStream to be tested, in a way independent from the * source. Times spent will be the difference between the times from the * source and times on the final wrapper. *

* * @param istream * Stream whose bytes must be counted. * @param fullReadOnClose * if true after the close the inner stream is read * completely and the effective size of the inner stream is * calculated. * @param automaticLog * if true statistics will be automatically * written when the stream is closed or finalized. * @param chainStream * The InputStream to chain. * @since 1.3.0 */ public StatsInputStream(final InputStream istream, final boolean fullReadOnClose, final boolean automaticLog, final StatsInputStream chainStream) { if (istream == null) { throw new IllegalArgumentException("InputStream can't be null"); } this.innerStream = istream; this.fullReadOnClose = fullReadOnClose; this.automaticLog = automaticLog; this.callerId = LogUtils.getCaller(this.getClass()); this.chainStream = chainStream; addToMapL(instanceNumber, 1); } private void addToMap(final Map map, final long value) { if (!map.containsKey(this.callerId)) { map.put(this.callerId, BigInteger.valueOf(value)); } else { final BigInteger mvalue = map.get(this.callerId); mvalue.add(BigInteger.valueOf(value)); } } private void addToMapL(final Map map, final long value) { if (!map.containsKey(this.callerId)) { map.put(this.callerId, value); } else { final long mvalue = map.get(this.callerId); map.put(this.callerId, mvalue + value); } } /** {@inheritDoc} */ @Override public int available() throws IOException { return this.innerStream.available(); } /** * {@inheritDoc} * * Closes the inner stream. If fullReadOnClose was set in the * constructor it also count all the bytes of the underlying stream. * @see InputStream#close() * @exception IOException * if an I/O error occurs reading the whole content of the * stream. */ @Override public void close() throws IOException { if (!this.closeCalled) { final long start = System.currentTimeMillis(); // avoid multiple calls to close(); this.closeCalled = true; try { if (this.fullReadOnClose) { final byte[] buffer = new byte[EasyStreamConstants.SKIP_BUFFER_SIZE]; while (this.read(buffer) >= 0) { // Do nothing, just throw away the bytes and count // them. } } } finally { this.innerStream.close(); final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); } } } /** {@inheritDoc} */ @Override protected void finalize() throws Throwable { if (this.automaticLog) { logCurrentStatistics(); } super.finalize(); } /** *

* Returns the average bytes per read. *

*

* If this parameter is near 1 means that too many calls are made to read * the InputStream bringing a loss of performances for large * amount of data. Access to this InputStream should be made * trough a BufferedInputStream with a reasonable buffer * size. *

*

* WARN: This measure is not accurate in case of mark and reset. *

* * @return The average bytes per read(). */ public float getAverageBytePerRead() { return (this.size * 1.0f) / this.numberRead; } /** * Returns the reading bit rate in KB per second of this single instance. * * @return The KB/Sec bitRate of the stream. */ public float getBitRate() { return (this.size / EasyStreamConstants.ONE_KILOBYTE) / TimeUnit.SECONDS.convert(getTime(), TimeUnit.MILLISECONDS); } /** * Returns the reading bit rate formatted with a convenient unit. * * @return The bitRate of the stream. */ public String getBitRateString() { return StreamUtils.getRateString(this.size, this.time); } /** * Number of calls to int read() , * int read(byte[]) and int read(byte[],int,int) * methods. * * @return Long representing the number of calls to read() methods. */ public long getNumberRead() { return this.numberRead; } /** *

* Returns the number of bytes read until now from the internal * InputStream or total length of the stream if the * {@link #close()} method has been called or EOF was * reached. *

*

* Calculation refers to the original size of the internal * InputStream. If {@linkplain #mark(int)} and * {@linkplain #reset()} are called, the extra data read after the * {@linkplain #reset()} is not taken in account, until the * mark position is reached again. *

* * @return bytes read until now or the total length of the stream if * close() was called. */ public long getSize() { return this.size; } /** *

* Returns the time (in milliseconds) spent until now waiting for reading * from the internal InputStream. *

* * @return time spent in waiting in milliseconds. */ public long getTime() { long time2 = this.time; if (this.chainStream != null) { time2 -= this.chainStream.getTime(); } return time2; } /** *

* Returns the time spent until now waiting for the internal stream to * respond. *

* * @param tu * Unit to measure the time. * @return time spent in waiting. */ public long getTime(final TimeUnit tu) { if (tu == null) { throw new IllegalArgumentException("TimeUnit can't be null"); } long convertedTime = tu.convert(this.time, TimeUnit.MILLISECONDS); if (this.chainStream != null) { convertedTime -= this.chainStream.getTime(tu); } return convertedTime; } /** * Total count of calls to int read(), * int read(byte[]) and int read(byte[],int,int) * methods, made by this instance over the subsequent calls. * * @return Long representing the number of calls to read() methods. */ public long getTotalNumberRead() { final BigInteger totalReadBytes = StatsInputStream.totalRead .get(this.callerId); return (totalReadBytes == null) ? 0 : totalReadBytes.longValue(); } /** *

* Returns the total time (in milliseconds) spent until now waiting for * reading from the internal InputStream by the instances * (identified by their constructor position). *

* * @param tu * Unit to measure the time. * @return time spent in waiting. */ public long getTotalTime(final TimeUnit tu) { if (tu == null) { throw new IllegalArgumentException("TimeUnit can't be null"); } final Long totalTimeLocal = StatsInputStream.totalTime .get(this.callerId); final long timeMs = (totalTimeLocal == null) ? 0 : totalTimeLocal; long convertedTotalTime = tu.convert(timeMs, TimeUnit.MILLISECONDS); if (this.chainStream != null) { convertedTotalTime = convertedTotalTime - this.chainStream.getTotalTime(tu); } return convertedTotalTime; } private void internallogCurrentStatistics( final boolean addNotClosedWarning) { final StringBuffer message = new StringBuffer("Time spent["); message.append(getTime()); message.append("]ms, bytes read ["); message.append(getSize()); message.append("] at ["); message.append(getBitRate()); message.append("]."); if (addNotClosedWarning) { message.append("The stream is being finalized and " + "close() was not called."); } LOGGER.info(message.toString()); } /** * Returns the behavior of the close method. If true when close is invoked * a full read of the stream will be performed. * * @return Whether a full read will be performed on the invocation of * {@linkplain #close()} method. */ public boolean isFullReadOnClose() { return this.fullReadOnClose; } /** * Logs the current statistics. */ public void logCurrentStatistics() { internallogCurrentStatistics(false); } /** {@inheritDoc} */ @Override public void mark(final int readlimit) { final long start = System.currentTimeMillis(); this.innerStream.mark(readlimit); this.markPosition = this.size; final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); } /** {@inheritDoc} */ @Override public boolean markSupported() { return this.innerStream.markSupported(); } /** {@inheritDoc} */ @Override public int read() throws IOException { final long start = System.currentTimeMillis(); final int read = this.innerStream.read(); if (read >= 0) { this.size++; addToMap(totalBytes, 1); } addToMap(totalRead, 1); this.numberRead++; final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); return read; } /** {@inheritDoc} */ @Override public int read(final byte[] b) throws IOException { final long start = System.currentTimeMillis(); final int read = this.innerStream.read(b); if (read >= 0) { this.size += read; addToMap(totalBytes, read); } final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); this.numberRead++; addToMap(totalRead, 1); return read; } /** {@inheritDoc} */ @Override public int read(final byte[] b, final int off, final int len) throws IOException { final long start = System.currentTimeMillis(); final int read = this.innerStream.read(b, off, len); if (read >= 0) { this.size += read; addToMap(totalBytes, read); } this.numberRead++; addToMap(totalRead, 1); final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); return read; } /** {@inheritDoc} */ @Override public void reset() throws IOException { final long start = System.currentTimeMillis(); this.innerStream.reset(); this.size = this.markPosition; final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); } /** {@inheritDoc} */ @Override public long skip(final long n) throws IOException { final long start = System.currentTimeMillis(); final long skipSize = this.innerStream.skip(n); this.size += skipSize; final long timeElapsed = System.currentTimeMillis() - start; this.time += timeElapsed; addToMapL(totalTime, timeElapsed); return skipSize; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy