diff --git a/src/main/java/org/jboss/invocation/ChainedInterceptor.java b/src/main/java/org/jboss/invocation/ChainedInterceptor.java index 31402f8..a0775ff 100644 --- a/src/main/java/org/jboss/invocation/ChainedInterceptor.java +++ b/src/main/java/org/jboss/invocation/ChainedInterceptor.java @@ -23,8 +23,9 @@ package org.jboss.invocation; import java.io.Serializable; - -import javax.interceptor.InvocationContext; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; import static org.jboss.invocation.InvocationMessages.msg; @@ -38,7 +39,7 @@ class ChainedInterceptor implements Interceptor, Serializable { private static final long serialVersionUID = 7951017996430287249L; - private final Interceptor[] interceptors; + private final List interceptors; /** * Construct a new instance. @@ -49,26 +50,17 @@ class ChainedInterceptor implements Interceptor, Serializable { if (interceptors == null) { throw msg.nullParameter("interceptors"); } - this.interceptors = interceptors; + this.interceptors = Arrays.asList(interceptors); } /** {@inheritDoc} */ - public Object processInvocation(final InvocationContext context) throws Exception { - final InvocationContext childContext = new DelegatingInvocationContext(context) { - private int index = 0; - - public Object proceed() throws Exception { - if (index < interceptors.length) { - try { - return interceptors[index++].processInvocation(this); - } finally { - index--; - } - } else { - return super.proceed(); - } - } - }; - return childContext.proceed(); + public Object processInvocation(final InterceptorContext context) throws Exception { + final ListIterator old = context.getInterceptorIterator(); + context.setInterceptorIterator(new ConcatenatedIterator(interceptors.listIterator(), old)); + try { + return context.proceed(); + } finally { + context.setInterceptorIterator(old); + } } } diff --git a/src/main/java/org/jboss/invocation/ConcatenatedIterator.java b/src/main/java/org/jboss/invocation/ConcatenatedIterator.java new file mode 100644 index 0000000..19e3767 --- /dev/null +++ b/src/main/java/org/jboss/invocation/ConcatenatedIterator.java @@ -0,0 +1,77 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.invocation; + +import java.util.ListIterator; + +/** + * A concatenated iterator. + * + * @param the element type + * @author David M. Lloyd + */ +class ConcatenatedIterator implements ListIterator { + private final ListIterator first; + private final ListIterator second; + + ConcatenatedIterator(final ListIterator first, final ListIterator second) { + this.first = first; + this.second = second; + } + + public boolean hasNext() { + return first.hasNext() || second.hasNext(); + } + + public T next() { + return first.hasNext() ? first.next() : second.next(); + } + + public boolean hasPrevious() { + return second.hasPrevious() || first.hasPrevious(); + } + + public T previous() { + return second.hasPrevious() ? second.previous() : first.previous(); + } + + public int nextIndex() { + throw new UnsupportedOperationException(); + } + + public int previousIndex() { + throw new UnsupportedOperationException(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(final T t) { + throw new UnsupportedOperationException(); + } + + public void add(final T t) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/jboss/invocation/ContextClassLoaderInterceptor.java b/src/main/java/org/jboss/invocation/ContextClassLoaderInterceptor.java index 77eb883..1571b09 100644 --- a/src/main/java/org/jboss/invocation/ContextClassLoaderInterceptor.java +++ b/src/main/java/org/jboss/invocation/ContextClassLoaderInterceptor.java @@ -28,8 +28,6 @@ import java.security.AccessController; import java.security.PrivilegedAction; -import javax.interceptor.InvocationContext; - /** * An interceptor which sets the thread context class loader for the duration of an invocation. *

@@ -58,7 +56,7 @@ public ContextClassLoaderInterceptor(final ClassLoader classLoader) { } /** {@inheritDoc} */ - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { final Thread thread = Thread.currentThread(); final ClassLoader old; final SecurityManager sm = System.getSecurityManager(); diff --git a/src/main/java/org/jboss/invocation/InVMRemoteInterceptor.java b/src/main/java/org/jboss/invocation/InVMRemoteInterceptor.java index 0fc4c9d..f657dc0 100644 --- a/src/main/java/org/jboss/invocation/InVMRemoteInterceptor.java +++ b/src/main/java/org/jboss/invocation/InVMRemoteInterceptor.java @@ -22,7 +22,6 @@ package org.jboss.invocation; -import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import org.jboss.marshalling.cloner.ClassLoaderClassCloner; @@ -31,8 +30,6 @@ import org.jboss.marshalling.cloner.ObjectClonerFactory; import org.jboss.marshalling.cloner.ObjectCloners; -import javax.interceptor.InvocationContext; - /** * An invocation processor which passes the invocation (possibly by value) to a target class loader. Invocations will be * cloned to the target class loader; replies will be cloned to the caller's thread context class loader. The target @@ -49,99 +46,41 @@ public ClassLoader run() { } }; - private final Interceptor targetInterceptor; - private final Method targetMethod; - private final ClassLoaderClassCloner classCloner; - private final PassMode passMode; - private final Object targetInstance; private final ClonerConfiguration configuration; /** * Construct a new instance. * - * @param targetInterceptor the target interceptor - * @param targetMethod the target method * @param targetClassLoader the target class loader - * @param passMode the parameter pass mode - * @param targetInstance the target invocation instance + * */ - public InVMRemoteInterceptor(final Interceptor targetInterceptor, final Method targetMethod, final ClassLoader targetClassLoader, final PassMode passMode, final Object targetInstance) { - this.targetInterceptor = targetInterceptor; - this.targetMethod = targetMethod; - this.passMode = passMode; - this.targetInstance = targetInstance; + public InVMRemoteInterceptor(final ClassLoader targetClassLoader) { configuration = new ClonerConfiguration(); - configuration.setClassCloner(classCloner = new ClassLoaderClassCloner(targetClassLoader)); + configuration.setClassCloner(new ClassLoaderClassCloner(targetClassLoader)); } /** {@inheritDoc} */ - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { final Object[] parameters = context.getParameters(); final ObjectClonerFactory clonerFactory = ObjectCloners.getSerializingObjectClonerFactory(); final ObjectCloner cloner = clonerFactory.createCloner(configuration); final Object[] newParameters; - switch (passMode) { - case REFERENCE_ONLY: { - newParameters = parameters; - break; - } - case SAME_CLASS_LOADER: { - newParameters = parameters.clone(); - final int len = newParameters.length; - for (int i = 0; i < len; i++) { - final Object param = parameters[i]; - if (param != null) { - final Class origClass = param.getClass(); - final Class newClass = classCloner.clone(origClass); - if (newClass != origClass) { - newParameters[i] = cloner.clone(param); - } - } - } - break; - } - case VALUE_ONLY: { - final int len = parameters.length; - newParameters = new Object[len]; - for (int i = 0; i < len; i++) { - newParameters[i] = cloner.clone(parameters[i]); - } - break; - } - default: { - // not reachable - throw new IllegalStateException(); - } + final int len = parameters.length; + newParameters = new Object[len]; + for (int i = 0; i < len; i++) { + newParameters[i] = cloner.clone(parameters[i]); } - final InvocationContext newContext = new SimpleInvocationContext(targetInstance, targetMethod, newParameters, context.getContextData(), null); - final Object result = targetInterceptor.processInvocation(newContext); - switch (passMode) { - case REFERENCE_ONLY: { - return result; - } - case SAME_CLASS_LOADER: { - if (result == null) { - return null; - } - final ClassLoaderClassCloner classCloner = new ClassLoaderClassCloner(getContextClassLoader()); - final Class classClone = classCloner.clone(result.getClass()); - if (classClone == result.getClass()) { - return result; - } - // fall through - } - case VALUE_ONLY: { - if (result == null) { - return null; - } - final ClonerConfiguration copyBackConfiguration = new ClonerConfiguration(); - copyBackConfiguration.setClassCloner(new ClassLoaderClassCloner(getContextClassLoader())); - return clonerFactory.createCloner(copyBackConfiguration).clone(result); - } - default: { - // not reachable - throw new IllegalStateException(); + context.setParameters(newParameters); + try { + final Object result = context.proceed(); + if (result == null) { + return null; } + final ClonerConfiguration copyBackConfiguration = new ClonerConfiguration(); + copyBackConfiguration.setClassCloner(new ClassLoaderClassCloner(getContextClassLoader())); + return clonerFactory.createCloner(copyBackConfiguration).clone(result); + } finally { + context.setParameters(parameters); } } @@ -153,22 +92,4 @@ private ClassLoader getContextClassLoader() { return Thread.currentThread().getContextClassLoader(); } } - - /** - * The mode to use for parameter and result passing to the target. - */ - public enum PassMode { - /** - * Pass all items by reference always. - */ - REFERENCE_ONLY, - /** - * Pass items by reference which are of the same type and class loader. - */ - SAME_CLASS_LOADER, - /** - * Pass all items by value always. - */ - VALUE_ONLY, - } } diff --git a/src/main/java/org/jboss/invocation/Interceptor.java b/src/main/java/org/jboss/invocation/Interceptor.java index d2d6c43..f4ea77c 100644 --- a/src/main/java/org/jboss/invocation/Interceptor.java +++ b/src/main/java/org/jboss/invocation/Interceptor.java @@ -22,8 +22,6 @@ package org.jboss.invocation; -import javax.interceptor.InvocationContext; - /** * A processor for invocations. May perform some action, including but not limited to handling the invocation, before * or in lieu of passing it on to the dispatcher or another processor. @@ -35,11 +33,11 @@ public interface Interceptor { /** * Process an invocation. The invocation can be handled directly, or passed on to the next processor in the - * chain via {@code context}. + * chain. * - * @param context the invocation context + * @param context the interceptor context * @return the result of the invocation * @throws Exception If the underlying invocation resulted in some exception */ - Object processInvocation(InvocationContext context) throws Exception; + Object processInvocation(InterceptorContext context) throws Exception; } diff --git a/src/main/java/org/jboss/invocation/InterceptorContext.java b/src/main/java/org/jboss/invocation/InterceptorContext.java new file mode 100644 index 0000000..7198d4a --- /dev/null +++ b/src/main/java/org/jboss/invocation/InterceptorContext.java @@ -0,0 +1,248 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.invocation; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.ListIterator; +import java.util.Map; + +import javax.interceptor.InvocationContext; + +import static org.jboss.invocation.InvocationMessages.msg; + +/** + * An interceptor/invocation context object. + * + * @author David M. Lloyd + */ +public final class InterceptorContext { + private static final ListIterator EMPTY = Collections.emptyList().listIterator(); + + private Object target; + private Method method; + private Object[] parameters; + private Map contextData; + private Object timer; + private ListIterator interceptorIterator = EMPTY; + private final Map, Object> privateData = new IdentityHashMap, Object>(); + private final InvocationContext invocationContext = new Invocation(); + + /** + * Get the invocation target which is reported to the interceptor invocation context. + * + * @return the invocation target + */ + public Object getTarget() { + return target; + } + + /** + * Set the invocation target which is reported to the interceptor invocation context. + * + * @param target the invocation target + */ + public void setTarget(final Object target) { + this.target = target; + } + + /** + * Get the invoked method which is reported to the interceptor invocation context. + * + * @return the method + */ + public Method getMethod() { + return method; + } + + /** + * Set the invoked method which is reported to the interceptor invocation context. + * + * @param method the method + */ + public void setMethod(final Method method) { + this.method = method; + } + + /** + * Get the method parameters which are reported to the interceptor invocation context. + * + * @return the method parameters + */ + public Object[] getParameters() { + return parameters; + } + + /** + * Set the method parameters which are reported to the interceptor invocation context. + * + * @param parameters the method parameters + */ + public void setParameters(final Object[] parameters) { + this.parameters = parameters; + } + + /** + * Get the context data which is reported to the interceptor invocation context. + * + * @return the context data + */ + public Map getContextData() { + return contextData; + } + + /** + * Set the context data which is reported to the interceptor invocation context. + * + * @param contextData the context data + */ + public void setContextData(final Map contextData) { + this.contextData = contextData; + } + + /** + * Get the timer object which is reported to the interceptor invocation context. + * + * @return the timer object + */ + public Object getTimer() { + return timer; + } + + /** + * Set the timer object which is reported to the interceptor invocation context. + * + * @param timer the timer object + */ + public void setTimer(final Object timer) { + this.timer = timer; + } + + /** + * Get the invocation context. + * + * @return the invocation context + */ + public InvocationContext getInvocationContext() { + return invocationContext; + } + + /** + * Get a private data item. + * + * @param type the data type class object + * @param the data type + * @return the data item or {@code null} if no such item exists + */ + public T getPrivateData(Class type) { + return type.cast(privateData.get(type)); + } + + /** + * Insert a private data item. + * + * @param type the data type class object + * @param value the data item value, or {@code null} to remove the mapping + * @param the data type + * @return the data item which was previously mapped to this position, or {@code null} if no such item exists + */ + public T putPrivateData(Class type, T value) { + if (value == null) { + return type.cast(privateData.remove(type)); + } else { + return type.cast(privateData.put(type, type.cast(value))); + } + } + + /** + * Get the current interceptor iterator; guaranteed to be non-{@code null}. + * + * @return the interceptor iterator + */ + public ListIterator getInterceptorIterator() { + return interceptorIterator; + } + + /** + * Set the interceptor iterator. + * + * @param interceptorIterator the interceptor iterator + */ + public void setInterceptorIterator(final ListIterator interceptorIterator) { + if (interceptorIterator == null) { + throw new IllegalArgumentException("interceptorIterator is null"); + } + this.interceptorIterator = interceptorIterator; + } + + /** + * Pass the invocation on to the next step in the chain. + * + * @return the result + * @throws Exception if an invocation throws an exception + */ + public Object proceed() throws Exception { + final ListIterator iterator = interceptorIterator; + if (iterator.hasNext()) { + Interceptor next = iterator.next(); + try { + return next.processInvocation(this); + } finally { + if (iterator.hasPrevious()) iterator.previous(); + } + } else { + throw msg.cannotProceed(); + } + } + + private class Invocation implements InvocationContext { + public Object getTarget() { + return target; + } + + public Method getMethod() { + return method; + } + + public Object[] getParameters() { + return parameters; + } + + public void setParameters(final Object[] params) { + parameters = params; + } + + public Map getContextData() { + return contextData; + } + + public Object getTimer() { + return timer; + } + + public Object proceed() throws Exception { + return InterceptorContext.this.proceed(); + } + } +} diff --git a/src/main/java/org/jboss/invocation/InterceptorInvocationHandler.java b/src/main/java/org/jboss/invocation/InterceptorInvocationHandler.java index 001a8eb..b6feda9 100644 --- a/src/main/java/org/jboss/invocation/InterceptorInvocationHandler.java +++ b/src/main/java/org/jboss/invocation/InterceptorInvocationHandler.java @@ -27,8 +27,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import javax.interceptor.InvocationContext; - /** * A {@link Proxy} {@code InvocationHandler} which delegates invocations to an {@code Interceptor}. * @@ -64,19 +62,10 @@ public InterceptorInvocationHandler(final Interceptor interceptor) { * @throws Throwable the exception to thrown from the method invocation on the proxy instance, if any */ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { - return interceptor.processInvocation(createInvocationContext(proxy, method, args)); - } - - /** - * Get the invocation context to use. - * - * @param proxy the proxy object - * @param method the invoked method - * @param args the method parameters - * @return the invocation context - */ - protected InvocationContext createInvocationContext(final Object proxy, final Method method, final Object[] args) { - return new SimpleInvocationContext(proxy, method, args, null); + InterceptorContext context = new InterceptorContext(); + context.setParameters(args); + context.setMethod(method); + return interceptor.processInvocation(context); } /** {@inheritDoc} */ diff --git a/src/main/java/org/jboss/invocation/InvokingInterceptor.java b/src/main/java/org/jboss/invocation/InvokingInterceptor.java index f14de89..271f9bd 100644 --- a/src/main/java/org/jboss/invocation/InvokingInterceptor.java +++ b/src/main/java/org/jboss/invocation/InvokingInterceptor.java @@ -26,8 +26,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import javax.interceptor.InvocationContext; - /** * @author David M. Lloyd */ @@ -38,7 +36,7 @@ class InvokingInterceptor implements Interceptor, Serializable { private static final long serialVersionUID = 175221411434392097L; - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { final Method method = context.getMethod(); if (method == null) { return null; diff --git a/src/main/java/org/jboss/invocation/MethodInterceptor.java b/src/main/java/org/jboss/invocation/MethodInterceptor.java index 4de95f9..56f8671 100644 --- a/src/main/java/org/jboss/invocation/MethodInterceptor.java +++ b/src/main/java/org/jboss/invocation/MethodInterceptor.java @@ -61,9 +61,9 @@ public MethodInterceptor(final Object interceptorInstance, final Method method) } /** {@inheritDoc} */ - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { try { - return method.invoke(interceptorInstance, context); + return method.invoke(interceptorInstance, context.getInvocationContext()); } catch (IllegalAccessException e) { final IllegalAccessError n = new IllegalAccessError(e.getMessage()); n.setStackTrace(e.getStackTrace()); diff --git a/src/main/java/org/jboss/invocation/MethodInvokingInterceptor.java b/src/main/java/org/jboss/invocation/MethodInvokingInterceptor.java index 1decf8f..229f642 100644 --- a/src/main/java/org/jboss/invocation/MethodInvokingInterceptor.java +++ b/src/main/java/org/jboss/invocation/MethodInvokingInterceptor.java @@ -24,8 +24,6 @@ import java.lang.reflect.Method; -import javax.interceptor.InvocationContext; - /** * An interceptor which always invokes one specific method on one specific object given the parameters from * the invocation context. @@ -48,7 +46,7 @@ public MethodInvokingInterceptor(final Object target, final Method method) { } /** {@inheritDoc} */ - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { return method.invoke(target, (Object[]) context.getParameters()); } } diff --git a/src/main/java/org/jboss/invocation/NullInterceptor.java b/src/main/java/org/jboss/invocation/NullInterceptor.java index d564955..e949e0f 100644 --- a/src/main/java/org/jboss/invocation/NullInterceptor.java +++ b/src/main/java/org/jboss/invocation/NullInterceptor.java @@ -24,8 +24,6 @@ import java.io.Serializable; -import javax.interceptor.InvocationContext; - /** * @author David M. Lloyd */ @@ -36,7 +34,7 @@ class NullInterceptor implements Interceptor, Serializable { private static final long serialVersionUID = -2792151547173027051L; - public Object processInvocation(final InvocationContext context) throws Exception { + public Object processInvocation(final InterceptorContext context) throws Exception { return context.proceed(); }