Is there a simple “tee” filter for Java input streams?
For debugging purposes I would like to dump the content of an input stream into a file while it is processed. The stream is parsed by org.xml.sax.XMLReader
which will consume the data.
I would gess one need a filter to archive this and before writing one my self I wanted to ask if there is a ready made version already.
I work on Android.
What I have:
final org.apache.http.HttpEntity responseEntity = response.getEntity ();
final java.io.InputStream content = responseEntit开发者_高级运维y.getContent ();
final java.io.InputStreamReader contentReader =
new java.io.InputStreamReader (content, "UTF-8");
So I have an java.io.InputStream
and an InputStreamReader
.
Commons IO to the rescue! Check out TeeInputStream and TeeOutputStream.
Not quite a ready rolled one, but this might be of interest. There is a TeeOutputStream
implementation in the examples.
Thanks for all the pointer. Here a copy of the class I created to solve my problem:
/**
* <p>
* Tee-Filters a Reader into an Writer.
* </p>
*
* @author "Martin Krischik" <martin.krischik@noser.com>
* @version 1.0 $Revision: 1046 $
* @since 1.0
*/
public class TeeReader
extends
java.io.Reader
{
/**
* <p>
* Reader to read from
* </p>
*/
private final java.io.Reader in;
/**
* <p>
* Tee output to which read date is written before beeing passed on.
* </p>
*/
private final java.io.Writer tee;
/**
* <p>
* create new filter.
* </p>
*
* @param in
* Reader to read from
* @param tee
* Tee output to which read date is written before beeing passed
* on.
*/
public TeeReader (final java.io.Reader in, final java.io.Writer tee)
{
this.in = in;
this.tee = tee;
} // TeeReader
/**
* <p>
* Close the stream. Once a stream has been closed, further read(), ready(),
* mark(), or reset() invocations will throw an IOException. Closing a
* previously-closed stream, however, has no effect.
* </p>
*
* @throws java.io.IOException
* @see java.io.Reader#close()
*/
@Override
public void close ()
throws java.io.IOException
{
this.in.close ();
} // close
/**
* <p>
* Reads characters into a portion of an array. This method will block until
* some input is available, an I/O error occurs, or the end of the stream is
* reached.
* </p>
*
* @param cbuf
* Destination buffer
* @param off
* Offset at which to start storing characters
* @param len
* Maximum number of characters to read
* @return The number of characters read, or -1 if the end of the stream has
* been reached Throws:
* @throws java.io.IOException
* @see java.io.Reader#read(char[], int, int)
*/
@Override
public int read (final char [] cbuf, final int off, final int len)
throws java.io.IOException
{
final int retval = this.in.read (cbuf, off, len);
if (retval >= 0)
{
this.tee.write (cbuf, off, len);
}
else
{
this.tee.close ();
} // if
return retval;
} // read
} // TeeReader
I had the same problem. I wanted to be able to see what the Xml parser is getting so I could debug the server-side scripts. Ideally, you want to send the Xml to both the Xml parser and LogCat. You can use the enclosed clss InputStreamIntercept to do that. Just run the input stream through it:
final java.io.InputStream content =
new InputStreamIntercept(responseEntity.getContent());
Here is the class:
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import android.util.Log;
/**
* This custom stream filter can intercept data being
* sent to another stream and send it to LogCat. It is
* useful as a way to tell what is being sent to an
* Xml parser.
*/
public class InputStreamIntercept extends FilterInputStream {
/**
* The string we build from data we input.
*/
private StringBuilder sb = new StringBuilder();
/**
* Constructor.
* @param in the stream we are intercepting
*/
public InputStreamIntercept(InputStream in) {
super(in);
}
/**
* Intercept read() and store the character for output
* @returns -1 if nothing read or the character read
*/
@Override
public int read() throws IOException {
int ret = super.read();
if(ret >= 0) {
newChar((byte)ret);
}
return ret;
}
/**
* Intercept read and output the content to Log.i.
*/
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
int ret = super.read(buffer, offset, count);
for(int i=0; i<ret; i++) {
newChar(buffer[i]);
}
return ret;
}
/**
* Handle a new character. We output whenever we get a newline
* @param ch
*/
private void newChar(byte ch) {
if(ch == 10) {
String str = sb.toString();
if(str.equals("")) {
Log.i("debug", "--blank line--");
} else {
Log.i("debug", str);
}
sb = new StringBuilder();
} else if(ch == 13) {
// Skip this character
} else {
sb.append((char)ch);
}
}
/**
* Close the stream
*/
@Override
public void close() throws IOException {
super.close();
if(sb.length() > 0) {
Log.i("debug", sb.toString());
}
}
}
Here's a trivial output stream version (without exception handling):
public class TeeOutputStream extends OutputStream
{
private OutputStream[] streams;
public TeeOutputStream(final OutputStream ... streams)
{
this.streams = streams;
}
@Override
public void close() throws IOException
{
for (OutputStream stream : streams)
{
stream.close();
}
}
@Override
public void flush() throws IOException
{
for (OutputStream stream : streams)
{
stream.flush();
}
}
@Override
public void write(final byte[] buffer) throws IOException
{
for (OutputStream stream : streams)
{
stream.write(buffer);
}
}
@Override
public void write(final byte[] buffer, final int offset, final int count) throws IOException
{
for (OutputStream stream : streams)
{
stream.write(buffer, offset, count);
}
}
@Override
public void write(final int i) throws IOException
{
for (OutputStream stream : streams)
{
stream.write(i);
}
}
}
精彩评论