I'm experiencing problems with my Restlet Resource. It uses TrueZip to build a subset of a Zip Archive and allows the user to download it.
// Create a Streaming Response Entity.
final StreamingOutput stream = new StreamingOutput() {
@Override
public void write(final OutputStream output) {
ZipBrowser.extract(source, path, output);
}
};
LOGGER.debug("Download of Path {} with the length {} initiated", path, length);
ResponseBuilder rb = Response.ok(stream);
rb.header(HeaderConstants.HEADER_CONTENT_DISPOSITION, CONDISPOVALUE + fileName);
rb.header(HeaderConstants.HEADER_CONTENT_LENGTH, length);
return rb.build();
Even though it works I receive an annoying StreamClosedException. This error appears only when I try to download a subset and not the whole Zip Archive:
An exception occured writing the responseentity
sun.net.httpserver.StreamClosedException
at sun.net.httpserver.ChunkedOutputStream.flush(ChunkedOutputStream.java:156)
at sun.net.httpserver.PlaceholderOutputStream.flush(ExchangeImpl.java:449)
at org.restlet.engine.adapter.ServerCall.writeResponseBody(ServerCall.java:511)
at org.restlet.engine.adapter.ServerCall.sendResponse(ServerCall.java:454)
at org.restlet.engine.adapter.ServerAdapter.commit(ServerAdapter.java:187)
at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:144)
at org.restlet.engine.connector.HttpServerHelper$1.handle(HttpServerHelper.java:64)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)Unable to send error response
java.io.IOException: headers already sent
at sun.net.httpserver.ExchangeImpl.sendResponseHeaders(ExchangeImpl.java:204)
at sun.net.httpserver.HttpExchangeImpl.sendResponseHeaders(HttpExchangeImpl.java:86)
at org.restlet.engine.connector.HttpExchangeCall.writeResponseHead(HttpExchangeCall.java:148)
at org.restlet.engine.adapter.ServerCall.sendResponse(ServerCall.java:450)
at org.restlet.engine.adapter.ServerAdapter.commit(ServerAdapter.java:205)
at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:144)
at org.restlet.engine.connector.HttpServerHelper$1.handle(HttpServerHelper.java:64)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80)
at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:677)
at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77)
at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:649)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
This is caused by both the zip library and the Restlet Framework trying to close the OutputStream
once they have finished.
I have experienced this in the past with other libraries and have managed to eliminate the exception by wrapping the OutputStream
I pass to the zip library in a class which Overrides close()
to do nothing. With all other methods delegated. This then allows Restlet to close the stream.
So that the line calling the Zip utility in your code becomes:
ZipBrowser.extract(source, path, new WrappedOutputStream(output));
Where the WrappedOutputStream
class is indicated below (delegating methods will need to be added).
import java.io.IOException;
import java.io.OutputStream;
public class WrappedOutputStream extends OutputStream {
private final OutputStream delegate;
public WrappedOutputStream(final OutputStream delegate) {
this.delegate = delegate;
}
public void close() throws IOException {
// Do Nothing to allow Restlet to close the underlying stream
}
// TODO Delegate other Outpt Stream methods.
}