Instead of writing more comments (deleted excess), I'll put some of my thoughts here + one kind of generic solution I came up with.
http://docs.oracle.com/javaee/6/tutorial/doc/bnbpj.html
This puts this matter in simple context. When people are not used to exceptions (like script/functional programmers) they rant about using only RuntimeExceptions ... which takes you in polluting the frontend with un-managed errors bubling on UI. (I do not find that very convenient if you consider the program code in long run ... maintainability)
http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html
If the client can take some alternate action to recover from the exception, make it a checked exception. If the client cannot do anything useful, then make the exception unchecked. By useful, I mean taking steps to recover from the exception and not just logging the exception.
(There are posts about logging patterns with Exceptions, but I am not going there in this as it trails off)
It is hard to detect unchecked exceptions and they easily get pass your "close to source" catching. (When they are unchecked, they will be invisible for the client code and will slip through).
I am challenged with this issue with some legacy code throwing lots of unchecked exceptions that crash the UI now. (because of unreliable services)
Probably "everyone" has their own opinion how to do things correctly, but I am trying to have a generic pattern for catching the errors (unchecked Exceptions) close to UI and presenting them in nice error popups (JSF by the way) instead of directing the user to "error.xhtml" page (introduced in web.xml).
// For the above scenario I came up with this solution.
It even seems to work pretty well.
- Create an interceptor and Interceptor binding annotation.
- Annotate the classes or methods you want to have their bubbling EJBExceptions sanitized.
- Don't go intercepting everything or you get unnecessary processing/overhead. Just use it where you actually need it on BackingBean/ManagedBean level.
Interceptor binding annotation
@Inherited
@InterceptorBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptEJBExceptions {}
The interceptor
@Interceptor
@InterceptEJBExceptions
public class EjbExceptionInterceptor {
@AroundInvoke
public Object interceptBubblingExceptions(final InvocationContext context) throws Exception {
try {
return context.proceed();
} catch (MySpecificEJBException msejbe) {
Object[] args = { msejbe.getErrorCode(), msejbe.getMethodSignature(), msejbe.getParameters()};
// This would create a FacesMessage in JSF
addErrorMessageToFacesContext(null, summary_template, details_template, args);
} catch (EJBException ejbe) {
super.addErrorMessageToFacesContext(null, "Unspecified EJBException", ejbe.getMessage());
}
// "Pure" RuntimeExceptions will fall through and will be shown on 'error-page' specified in web.xml
return null;
}
}
beans.xml
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>xxx.xxx.xxx.exception.interceptor.EjbExceptionInterceptor</class>
</interceptors>
</beans>
Usage in java classes
@RequestScoped // Class must be in EE context
// @InterceptEJBExceptions // Have the annotation at class level to intercept all method calls in this class
public class MyBB {
@InterceptEJBExceptions // Only intercept when this method gets called
public String myEjbExceptionThrowingActionListener(Object obj) {
// Some code that may throw an EJBException
}
}
MySpecificEJBException Would be an Exception class extending EJBException. Construct the class as you like to transport usefull information for later logging and for showing a clean error messages on a screen instead of vomitting the exception on screen with or without error-page defined in web.xml (which you should also have as a fallback).
Do not forget to include the original Exception in the class!
Create more your own Exceptions extending EJBException as you need and throw them as you like (as RuntimeExceptions they are unchecked). Then just add the catch block in the interceptor and do the logging and/or error message displaying specific for that case.
In general:
Use checked Exceptions where you can to react for the error.
If you have lots of checked Exceptions, you can/should maybe group them in categories (extend some more generic Exception of yours). You can then catch based on that grouping exception (in addition to/instead of having grouping catch-clauses).
- Turn those Exceptions into EJB/RuntimeExceptions that pollute you code (if there really is nothing you can do close by). These should be then captured and handled by the generic EjbExceptionInterceptor. The interceptor will catch the exception => show a clean error message/popup, but the user will remain on the same page without losing the data he/she was working on at.
- Avoid throwing plain RuntimeException. These signal a bug in code (my opinion) and the above interceptor will let them fall through on the error-page. This will also move the user away from the page he/she is currently on at.
- These kind of errors should be always high priority bugs to fix (e.g. NullPointerException would do this).
- Also: Do not throw Exceptions just about everything, that will be bad programming. If your code can do something about the error before hand (e.g. use overloaded methods or default values to prevent from using null parameters), then do it, do not leak the problem to the client (calling method). That is just lazy programming + using RuntimeExceptions to hide those errors is just irresponsible! Do unit tests!
I could not find any actual solution for error handling (as pattern), but a lots of debate what is right and what is wrong. We have 2 types of Throwables, so why not use them both constructively?
Instead of continuing this debate, I am hoping this will be a real solution and do the trick.
I'd appreciate all constructive comments and improvement suggestions.