/* * Copyright 2011 Blazebit */ package com.blazebit.cdi.exception; import com.blazebit.annotation.AnnotationUtils; import com.blazebit.cdi.exception.annotation.ExceptionWrap; import com.blazebit.cdi.exception.annotation.ExceptionWrapping; import com.blazebit.exception.ExceptionUtils; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; /** * This interceptor simply wraps exceptions which are declared as sources in the * ExceptionWrap annotations into the exception type "wrapper" which is also * declared in the ExceptionWrap annotation. It ignores exceptions which are * declared in the throws clause of the method for which this interceptor runs. * * <pre> * <code> * public class Bean implements Serializable { * * @ExceptionWrapping(value = { * @ExceptionWrap(source = RuntimeExceptionA.class, wrapper = MyExceptionA.class), * @ExceptionWrap(source = RuntimeExceptionB.class, wrapper = MyExceptionB.class), * @ExceptionWrap(wrapper = MyExceptionC.class) }) * public void example() throws MyExceptionA, MyExceptionB, MyExceptionC { * // code * } * } * </code> * </pre> * * RuntimeExceptionA will be wrapped into MyExceptionA RuntimeExceptionB will be * wrapped into MyExceptionB Every other Exception, that is not instance of * MyExceptionA, MyExceptionB or MyExceptionC, will be wrapped into MyExceptionC * * The order of ExceptionWrap annotations within the ExceptionWrapping * annotation is important and should be declared from specific to general. * * If you would specify <code>@ExceptionWrap( wrapper=MyExceptionC.class)</code> * as the first element of the ExceptionWrapping annotation, then every * exception that is not declared in the throws clause will be wrapped into a * MyExceptionC! * * @since 0.1.2 * @author Christian Beikov * @see ExceptionWrapping * @see ExceptionWrap * */ @Interceptor @ExceptionWrapping(value = {}) public class ExceptionWrappingInterceptor implements Serializable { private static final long serialVersionUID = 1L; @AroundInvoke public Object errorLogging(InvocationContext ic) throws Exception { /* Retrieve the logger for the intercepted class */ Logger log = Logger.getLogger(ic.getTarget().getClass().getName()); Method m = ic.getMethod(); Object targetObject = ic.getTarget(); Class<?> targetClass = targetObject == null ? m.getDeclaringClass() : targetObject.getClass(); /* * Retrieve the exceptions declared in the throws clause of the * intercepted method */ Class<?>[] declaredExceptions = m.getExceptionTypes(); ExceptionWrapping wrappingAnnotation = AnnotationUtils.findAnnotation( m, targetClass, ExceptionWrapping.class); ExceptionWrap[] wraps = null; Object ret; boolean doWrapping = true; if (wrappingAnnotation == null) { throw new IllegalStateException( "The interceptor annotation can not be determined!"); } wraps = wrappingAnnotation.value(); /* Only try to do wrapping when exceptionWrap elements are available */ doWrapping = wraps != null && wraps.length > 0; try { ret = ic.proceed(); } catch (Throwable t) { /* * Check if the throwable is an instance of * InvocationTargetException and if so, unwrap the cause. OWB did * not unwrap exceptions that have been thrown in decorators in some * versions so we need to do this to be able to handle the right * exception */ Throwable t1 = ExceptionUtils.unwrap(t, InvocationTargetException.class); if (doWrapping) { /* * Check if the exception that was thrown is declared in the * throws clause and if so, don't apply exception wrapping */ for (Class<?> declaredException : declaredExceptions) { if (declaredException.isInstance(t1)) { /* Do this so no exception wrapper is applied */ doWrapping = false; break; } } if (doWrapping) { /* * If a wrapping should be applied, iterate through * exception wraps and check if the thrown exception is an * instance of a declared source class. */ for (ExceptionWrap wrap : wraps) { Class<? extends Throwable>[] sourceClasses = wrap .sources(); Class<? extends Exception> wrapperClass = wrap .wrapper(); Exception e = null; /* * Only do wrapping if the exception isn't already a * instance of the wrapper exceptionF */ if (!wrapperClass.isInstance(t1)) { for (Class<? extends Throwable> source : sourceClasses) { /* * When the thrown exception is instance of an * exception wrap source class, create a new * exception instance with the thrown exception * as cause */ if (source.isInstance(t1)) { try { e = wrapperClass.getConstructor( Throwable.class) .newInstance(t1); } catch (Throwable t2) { /* * Declared wrapper exception has no * constructor with a throwable as param */ Exception ex1 = null; /* * Cast to or wrap the throwable into a * new exception because we may not * throw Throwable instances within here */ if (t2 instanceof Exception) { ex1 = (Exception) t2; } else { ex1 = new Exception(t2); } log.log(Level.WARNING, "The applied wrapper exception on the method " + m.getName() + " has no constructor for type Throwable!", ex1); } if (e != null) { throw e; } } } } } } } /* * No exception wrapping was applied, so cast to or wrap the * throwable into a new exception and throw it */ if (t1 instanceof Exception) { throw (Exception) t1; } throw new Exception(t1); } return ret; } }