Errorhandling with Interceptor. Advice needed.
lcoetzee May 11, 2006 10:11 AMHi all,
I have been strugling quite some time to handle errors gracefully (these errors can include stuff like null pointers or entity not found). I have kept an eye out for good ways to handle errors but as yet have not found a good way ( I am aware of the current EJB3 rollback bugs: e.g. http://www.jboss.com/index.html?module=bb&op=viewtopic&t=82614)
It is also something that we have discussed a bit on this forum: e.g.http://www.jboss.com/index.html?module=bb&op=viewtopic&t=76085
as well as more recently.
The following is the scheme I have tried but unfortunately it is not working as expected.
1. I have created my own exception(note the rollback=false):
. . @ApplicationException(rollback=false) public class NAPException extends Exception implements Serializable { /** * */ private static final long serialVersionUID = -5617117494284445988L; public NAPException() { super(); } public NAPException(String message) { super(message); } }
2. I have made my business methods throw the above exception when catching another exception. In addition I have implemented an interceptor to handle this thrown exception.
@IfNAPException(goToOutcome = "doErrorPage") public String handleLoadAllServices() throws NAPException { try { //do something here that can throw checked or unchecked exceptions //e.g not finding entity or get a NPE } catch (Exception e) throw new NAPException(e); }
3. My annotation (annotate the method as well as class)
@Target( { ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Interceptors(IfNAPExceptionInterceptor.class) public @interface IfNAPException { /** * The JSF outcome if the NAPException was thrown and handled */ String goToOutcome() default ""; }
4. The interceptor
@Around( { BijectionInterceptor.class, ValidationInterceptor.class, OutcomeInterceptor.class, BusinessProcessInterceptor.class }) @Within(RemoveInterceptor.class) public class IfNAPExceptionInterceptor { private UserTransaction userTransaction; @In(create = true, required = true) private transient FacesMessages facesMessages; static final Logger logger = Logger .getLogger(IfNAPExceptionInterceptor.class); @AroundInvoke public Object customInterceptor(InvocationContext ctx) throws Exception { // logger.info("*** BEFORE INTERCEPTION ***"); Object object = null; try { object = ctx.proceed(); } catch (NAPException e) { if (shouldRollback()) { logger.error("Got NAPException in customInterceptor "); // Only handle the NAP exception if we have the annotation on // the method if (ctx.getMethod().isAnnotationPresent(IfNAPException.class)) { logger.error("Method has annotation. Will be handled"); IfNAPException annotation = ctx.getMethod().getAnnotation( IfNAPException.class); object = annotation.goToOutcome(); logger.info("Returning outcome: " + object); rollbackAfterException(); redirect(annotation.goToOutcome(), e.getClass().toString() + " " + e.getMessage()); } } } // logger.info("*** AFTER INTERCEPTION ***"); return object; } private void rollbackAfterException() { try { if (Transactions.isTransactionActiveOrMarkedRollback()) { int status = Transactions.getUserTransaction().getStatus(); if (status == Status.STATUS_MARKED_ROLLBACK) { logger.info("killing transaction"); Transactions.getUserTransaction().rollback(); } } } catch (Exception te) { logger.error("could not roll back transaction", te); } } private void redirect(String outcome, String errorMessage) { FacesContext facesContext = FacesContext.getCurrentInstance(); facesContext.addMessage(null, new FacesMessage("An error occured: " + errorMessage)); // add the error message facesContext.getApplication().getNavigationHandler().handleNavigation( facesContext, null, outcome); facesContext.responseComplete(); } private boolean shouldRollback() throws Exception, NamingException { // int status = Transactions.getUserTransaction().getStatus(); // return (status == Status.STATUS_MARKED_ROLLBACK); return Transactions.isTransactionActiveOrMarkedRollback(); }
I have three main problems:
1. I cannot redirect to my outcome (as defined in my faces-config.xml) which points to an error page. I always end up at the page where I was.
2. For some weird reason I cannot stop the interception process. The original method (throwing the exception )gets re-invoked .
3. I havent got a clue as to where I should put this interceptor in the stack (Around/Within).
Anyway, any suggestions as how to do error handling would be appreciated.
Thanks
Louis