blob: ad16c40a09f267f1f5a7a68e74d4a9fe52680db3 [file] [log] [blame]
package org.xmlrpc.android;
import java.io.IOException;
import java.io.InputStream;
/**
* A LoggedInputStream adds logging functionality to another input stream.
* <p>Note that calls on a LoggedInputStream are passed "as-is" to the underlying stream.</p>
*
*
* <p>We're using a LoggedInputStream in {@code XMLRPClient.java} to log the XML-RPC response document in case of parser errors.<br />
*
* There are plenty of other ways to log the response, but a {@code XmlPullParser} wants an InputStream as input parameter, and
* a LoggedInputStream seems the most reliable solution, with the smallest memory footprint.<br />
* Below are other examples of logging we tried:</p>
* <ul>
* <li>Read the first 1000 characters from the original input stream, then create a new SequenceInputStream with both the characters just read (a new ByteArrayInputStream),
* and the original input stream.</li>
* <li>Read the whole content in a String and log it, then create an StringInputStream over the string, and pass the new stream to the parser.</li>
* </ul>
*/
public final class LoggedInputStream extends InputStream {
private final InputStream inputStream;
private final static int MAX_LOG_SIZE = 1000;
private final byte[] loggedString = new byte[MAX_LOG_SIZE];
private int loggedStringSize = 0;
public LoggedInputStream(InputStream input) {
this.inputStream = input;
}
@Override
public int available() throws IOException {
return inputStream.available();
}
@Override
public void close() throws IOException {
inputStream.close();
}
@Override
public void mark(int readlimit) {
inputStream.mark(readlimit);
}
@Override
public boolean markSupported() {
return inputStream.markSupported();
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
int bytesRead = inputStream.read(buffer, byteOffset, byteCount);
if (bytesRead != -1) {
log(buffer, byteOffset, bytesRead);
}
return bytesRead;
}
@Override
public int read(byte[] buffer) throws IOException {
return this.read(buffer, 0, buffer.length);
}
@Override
public int read() throws IOException {
int characterRead = inputStream.read();
if (characterRead != -1) {
log(characterRead);
}
return characterRead;
}
@Override
public synchronized void reset() throws IOException {
inputStream.reset();
}
@Override
public long skip(long byteCount) throws IOException {
return inputStream.skip(byteCount);
}
private void log(byte[] inputArray, int byteOffset, int byteCount) {
int availableSpace = MAX_LOG_SIZE - loggedStringSize;
if (availableSpace <= 0) {
return;
}
int bytesLength = Math.min(availableSpace, byteCount);
int startingPosition = MAX_LOG_SIZE - availableSpace;
System.arraycopy(inputArray, byteOffset, loggedString, startingPosition, bytesLength);
loggedStringSize += bytesLength;
}
private void log(int inputChar) {
byte[] logThis = {(byte) inputChar};
log(logThis, 0, 1);
}
public String getResponseDocument() {
if (loggedStringSize == 0) {
return "";
} else {
return new String(loggedString, 0, loggedStringSize);
}
}
}