Wrong catch block is reach
Hi,
I encounter a strange behaviour in exception handling.
My web app can generate pdf files, and download them. If client closes its browser window, a ClientAbortException is thrown (which is expected behaviour, I guess.)
However, I can't catch it correctly. Here is my code:
try{
// code for generating PDF and downloading it through response output stream.
}
catch(ClientAbortException caEx){
// No specific error handling required
logger.info("Request aborted by client ["+caEx.getClass()+"] ["+caEx.getMessage()+"]", caEx);
return"PDF_ERROR";
}
catch(Exception ex){
// A problem occured (TODO notify administrator)
logger.info("PDF generation failed ["+ex.getClass()+"] ["+ex.getMessage()+"]", ex);
return"PDF_ERROR";
}
The problem is that even in case of ClientAbortException, the exception is caught by the second catch block, and log is:
PDF generation failed [class org.apache.catalina.connector.ClientAbortException] [null]
As you can see, Class name is the same. I tried debugging by adding the following logs in the second catch block:logger.info("instanceof ? "+ (exinstanceof ClientAbortException));
logger.info("class ? "+ (ex.getClass() == ClientAbortException.class));
But both loggedfalse.
Could someone explain why it can happen?
1) The fully qualified name of ClientAbortException from your class is indeed org.apache.catalina.connector.ClientAbortException. i.e., you are not importing some other ClientAbortException (that lives in a different package). You may want to print out exact class name by ClientAbortException.class.getName() or change the catch block as
catch (org.apache.catalina.connector.ClientAbortException caEx)
just to be sure.
2) If you get the situation even after verifying the fully qualified class name, then you may want to check whether these are loaded by the same loader or not. You may add the following lines:
logger.info("name equals? "+ (ex.getClass().getName().equals(ClientAbortException.class.getName());
logger.info("same loader ? "+ (ex.getClass().getClassLoader() == ClientAbortException.class.getClassLoader()));
if you get true and false respectively, then the what is seen is correct -- a class uniquely identified by fully qualified class name and the class loader that loaded it.
Ok, exception class was not loaded by the same loader...
I'm using Tomcat.
Actually, the class is in catalina.jar, and this jar is present in two directories:
TOMCAT_ROOT/server/lib/
and
TOMCAT_ROOT/webapps/myApp/WEB-INF/lib/
I guess the first one is by default, but it can't be accessed by applications. (I guess that's why I copied it in the second directory.)
Is there any way to settle this issue?
Looks like my server startup fails if I move the jar (e.g. in TOMCAT_ROOT/common/lib/ )
Are you copying catalina.jar just for accessing this exception? If so, you can do the following.
1) remove catalina.jar from your app "lib" directory
2) check for ClientAbortException using name
try {
// .... code here....
} catch (Exception ex) {
String name = ex.getClass().getName();
if (name.equals("org.apache.catalina.connector.ClientAbortException") {
// generate log entry for client abortion
} else {
// generate log entry for any other exception
}
}
Having said that, it appears that Tomcat designers don't want application developers to access classes in catalina.jar. That is why they have moved this jar file under server/lib. If so, you probably hava to avoid accessing/depending on these classes...
> check for ClientAbortException using name
This might be a last chance option.
> it appears that Tomcat designers don't want application developers to access classes in catalina.jar
Yes, it looks like they don't. However, I find the ClientAbortException information quite interesting, as the finest precision I can obtain otherwise is IOException (superclass of ClientAbortException), which I find a bit vague.
In fact, my goal was to ignore ClientAbortExceptions, and notify administrator in case of other exceptions (including IOExceptions.)
Thank you for your support.