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