001 package org.rakeshv.io;
002
003 import java.io.BufferedOutputStream;
004 import java.io.File;
005 import java.io.FileOutputStream;
006 import java.io.FileNotFoundException;
007 import java.io.IOException;
008 import java.io.InputStream;
009 import java.io.OutputStream;
010
011 /**
012 * A decorator class that logs all the data it reads from the underlying
013 * <code>InputStream</code> to a specified <code>OutputStream</code>.
014 *
015 * <p>The following code shows a way of using this class.</p>
016 * <pre>
017 * import org.rakeshv.io.LoggingInputStream;
018 * import java.io.BufferedInputStream;
019 * import java.io.FileInputStream;
020 *
021 * public class test
022 * {
023 * public static void main( String[] args )
024 * {
025 * try
026 * {
027 * String logFile = "/tmp/log.txt";
028 * BufferedInputStream inputStream = new BufferedInputStream(
029 * new FileInputStream( "/tmp/test.java" ) );
030 * LoggingInputStream lis = new LoggingInputStream( inputStream,
031 * logFile );
032 * byte[] buffer = new byte[ 2048 ];
033 * int length = 0;
034 * while ( ( length = lis.read( buffer, 0, buffer.length ) ) != -1 )
035 * {
036 * }
037 * lis.close();
038 * }
039 * catch ( Throwable t )
040 * {
041 * t.printStackTrace();
042 * }
043 * }
044 * }
045 * </pre>
046 *
047 * @see LoggingReader
048 * <p>© Copyright 2005, Rakesh Vidyadharan</p>
049 * @author Rakesh Vidyadharan 19<sup><small>th</small></sup> September 2005
050 *
051 * @version $Id: LoggingInputStream.java,v 1.1 2005/09/20 11:59:15 rakesh Exp $
052 */
053 public class LoggingInputStream extends InputStream
054 {
055 /**
056 * The <code>InputStream</code> from which the data that is to be
057 * logged will be read.
058 */
059 private InputStream inputStream;
060
061 /**
062 * The stream to which the contents read from the stream are to be
063 * logged.
064 */
065 private OutputStream outputStream;
066
067 /**
068 * A flag used to indicate that the {@link #outputStream} must be
069 * flushed prior to closing this input stream.
070 */
071 private boolean flush = false;
072
073 /**
074 * Create a new instance of the class that decorates the specified
075 * <code>InputStream</code>, and the specified <code>OutputStream
076 * </code>
077 *
078 * @param inputStream The stream from which data will be read. This
079 * is the stream that will be decorated.
080 * @param outputStream The stream to which the data read is to be
081 * logged.
082 */
083 public LoggingInputStream( InputStream inputStream,
084 OutputStream outputStream )
085 {
086 setInputStream( inputStream );
087 setOutputStream( outputStream );
088 }
089
090 /**
091 * Create a new instance of the class that decorates the specified
092 * <code>InputStream</code>, and the specified <code>fileName
093 * </code>
094 *
095 * @see FileUtilities#mkdirs
096 * @param inputStream The stream from which data will be read. This
097 * is the stream that will be decorated.
098 * @param fileName The fully qualified name of the file to which the
099 * data is to be logged.
100 * @throws FileNotFoundException If the specified file could not be
101 * created or accessed.
102 */
103 public LoggingInputStream( InputStream inputStream, String fileName )
104 throws FileNotFoundException
105 {
106 setInputStream( inputStream );
107
108 File file = new File( fileName );
109 FileUtilities.mkdirs( file );
110 BufferedOutputStream outputStream = new BufferedOutputStream(
111 new FileOutputStream( file ) );
112
113 setFlush( true );
114 setOutputStream( outputStream );
115 }
116
117 /**
118 * Create a new instance of the class that decorates the specified
119 * <code>InputStream</code>, and the specified <code>file</code>
120 *
121 * @see FileUtilities#mkdirs
122 * @param inputStream The stream from which data will be read. This
123 * is the stream that will be decorated.
124 * @param file The file to which the data is to be logged.
125 * @throws FileNotFoundException If the specified file could not be
126 * created or accessed.
127 */
128 public LoggingInputStream( InputStream inputStream, File file )
129 throws FileNotFoundException
130 {
131 setInputStream( inputStream );
132
133 FileUtilities.mkdirs( file );
134 BufferedOutputStream outputStream = new BufferedOutputStream(
135 new FileOutputStream( file ) );
136
137 setFlush( true );
138 setOutputStream( outputStream );
139 }
140
141 /**
142 * Reads the next byte of data from the input stream. The value byte
143 * is returned as an int in the range <code>0</code> to <code>255
144 * </code>. If no byte is available because the end of the stream has
145 * been reached, the value <code>-1</code> is returned. This method
146 * blocks until input data is available, the end of the stream is
147 * detected, or an exception is thrown.
148 *
149 * @return int - The next byte of data, or <code>-1</code> if the
150 * end of the stream is reached.
151 * @throws IOException If errors are encountered while reading the
152 * byte of data.
153 */
154 public int read() throws IOException
155 {
156 int data = inputStream.read();
157 if ( data != -1 )
158 {
159 outputStream.write( data );
160 }
161
162 return data;
163 }
164
165 /**
166 * Reads some number of bytes from the input stream and stores them
167 * into the buffer array <code>buffer</code>. The method behaves
168 * similar to the implementation in the decorated underlying {@link
169 * #inputStream}.
170 *
171 * @see #read( byte[], int, int )
172 * @param buffer The buffer into which the data will be read.
173 * @return int - The total number of bytes read into the buffer, or
174 * <code>-1</code> is there is no more data because the end of the
175 * stream has been reached.
176 * @throws IOException If errors are encountered while reading the
177 * data.
178 * @throws NullPointerException If the <code>buffer</code> is
179 * <code>null</code>.
180 */
181 public int read( byte[] buffer )
182 throws IOException, NullPointerException
183 {
184 return read( buffer, 0, buffer.length );
185 }
186
187 /**
188 * Reads up to <code>length</code> bytes of data from the input
189 * stream into an array of bytes. An attempt is made to read as many
190 * as <code>length</code> bytes, but a smaller number may be read,
191 * possibly <code>zero</code>. The number of bytes actually read is
192 * returned as an integer. This method behaves similar to the
193 * implementation in the decorated underlying {@link #inputStream}.
194 *
195 * @param buffer The buffer into which the data will be read.
196 * @param offset The start offset in array <code>buffer</code> at
197 * which the data is written.
198 * @param length The maximum number of bytes to read.
199 * @return int - The total number of bytes read into the buffer, or
200 * <code>-1</code> is there is no more data because the end of the
201 * stream has been reached.
202 * @throws IOException If errors are encountered while reading the
203 * data.
204 * @throws NullPointerException If the <code>buffer</code> is
205 * <code>null</code>.
206 */
207 public int read( byte[] buffer, int offset, int length )
208 throws IOException, NullPointerException
209 {
210 int size = inputStream.read( buffer, offset, length );
211 if ( size != -1 )
212 {
213 outputStream.write( buffer, offset, size );
214 }
215
216 return size;
217 }
218
219 /**
220 * Skips over and discards <code>number</code> bytes of data from
221 * this input stream. Delegates to the decorated {@link
222 * #inputStream} implementation.
223 *
224 * @param number The number of bytes to be skipped.
225 * @return long - The actual number of bytes skipped.
226 * @throws IOException If errors are encountered while skipping.
227 */
228 public long skip( long number ) throws IOException
229 {
230 return inputStream.skip( number );
231 }
232
233 /**
234 * Returns the number of bytes that can be read (or skipped over)
235 * from this input stream without blocking by the next caller of a
236 * method for this input stream. The next caller might be the same
237 * thread or or another thread.
238 *
239 * @return int - The number of bytes that can be read from this
240 * input stream without blocking.
241 * @throws IOException If errors are encountered while interacting
242 * with the input stream.
243 */
244 public int available() throws IOException
245 {
246 return inputStream.available();
247 }
248
249 /**
250 * Closes this input stream and releases any system resources
251 * associated with the stream. Closes the {@link #inputStream} that
252 * this stream decorates. Also closes the {@link #outputStream}
253 * to which the data is logged. If the {@link #flush} is
254 * <code>true</code>, then the {@link #outputStream} is flushed
255 * prior to closing.
256 *
257 * @throws IOException If errors are encountered while interacting
258 * with the input stream.
259 */
260 public void close() throws IOException
261 {
262 inputStream.close();
263
264 if ( flush )
265 {
266 outputStream.flush();
267 }
268
269 outputStream.close();
270 }
271
272 /**
273 * Marks the current position in this input stream. A subsequent call
274 * to the reset method repositions this stream at the last marked
275 * position so that subsequent reads re-read the same bytes.
276 *
277 * @param readlimit The maximum limit of bytes that can be read
278 * before the mark position becomes invalid.
279 */
280 public void mark( int readlimit )
281 {
282 inputStream.mark( readlimit );
283 }
284
285 /**
286 * Repositions this stream to the position at the time the mark
287 * method was last called on this input stream.
288 *
289 * @see #mark
290 * @throws IOException If this stream has not been marked or if the
291 * mark has been invalidated.
292 */
293 public void reset() throws IOException
294 {
295 inputStream.reset();
296 }
297
298 /**
299 * Tests if this input stream supports the mark and reset methods.
300 * Whether or not mark and reset are supported is an invariant
301 * property of a particular {@link #inputStream} used to initialise
302 * this stream.
303 *
304 * @return boolean - Returns <code>true</code> if this stream
305 * instance supports the {@link #mark} and {@link #reset} methods;
306 * <code>false</code> otherwise.
307 */
308 public boolean markSupported()
309 {
310 return inputStream.markSupported();
311 }
312
313 /**
314 * Flushes the {@link #outputStream}. Invoke this method if the
315 * <code>OutputStream</code> you used to initialise this stream
316 * requires to be flushed prior to a <code>close</code> invocation.
317 * You <b>must</b> invoke this method prior to invoking the
318 * {@link #close} method if the <code>OutputStream</code> needs to
319 * be flushed.
320 */
321 public void flush() throws IOException
322 {
323 outputStream.flush();
324 }
325
326 /**
327 * Returns {@link #inputStream}.
328 *
329 * @return InputStream The value/reference of/to inputStream.
330 */
331 public final InputStream getInputStream()
332 {
333 return inputStream;
334 }
335
336 /**
337 * Set {@link #inputStream}.
338 *
339 * @param inputStream The value to set.
340 */
341 protected final void setInputStream( InputStream inputStream )
342 {
343 this.inputStream = inputStream;
344 }
345
346 /**
347 * Returns {@link #outputStream}.
348 *
349 * @return OutputStream The value/reference of/to outputStream.
350 */
351 public final OutputStream getOutputStream()
352 {
353 return outputStream;
354 }
355
356 /**
357 * Set {@link #outputStream}.
358 *
359 * @param outputStream The value to set.
360 */
361 protected final void setOutputStream( OutputStream outputStream )
362 {
363 this.outputStream = outputStream;
364 }
365
366 /**
367 * Returns {@link #flush}.
368 *
369 * @return boolean The value/reference of/to flush.
370 */
371 public final boolean getFlush()
372 {
373 return flush;
374 }
375
376 /**
377 * Set {@link #flush}.
378 *
379 * @param flush The value to set.
380 */
381 protected final void setFlush( boolean flush )
382 {
383 this.flush = flush;
384 }
385 }