Get Progress of File Upload using HttpPost in Android - java

I am trying to get the progress of the actual file upload using HttpPost. I have a stable solution so far (that i found here in SO) that has a progress but after uploading large files i realized it only counts the bytes written to the output buffer NOT the post transfer progress. I would like to somehow get the progress of the actual "post". Can someone explain how I can achieve this using what I worked so hard to get so far? Most of the solutions I have found online are only counting bytes written to output buffer (which is good enough for small files but not when transferring large files).
public static String postFile(final Context context, String fileName) throws Exception {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://my.url/");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
final File file = new File(fileName);
final long totalSize = file.length();
FileBody fb = new FileBody(file);
builder.addPart("uploaded_file", new FileBody(new File(fileName)));
final HttpEntity yourEntity = builder.build();
int progressPercent = 0;
class ProgressiveEntity implements HttpEntity {
#Override
public void consumeContent() throws IOException {
yourEntity.consumeContent();
}
#Override
public InputStream getContent() throws IOException,
IllegalStateException {
return yourEntity.getContent();
}
#Override
public Header getContentEncoding() {
return yourEntity.getContentEncoding();
}
#Override
public long getContentLength() {
return yourEntity.getContentLength();
}
#Override
public Header getContentType() {
return yourEntity.getContentType();
}
#Override
public boolean isChunked() {
return yourEntity.isChunked();
}
#Override
public boolean isRepeatable() {
return yourEntity.isRepeatable();
}
#Override
public boolean isStreaming() {
return yourEntity.isStreaming();
} // CONSIDER put a _real_ delegator into here!
#Override
public void writeTo(OutputStream outstream) throws IOException {
class ProxyOutputStream extends FilterOutputStream {
public ProxyOutputStream(OutputStream proxy) {
super(proxy);
}
public void write(int idx) throws IOException {
out.write(idx);
}
public void write(byte[] bts) throws IOException {
out.write(bts);
}
public void write(byte[] bts, int st, int end) throws IOException {
out.write(bts, st, end);
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
} // CONSIDER import this class (and risk more Jar File Hell)
class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
totalSent = 0;
}
public void write(byte[] bts, int st, int end) throws IOException {
// end is the amount being sent this time
// st is always zero and end=bts.length()
totalSent += end;
int progress = (int) ((totalSent / (float) totalSize) * 100);
out.write(bts, st, end);
}
}
yourEntity.writeTo(new ProgressiveOutputStream(outstream));
}
};
ProgressiveEntity myEntity = new ProgressiveEntity();
post.setEntity(myEntity);
//Output to buffer is complete at this point!
HttpResponse response = client.execute(post);
String jsonResponseStr = getContent(response);
Log.d("MYTAG",jsonResponseStr);
return jsonResponseStr;
}
In my receiving script on the remote server, I am only echo-ing a string so that I can send an immediate response (no file/database processing at all) and the response from server still takes a very long time. I strongly believe at this point the transfer happens after write to buffer completes.

class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
totalSent = 0;
}
public void write(byte[] bts, int st, int end) throws IOException {
// FIXME Put your progress bar stuff here!
// end is the amount being sent this time
// st is always zero and end=bts.length()
totalSent += end;
progress.publish((int) ((totalSent / (float) totalSize) * 100));
out.write(bts, st, end);
}

Since I don't see any solution for this, I suppose the answer is to use a spinning animation without a progress percent. Since nothing can be done until the transmission is complete anyway. Oh well... atleast it solved my problem.

I know this is quite old, but I've just found the solution.
If totalSize is your entity content length, then you can base your progress on that:
class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
long totalSize;
public ProgressiveOutputStream(OutputStream proxy, long total) {
super(proxy);
totalSent = 0;
totalSize = total;
}
public void write(byte[] bts, int st, int end) throws IOException {
totalSent += end;
publishProgress((int) ((totalSent / (float) totalSize) * 100));
out.write(bts, st, end);
}
}
yourEntity.writeTo(new ProgressiveOutputStream(outstream, yourEntity.getContentLength()));
You update your progressbar in OnProgressUpdate of your asyncTask (pb is the progressBar):
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
pb.setProgress(values[0]);
}

maybe you could flush the data every time you call write:
totalSent += end;
int progress = (int) ((totalSent / (float) totalSize) * 100);
out.write(bts, st, end);
out.flush(); //flush
Edit:
you can also try this since i get the feeling end represents the end index in the outputstream named out:
totalSent += (end-st);

Please try next solution instead of using ProxyOutputStream in your writeTo method:
#Override
public void writeTo(OutputStream outstream) throws IOException {
ByteArrayInputStream reader = new ByteArrayInputStream(mImageData);
byte[] fileBuffer = new byte[2048];
int bytesRead;
while ((bytesRead = reader.read(fileBuffer)) != -1) {
outstream.write(fileBuffer, 0, bytesRead);
int progress = bytesRead;
}
reader.close();
yourEntity.writeTo(outstream);
}

Related

Get progress information during JAXB de-/serialization

Is there a way to register some progress monitor on JAXB Marshaller and Unmarshaller?
I would like to show some progress information in my GUI while data is de-/serialized.
I see that you can set a Unmarshaller.Listener and Marshaller.Listener, which have a "before" and "after" method. Nevertheless, I do not see any straight forward way to get the total number of elements to serialize.
I would need that obviously to calculate some "percentage done" info.
Is it ok to parse before unmarshalling?
If so, assuming you have a list of objects, you could do something like...
final String tagName = *** name of tag you are counting ***;
InputStream in = *** stream of your xml ***;
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
final AtomicInteger counter = new AtomicInteger();
saxParser.parse(in, new DefaultHandler() {
#Override
public void startElement (String uri, String localName, String qName, Attributes attributes) {
if (localName.equals(tagName))
counter.incrementAndGet();
}
});
Would doing a more low-level approach by leveraging on the InputStream be an acceptable solution?
E.g.
import java.io.IOException;
import java.io.InputStream;
import java.util.function.DoubleConsumer;
public class InputStreamWithProgressDecorator extends InputStream {
/** Input stream to be decorated */ private final InputStream inputStream;
/** Amount of byte read */ private long position = 0L;
/** File size */ private final long length;
/** Mark */ private int mark = 0;
/** Consumer of the progress */ private final DoubleConsumer callBack;
public InputStreamWithProgressDecorator(final InputStream is, final long l, final DoubleConsumer cb) {
inputStream = is;
length = l;
callBack = cb;
}
private void setPosition(final long fp) {
position = fp;
callBack.accept(getProgress());
}
public double getProgress() {
return length == 0L ? 100d : ((double) position) * 100d / ((double) length);
}
public long getPosition() {
return position;
}
#Override
public int read(byte[] b) throws IOException {
final int rc = inputStream.read(b);
setPosition(position + rc);
return rc;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
final int rc = inputStream.read(b, off, len);
setPosition(position + rc);
return rc;
}
#Override
public byte[] readAllBytes() throws IOException {
final byte[] result = inputStream.readAllBytes();
setPosition(position + result.length);
return result;
}
#Override
public byte[] readNBytes(int len) throws IOException {
final byte[] result = inputStream.readNBytes(len);
setPosition(position + result.length);
return result;
}
#Override
public int readNBytes(byte[] b, int off, int len) throws IOException {
final int rc = inputStream.readNBytes(b, off, len);
setPosition(position + rc);
return rc;
}
#Override
public long skip(long n) throws IOException {
final long rc = inputStream.skip(n);
setPosition(position + rc);
return rc;
}
#Override
public int available() throws IOException {
return inputStream.available();
}
#Override
public void close() throws IOException {
inputStream.close();
}
#Override
public synchronized void mark(int readlimit) {
inputStream.mark(readlimit);
mark = readlimit;
}
#Override
public synchronized void reset() throws IOException {
inputStream.reset();
setPosition(mark);
}
#Override
public boolean markSupported() {
return inputStream.markSupported();
}
#Override
public int read() throws IOException {
final int c = inputStream.read();
setPosition(position + 1);
return c;
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.function.DoubleConsumer;
public class Demo1 {
public static void main(String[] args) throws IOException {
final File file = new File(args[0]);
final DoubleConsumer callBack = p -> System.out.printf("%.0f%%\n", p);
try (final FileInputStream fis = new FileInputStream(file); final InputStreamWithProgressDecorator is = new InputStreamWithProgressDecorator(fis, file.length(), callBack)) {
// Simulating JAXB unmarshaller reads
byte[] buffer = is.readNBytes(1024);
while (buffer.length != 0) buffer = is.readNBytes(1024);
}
}
}
Or if you have a FileInputStream with a separate Thread approach :
public class FileInputStreamReadProgressThread extends Thread implements UncaughtExceptionHandler {
/** Input stream */ private final FileInputStream fileInputStream;
/** File size */ private final long length;
/** Read progress in percents */ private double progress = 0d;
/** Exception from thread */ private Throwable exception = null;
/** Consumer of the progress */ private final DoubleConsumer callBack;
public FileInputStreamReadProgressThread(final FileInputStream fis, final long l, final DoubleConsumer cb) {
fileInputStream = fis;
length = l;
callBack = cb;
setUncaughtExceptionHandler(this);
setName(getClass().getSimpleName());
}
public double getProgress() { return progress; }
public Throwable getException() { return exception; }
#Override public void uncaughtException(final Thread t, final Throwable e) { exception = e; }
#Override
public void run() {
try {
long position = -1L;
final FileChannel channel = fileInputStream.getChannel();
while (!isInterrupted() && channel.isOpen() && position < length) {
position = channel.position();
progress = length == 0L ? 100d : ((double)position) * 100d / ((double)length);
callBack.accept(progress);
sleep(100L);
}
} catch (final IOException e) {
exception = e;
} catch (final InterruptedException e) {
// Do nothing
}
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.util.function.DoubleConsumer;
public class Demo2 {
public static void main(String[] args) throws IOException {
final File file = new File(args[0]);
final DoubleConsumer callBack = p -> System.out.printf("%.0f%%\n", p);
try (final FileInputStream fis = new FileInputStream(file); final InputStream is = Channels.newInputStream(fis.getChannel())) {
final FileInputStreamReadProgressThread readProgressThread = new FileInputStreamReadProgressThread(fis, file.length(), callBack);
readProgressThread.start();
// Simulating JAXB unmarshaller reads
is.readAllBytes();
}
}
}

Logging all network traffic in Spring mvc

I have spring mvc application using RequestBody and ResponseBody annotations. They are configured with MappingJackson2HttpMessageConverter. I also have slf4j set up. I would like to log all json as it comes in and out from my controller.
I did extend
MappingJackson2HttpMessageConverter
#Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
logStream(inputMessage.getBody());
return super.read(type, contextClass, inputMessage);
}
I can get the input stream, but if I read the content it becomes empty and I loose the message. Moreover mark() and reset() is not supported. It is implemented by PushbackInputStream, so I tried to read it's content and push it back like this:
public void logStream(InputStream is) {
if (is instanceof PushbackInputStream)
try {
PushbackInputStream pushbackInputStream = (PushbackInputStream) is;
byte[] bytes = new byte[20000];
StringBuilder sb = new StringBuilder(is.available());
int red = is.read();
int pos =0;
while (red > -1) {
bytes[pos] = (byte) red;
pos=1 + pos;
red = is.read();
}
pushbackInputStream.unread(bytes,0, pos-1);
log.info("Json payload " + sb.toString());
} catch (Exception e) {
log.error("ignoring exception in logger ", e);
}
}
but I get exception
java.io.IOException: Push back buffer is full
I also tried to turn on logging on http level as described here:Spring RestTemplate - how to enable full debugging/logging of requests/responses? without luck.
After more than whole work day of experimenting I got working solution.
It consists of Logging filter, two wrappers for request and response and registration of Logging filter:
the filter class is:
/**
* Http logging filter, which wraps around request and response in
* each http call and logs
* whole request and response bodies. It is enabled by
* putting this instance into filter chain
* by overriding getServletFilters() in
* AbstractAnnotationConfigDispatcherServletInitializer.
*/
public class LoggingFilter extends AbstractRequestLoggingFilter {
private static final Logger log = LoggerFactory.getLogger(LoggingFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
long id = System.currentTimeMillis();
RequestLoggingWrapper requestLoggingWrapper = new RequestLoggingWrapper(id, request);
ResponseLoggingWrapper responseLoggingWrapper = new ResponseLoggingWrapper(id, response);
log.debug(id + ": http request " + request.getRequestURI());
super.doFilterInternal(requestLoggingWrapper, responseLoggingWrapper, filterChain);
log.debug(id + ": http response " + response.getStatus() + " finished in " + (System.currentTimeMillis() - id) + "ms");
}
#Override
protected void beforeRequest(HttpServletRequest request, String message) {
}
#Override
protected void afterRequest(HttpServletRequest request, String message) {
}
}
this class is using stream wrappers, which was suggested by
Master Slave and David Ehrmann.
Request wrapper looks like this:
/**
* Request logging wrapper using proxy split stream to extract request body
*/
public class RequestLoggingWrapper extends HttpServletRequestWrapper {
private static final Logger log = LoggerFactory.getLogger(RequestLoggingWrapper.class);
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
private long id;
/**
* #param requestId and id which gets logged to output file. It's used to bind request with
* response
* #param request request from which we want to extract post data
*/
public RequestLoggingWrapper(Long requestId, HttpServletRequest request) {
super(request);
this.id = requestId;
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ServletInputStream servletInputStream = RequestLoggingWrapper.super.getInputStream();
return new ServletInputStream() {
private TeeInputStream tee = new TeeInputStream(servletInputStream, bos);
#Override
public int read() throws IOException {
return tee.read();
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
return tee.read(b, off, len);
}
#Override
public int read(byte[] b) throws IOException {
return tee.read(b);
}
#Override
public boolean isFinished() {
return servletInputStream.isFinished();
}
#Override
public boolean isReady() {
return servletInputStream.isReady();
}
#Override
public void setReadListener(ReadListener readListener) {
servletInputStream.setReadListener(readListener);
}
#Override
public void close() throws IOException {
super.close();
// do the logging
logRequest();
}
};
}
public void logRequest() {
log.info(getId() + ": http request " + new String(toByteArray()));
}
public byte[] toByteArray() {
return bos.toByteArray();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and response wrapper is different only in close/flush method (close doesn't get called)
public class ResponseLoggingWrapper extends HttpServletResponseWrapper {
private static final Logger log = LoggerFactory.getLogger(ResponseLoggingWrapper.class);
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
private long id;
/**
* #param requestId and id which gets logged to output file. It's used to bind response with
* response (they will have same id, currenttimemilis is used)
* #param response response from which we want to extract stream data
*/
public ResponseLoggingWrapper(Long requestId, HttpServletResponse response) {
super(response);
this.id = requestId;
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
final ServletOutputStream servletOutputStream = ResponseLoggingWrapper.super.getOutputStream();
return new ServletOutputStream() {
private TeeOutputStream tee = new TeeOutputStream(servletOutputStream, bos);
#Override
public void write(byte[] b) throws IOException {
tee.write(b);
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
tee.write(b, off, len);
}
#Override
public void flush() throws IOException {
tee.flush();
logRequest();
}
#Override
public void write(int b) throws IOException {
tee.write(b);
}
#Override
public boolean isReady() {
return servletOutputStream.isReady();
}
#Override
public void setWriteListener(WriteListener writeListener) {
servletOutputStream.setWriteListener(writeListener);
}
#Override
public void close() throws IOException {
super.close();
// do the logging
logRequest();
}
};
}
public void logRequest() {
byte[] toLog = toByteArray();
if (toLog != null && toLog.length > 0)
log.info(getId() + ": http response " + new String(toLog));
}
/**
* this method will clear the buffer, so
*
* #return captured bytes from stream
*/
public byte[] toByteArray() {
byte[] ret = bos.toByteArray();
bos.reset();
return ret;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
at last LoggingFilter needs to be registered in AbstractAnnotationConfigDispatcherServletInitializer like this:
#Override
protected Filter[] getServletFilters() {
LoggingFilter requestLoggingFilter = new LoggingFilter();
return new Filter[]{requestLoggingFilter};
}
I know, there is maven lib for this, but I don't want to include whole lib because of small logging utility. It was much harder than I originally thought. I expected to achieve this just by modifying log4j.properties. I still think this should be part of Spring.
It sounds like you want to decorate HttpInputMessage so it returns a decorated InputStream that logs all reads in an internal buffer, then on close() or finalize() logs what was read.
Here's an InputStream that will capture what was read:
public class LoggingInputStream extends FilterInputStream {
private ByteArrayOutputStream out = new ByteArrayOutputStream();
private boolean logged = false;
protected LoggingInputStream(InputStream in) {
super(in);
}
#Override
protected void finalize() throws Throwable {
try {
this.log();
} finally {
super.finalize();
}
}
#Override
public void close() throws IOException {
try {
this.log();
} finally {
super.close();
}
}
#Override
public int read() throws IOException {
int r = super.read();
if (r >= 0) {
out.write(r);
}
return r;
}
#Override
public int read(byte[] b) throws IOException {
int read = super.read(b);
if (read > 0) {
out.write(b, 0, read);
}
return read;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
int read = super.read(b, off, len);
if (read > 0) {
out.write(b, off, read);
}
return read;
}
#Override
public long skip(long n) throws IOException {
long skipped = 0;
byte[] b = new byte[4096];
int read;
while ((read = this.read(b, 0, (int)Math.min(n, b.length))) >= 0) {
skipped += read;
n -= read;
}
return skipped;
}
private void log() {
if (!logged) {
logged = true;
try {
log.info("Json payload " + new String(out.toByteArray(), "UTF-8");
} catch (UnsupportedEncodingException e) { }
}
}
}
And now
#Override
public Object read(Type type, Class<?> contextClass, final HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return super.read(type, contextClass, new HttpInputMessage() {
#Override
public InputStream getBody() {
return new LoggingInputStream(inputMessage.getBody());
}
#Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
});
}
Decorating HttpInputMessage as David Ehrmann suggested is one likely solution.
The whole trouble with this functionality is that it requires InputStream to be read more than once. However, this is not possible, once you read a portion or a stream, its "consumed" and there no way to go back and read it again.
A typical solution is to apply a filter that will create a wrapper for a request that will allow re-reads of the inputStream. One approach is by using the TeeInputStream which copies all the bytes read from InputStream to a secondary OutputStream.
There's a github project that uses just that kind of a filter and in fact just for the same purpose spring-mvc-logger The RequestWrapper class used
public class RequestWrapper extends HttpServletRequestWrapper {
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
private long id;
public RequestWrapper(Long requestId, HttpServletRequest request) {
super(request);
this.id = requestId;
}
#Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
private TeeInputStream tee = new TeeInputStream(RequestWrapper.super.getInputStream(), bos);
#Override
public int read() throws IOException {
return tee.read();
}
};
}
public byte[] toByteArray(){
return bos.toByteArray();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
A similar implementation wraps the response as well

Count the bytes written to file via BufferedWriter formed by GZIPOutputStream

I have a BufferedWriter as shown below:
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new GZIPOutputStream( hdfs.create(filepath, true ))));
String line = "text";
writer.write(line);
I want to find out the bytes written to the file with out querying file like
hdfs = FileSystem.get( new URI( "hdfs://localhost:8020" ), configuration );
filepath = new Path("path");
hdfs.getFileStatus(filepath).getLen();
as it will add overhead and I don't want that.
Also I cant do this:
line.getBytes().length;
As it give size before compression.
You can use the CountingOutputStream from Apache commons IO library.
Place it between the GZIPOutputStream and the file Outputstream (hdfs.create(..)).
After writing the content to the file you can read the number of written bytes from the CountingOutputStream instance.
If this isn't too late and you are using 1.7+ and you don't wan't to pull in an entire library like Guava or Commons-IO, you can just extend the GZIPOutputStream and obtain the data from the associated Deflater like so:
public class MyGZIPOutputStream extends GZIPOutputStream {
public MyGZIPOutputStream(OutputStream out) throws IOException {
super(out);
}
public long getBytesRead() {
return def.getBytesRead();
}
public long getBytesWritten() {
return def.getBytesWritten();
}
public void setLevel(int level) {
def.setLevel(level);
}
}
You can make you own descendant of OutputStream and count how many time write method was invoked
This is similar to the response by Olaseni, but I moved the counting into the BufferedOutputStream rather than the GZIPOutputStream, and this is more robust, since def.getBytesRead() in Olaseni's answer is not available after the stream has been closed.
With the implementation below, you can supply your own AtomicLong to the constructor so that you can assign the CountingBufferedOutputStream in a try-with-resources block, but still retrieve the count after the block has exited (i.e. after the file is closed).
public static class CountingBufferedOutputStream extends BufferedOutputStream {
private final AtomicLong bytesWritten;
public CountingBufferedOutputStream(OutputStream out) throws IOException {
super(out);
this.bytesWritten = new AtomicLong();
}
public CountingBufferedOutputStream(OutputStream out, int bufSize) throws IOException {
super(out, bufSize);
this.bytesWritten = new AtomicLong();
}
public CountingBufferedOutputStream(OutputStream out, int bufSize, AtomicLong bytesWritten)
throws IOException {
super(out, bufSize);
this.bytesWritten = bytesWritten;
}
#Override
public void write(byte[] b) throws IOException {
super.write(b);
bytesWritten.addAndGet(b.length);
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
bytesWritten.addAndGet(len);
}
#Override
public synchronized void write(int b) throws IOException {
super.write(b);
bytesWritten.incrementAndGet();
}
public long getBytesWritten() {
return bytesWritten.get();
}
}

Logging InputStream

I create an InputStream class, that extends CiphetInputStream. I want to log all data from my InputStream (that i use as input in parser further) so i done following:
public class MyInputStream extends CipherInputStream {
private OutputStream logStream = new ByteArrayOutputStream();
.....
#Override
public int read() throws IOException {
int read = super.read();
logStream.write(read);
return read;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
int read = super.read(b, off, len);
if (read > 0) {
logStream.write(b, off, read);
}
return read;
}
#Override
public int read(byte[] buffer) throws IOException {
int read = super.read(buffer);
if (read()>0) {
logStream.write(buffer);
}
return read;
}
#Override
public void close() throws IOException {
log();
super.close();
}
public void log() {
String logStr = new String(((ByteArrayOutputStream) logStream).toByteArray(), Charset.defaultCharset());
Log.d(getClass(), logStr);
try {
logStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In actual my stream has something like this:
<response>
<result>0</result>
</response>
but log show smth like this mutation :
<<response>
<resultt >0</resullt>
</respoonse>
[and (?) symbol at the end]
Thanks for any help!
You can combine TeeInputStream and Logger.stream():
new TeeInputStream(
yourStream,
Logger.stream(Level.INFO, this)
);
If you want to see log in logcat, try Log.i(String tag, String message); or System.out.println("");. Both of them works. You can also use, Log.d, Log.w and Log.e also.

Record size of objects as they're being serialized?

What's the best way to record the size of certain objects as they are being serialized? For example, once objects of type A, B, C are serialized, record the size of their serialized bytes. We can get the size of the entire object graph via getBytes, but we'd like to break it down as to what are the largest contributors to the overall serialized size.
ObjectOutputStream offers writeObjectOverride, but we don't want to rewrite the serialization process. In simplified terms, we need to be aware of when we encounter a certain object prior to serialization, record the total current byte count, and then after it's serialized, take the difference of byte counts. It seems like encompassing writeSerialData would work, but the method is private.
Ideas?
Thanks.
--- UPDATE ---
The answers/suggestions below are insightful. Below is what I have so far. Let me know your thoughts. Thanks.
// extend to get a handle on outputstream
MyObjectOutputStream extends ObjectOutputStream {
private OutputStream out;
public MyObjectOutputStream(out) {
super(out);
this.out = out;
}
public OutputStream getOut() {
return this.out;
}
}
// counter
public static class CounterOutputStream extends FilterOutputStream {
private int bytesWritten = 0;
...
public int getBytesWritten() {
return this.bytesWritten;
}
public void resetCounter() {
bytesWritten = 0;
}
private void update(int len) {
bytesWritten += len;
}
}
// go serialize
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new MyObjectOutputStream(new CounterOutputStream(out, 1024));
// record serialized size of this class; do this for every interested class
public class MyInterestingObject {
...
private void writeObject(ObjectOutputStream out) throws IOException {
CounterOutputStream counter = null;
if (out instanceof MyObjectOutputStream) {
counter = (CounterOutputStream)((MyObjectOutputStream)out).getOut();
counter.resetCounter();
}
// continue w/ standard serialization of this object
out.defaultWriteObject();
if (counter != null) {
logger.info(this.getClass() + " bytes written: " + counter.getBytesWritten());
// TODO: store in context or somewhere to be aggregated post-serialization
}
}
}
The simplest solution would be to wrap the OutputStream you're using with an implementation that will count bytes written.
import java.io.IOException;
import java.io.OutputStream;
public class CountingOutputStream extends OutputStream {
private int count;
private OutputStream out;
public CountingOutputStream(OutputStream out) {
this.out = out;
}
public void write(byte[] b) throws IOException {
out.write(b);
count += b.length;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
count += len;
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
public void write(int b) throws IOException {
out.write(b);
count++;
}
public int getBytesWritten() {
return count;
}
}
Then you would just use that
CountingOutputStream s = new CountingOutputStream(out);
ObjectOutputStream o = new ObjectOutputStream(s);
o.write(new Object());
o.close();
// s.getBytesWritten()
You could implement Externalizable rather than Serializable on any objects you need to capture such data from. You could then implement field-by-field byte counting in the writeExternal method, maybe by handing off to a utility class. Something like
public void writeExternal(ObjectOutput out) throws IOException
{
super.writeExternal(out);
out.writeUTF(this.myString == null ? "" : this.myString);
ByteCounter.getInstance().log("MyClass", "myString", this.myString);
}
Another hackish way would be to stick with Serializable, but to use the readResolve or writeReplace hooks to capture whatever data you need, e.g.
public class Test implements Serializable
{
private String s;
public Test(String s)
{
this.s = s;
}
private Object readResolve()
{
System.err.format("%s,%s,%s,%d\n", "readResolve", "Test", "s", s.length());
return this;
}
private Object writeReplace()
{
System.err.format("%s,%s,%s,%d\n", "writeReplace", "Test", "s", s.length());
return this;
}
public static void main(String[] args) throws Exception
{
File tmp = File.createTempFile("foo", "tmp");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(tmp));
Test test = new Test("hello world");
out.writeObject(test);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(tmp));
test = (Test)in.readObject();
in.close();
}
}

Categories

Resources