package org.jboss.weld.interceptor.proxy; import static org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.create; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.jboss.weld.bean.proxy.InterceptionDecorationContext; import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack; import org.jboss.weld.bean.proxy.StackAwareMethodHandler; import org.jboss.weld.interceptor.spi.model.InterceptionType; import org.jboss.weld.interceptor.util.InterceptionUtils; import org.jboss.weld.util.reflection.Reflections; /** * @author Marius Bogoevici * @author Marko Luksa * @author Jozef Hartinger */ public class InterceptorMethodHandler implements StackAwareMethodHandler, Serializable { public static final String INTERCEPTOR_BINDINGS_KEY = "org.jboss.weld.interceptor.bindings"; private static final long serialVersionUID = 1L; private final InterceptionContext ctx; private final transient ConcurrentMap<Method, CachedInterceptionChain> cachedChains; public InterceptorMethodHandler(InterceptionContext ctx) { this.ctx = ctx; this.cachedChains = new ConcurrentHashMap<Method, CachedInterceptionChain>(); } @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { return invoke(InterceptionDecorationContext.getStack(), self, thisMethod, proceed, args); } public Object invoke(Stack stack, Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { SecurityActions.ensureAccessible(proceed); if (proceed == null) { if (thisMethod.getName().equals(InterceptionUtils.POST_CONSTRUCT)) { return executeInterception(self, null, null, null, InterceptionType.POST_CONSTRUCT, stack); } else if (thisMethod.getName().equals(InterceptionUtils.PRE_DESTROY)) { return executeInterception(self, null, null, null, InterceptionType.PRE_DESTROY, stack); } } else { if (isInterceptorMethod(thisMethod)) { return Reflections.invokeAndUnwrap(self, proceed, args); } return executeInterception(self, thisMethod, proceed, args, InterceptionType.AROUND_INVOKE, stack); } return null; } protected Object executeInterception(Object instance, Method method, Method proceed, Object[] args, InterceptionType interceptionType, Stack stack) throws Throwable { CachedInterceptionChain chain = getInterceptionChain(instance, method, interceptionType); if (chain.interceptorMethods.isEmpty()) { // shortcut if there are no interceptors if (proceed == null) { return null; } else { return Reflections.invokeAndUnwrap(instance, proceed, args); } } if (InterceptionType.AROUND_INVOKE == interceptionType) { return executeAroundInvoke(instance, method, proceed, args, chain, stack); } else { return executeLifecycleInterception(instance, method, proceed, args, chain, stack); } } protected Object executeLifecycleInterception(Object instance, Method method, Method proceed, Object[] args, CachedInterceptionChain chain, Stack stack) throws Throwable { return new WeldInvocationContextImpl(instance, method, proceed, args, chain.interceptorMethods, chain.interceptorBindings, stack).proceed(); } protected Object executeAroundInvoke(Object instance, Method method, Method proceed, Object[] args, CachedInterceptionChain chain, Stack stack) throws Throwable { org.jboss.weld.interceptor.WeldInvocationContext ctx = create(instance, method, proceed, args, chain.interceptorMethods, chain.interceptorBindings, stack); try { return chain.interceptorMethods.get(0).invoke(ctx); } catch (InvocationTargetException e) { throw e.getCause(); } } private CachedInterceptionChain getInterceptionChain(Object instance, Method method, InterceptionType interceptionType) { if (method != null) { CachedInterceptionChain cachedChain = cachedChains.get(method); if (cachedChain == null) { cachedChain = new CachedInterceptionChain(ctx.buildInterceptorMethodInvocations(instance, method, interceptionType), ctx.getInterceptionModel() .getMemberInterceptorBindings(method)); CachedInterceptionChain old = cachedChains.putIfAbsent(method, cachedChain); if (old != null) { cachedChain = old; } } return cachedChain; } return new CachedInterceptionChain(ctx.buildInterceptorMethodInvocations(instance, null, interceptionType), ctx.getInterceptionModel().getClassInterceptorBindings()); } private boolean isInterceptorMethod(Method method) { return ctx.getInterceptionModel().getTargetClassInterceptorMetadata().isInterceptorMethod(method); } private Object readResolve() throws ObjectStreamException { return new InterceptorMethodHandler(ctx); } private static class CachedInterceptionChain { private final List<InterceptorMethodInvocation> interceptorMethods; private final Set<Annotation> interceptorBindings; public CachedInterceptionChain(List<InterceptorMethodInvocation> chain, Set<Annotation> interceptorBindings) { this.interceptorMethods = chain; this.interceptorBindings = interceptorBindings; } } }