I\'m using Jersey 2.13 in my web application for retrieving data async. There are some cases where requests take some time (i.E. when executing complex reports) until their
I worked around this issue by adding a low priority WriterInterceptor
that detects and ignores exceptions thrown while writing responses. If you're running on Tomcat and don't mind a dependency on Tomcat classes, you could use org.apache.catalina.connector.ClientAbortException
rather calling setOutputStream
, which would remove the need for the two nested classes (and the dependency on org.apache.commons.io.output.ProxyOutputStream
, which could easily also be avoided with a custom OutputStream
subclass instead).
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import org.apache.commons.io.output.ProxyOutputStream;
/**
* Ignore exceptions when writing a response, which almost always means the
* client disconnected before reading the full response.
*/
@Provider
@Priority(1)
public class ClientAbortExceptionWriterInterceptor implements WriterInterceptor {
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException {
context.setOutputStream(new ClientAbortExceptionOutputStream(context.getOutputStream()));
try {
context.proceed();
} catch (Throwable t) {
for (Throwable cause = t; cause != null; cause = cause.getCause()) {
if (cause instanceof ClientAbortException) {
return;
}
}
throw t;
}
}
private static class ClientAbortExceptionOutputStream extends ProxyOutputStream {
public ClientAbortExceptionOutputStream(OutputStream out) {
super(out);
}
@Override
protected void handleIOException(IOException e) throws IOException {
throw new ClientAbortException(e);
}
}
@SuppressWarnings("serial")
private static class ClientAbortException extends IOException {
public ClientAbortException(IOException e) {
super(e);
}
}
}
After digging into the Jersey Coding I found out the the only way to archive this is by disabling the Jersey internal Logger. This can be done in the Class that extends ResourceConfig.
@ApplicationPath("api")
public class Application extends ResourceConfig {
private final static Logger ORG_GLASSFISH_JERSEY_LOGGER = Logger
.getLogger("org.glassfish.jersey");
static {
ORG_GLASSFISH_JERSEY_LOGGER.setLevel(Level.OFF);
}
}
Thanks @Chip, it also works putting the code directly in WS.
@Path("/myWS")
public class MyWS {
private final static Logger ORG_GLASSFISH_JERSEY_LOGGER = Logger.getLogger("org.glassfish.jersey");
static {
ORG_GLASSFISH_JERSEY_LOGGER.setLevel(Level.OFF);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/version")
public String version() {
return "1.0.25";
}
}
I've encountered this as well and finally found a guide to "solve" this.
https://tutorial-academy.com/jersey-workaround-clientabortexception-ioexception/
There are two options where the first is the one that is currently the accepted answer. The other, preferred way, is to add a WriterInterceptor to drop the ClientAbortException. My personal twist is to WARN log this occurrence instead.
In case the URL is unreachable I add my implementation here. Don't forget to to register it in your Jersey context.
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Priority;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.IOException;
@Provider
@Priority(1)
public class ClientAbortExceptionWriterInterceptor implements WriterInterceptor {
private static final Logger logger = LoggerFactory.getLogger(ClientAbortExceptionWriterInterceptor.class);
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException {
try {
context.proceed();
} catch (Throwable t) {
for (Throwable cause = t; cause != null; cause = cause.getCause()) {
if (cause instanceof ClientAbortException) {
logger.warn("Client aborted request.", cause);
return;
}
}
throw t;
}
}
}