/* * Copyright 2011 Blazebit */ package com.blazebit.cdi.cleanup; import com.blazebit.annotation.AnnotationUtils; import com.blazebit.cdi.cleanup.annotation.Cleanup; import com.blazebit.cdi.cleanup.annotation.CleanupHandler; import com.blazebit.cdi.cleanup.annotation.CleanupHandling; import com.blazebit.exception.ExceptionUtils; import com.blazebit.reflection.ReflectionUtils; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; /** * Invokes cleanup methods after the invocation of a method. The specified * cleanup with the cleanup name declared in CleanupHandling is used for the * declared exception. If CleanupHandling does not specify a cleanup name, the * cleanup name of the CleanupHandler is used. Cleanup methods of * CleanupHandling elements that have the flag always set to true, are always * invoked. * * @author Christian Beikov * @since 0.1.2 * @see CleanupHandler * @see CleanupHandling * @see Cleanup */ @Interceptor @CleanupHandler(cleanup = Object.class) public class CleanupHandlerInterceptor implements Serializable { private static final long serialVersionUID = 2640134717545764135L; @AroundInvoke public Object cleanup(InvocationContext ic) throws Exception { Object ret = null; Method m = ic.getMethod(); Object targetObject = ic.getTarget(); Class<?> targetClass = targetObject == null ? m.getDeclaringClass() : targetObject.getClass(); CleanupHandler cleanupHandlerAnnotation = AnnotationUtils .findAnnotation(m, targetClass, CleanupHandler.class); if (cleanupHandlerAnnotation == null) { throw new IllegalStateException( "The interceptor annotation can not be determined!"); } // Avoid CleanupHandling for methods annotated with Cleanup // This could end up in an endless loop if (ic.getMethod().isAnnotationPresent(Cleanup.class)) { return ic.proceed(); } try { ret = ic.proceed(); } catch (Throwable t) { // Unwrap Exception if t is instanceof InvocationTargetException Throwable t1 = ExceptionUtils.unwrap(t, InvocationTargetException.class); handleCleanups(targetObject, cleanupHandlerAnnotation, t1); if (t1 instanceof Exception) { throw (Exception) t1; } else { throw new Exception(t1); } } handleCleanups(targetObject, cleanupHandlerAnnotation, null); return ret; } private void handleCleanups(Object target, CleanupHandler handler, Throwable t) throws Exception { CleanupHandling[] handlings = handler.value(); if (handlings.length > 0) { // Invoke cleanup handling cleanups for (CleanupHandling handling : handlings) { if (handling.always()) { doInvokeCleanup(target, handler, handling, t); } else if (t != null && t.getClass().getName().equals(handling.exception().getName())) { doInvokeCleanup(target, handler, handling, t); } } } else { // Invoke Cleanup handler cleanup doInvokeCleanup(target, handler, null, t); } } private void doInvokeCleanup(Object target, CleanupHandler handler, CleanupHandling handling, Throwable exception) throws Exception { // Invoke handler cleanup only if handling cleanup can not be found if (handling == null || !invokeCleanups(target, handling.cleanup(), exception)) { invokeCleanups(target, handler.cleanup(), exception); } } private boolean invokeCleanups(Object target, Class<?> cleanupClazz, Throwable exception) throws Exception { if (cleanupClazz != null) { // Christian Beikov 29.07.2013: Traverse whole hierarchy // instead of retrieving the annotation directly from // the class object. List<Method> methods = ReflectionUtils.getMethods(target.getClass(), Cleanup.class); Method m = null; for (Method candidate : methods) { Cleanup c = AnnotationUtils.findAnnotation(candidate, Cleanup.class); if (cleanupClazz.equals(c.value())) { m = candidate; break; } } if(m != null) { final Class<?>[] parameterTypes = m.getParameterTypes(); if (parameterTypes.length == 1) { // Need to check for null exception if(exception != null) { // Check if exception type fits formal parameter type if (!ReflectionUtils.isSubtype(exception.getClass(), parameterTypes[0])) { throw new IllegalArgumentException("Cleanup method with name " + cleanupClazz.getName() + " requires a parameter that is not a subtype of the exception class " + exception.getClass().getName()); } } // Invoked either with set or null exception m.invoke(target, exception); } else { m.invoke(target); } return true; } } else { return false; } throw new IllegalArgumentException("Cleanup method with name '" + cleanupClazz.getName() + "' not found in " + target.getClass().getName()); } }