Mark Thomas wrote: > John Bollinger wrote: >> See attached sample code for a class that would support this behavior. > > Your attachment didn't make it through. Could you post it in-line?
Sure: /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package example; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * <p> * A service class that assists in managing a "context" within an application or * service. This version provides for creating dynamic proxies around objects * belonging to a particular context to ensure that those objects' methods are * invoked in that context. * </p><p> * Instances may be created and held on a one-per-context basis, but currently * there is no harm in having multiple managers for the same context. * Alternatively, {...@code ContextManager}s are lightweight, so they may be created * at need and released freely. Model usage:<br/> * <tt><pre> * ContextManager contextManager = ContextManager.forCurrentContext(); * ... * void passRequestToForeignContext(RequestInterface request, ForeignContext context) { * context.handleRequest( * contextManager.createProxy(request, RequestInterface.class) * ); * } * </pre></tt> * </p> * * @author John C. Bollinger */ public class ContextManager { /** * The context ClassLoader for the context managed by this ContextManager */ private final ClassLoader contextClassLoader; /** * Initializes a new ContextManager managing the context defined by the * specified context {...@code ClassLoader} * * @param contextClassLoader a {...@code ClassLoader} representing the context * to be managed by this {...@code ContextManager} */ public ContextManager(ClassLoader contextClassLoader) { this.contextClassLoader = contextClassLoader; } /** * Creates and returns a {...@code ContextManger} for the current context * * @return a {...@code ContextManger} for the current context */ public static ContextManager forCurrentContext() { return new ContextManager(Thread.currentThread().getContextClassLoader()); } /** * Creates a dynamic proxy object wrapping the provided object and ensuring * that all methods of the wrapped object (when invoked via the proxy) see * the appropriate context ClassLoader for the context managed by this * {...@code ContextManager}. The proxy's class will implement the specified * interface(s) by delegating every method invocation to the wrapped object, * setting the context ClassLoader before, and resetting it after. * * @param <T> an interface type that the proxy object will implement * @param object the object to be wrapped by the * @param iface a Class representing one of the interfaces the proxy's class * must implement, and defining the formal type of the return value and * the {...@code object} argument * @param additionalIfaces {...@code Class}es representing additional interfaces * the proxy's class must implement * * @return a proxy object wrapping the specified delegate object, and whose * class implements all the specified interfaces */ public <T> T createProxy(T object, Class<T> iface, Class<?>... additionalIfaces) { Class<?>[] interfaces; /* * Could test to see whether the interfaces (iface and additionalIfaces) * are in fact interfaces (as opposed to normal classes). Haven't tested * what happens if they are non-interface classes, but probably Proxy * will fail to create the proxy object. * * Could also test whether they are null. */ if (additionalIfaces != null) { /* * Set up the full list of interface classes the proxy will implement. * iface comes first, then any additionalIfaces elements, in order. */ /* * Could test the additional interfaces here to make sure the * submitted object can successfully be cast to each type. If the * object's class does not implement one of the interfaces then * runtime failures will occur when those interfaces' methods are * invoked on the proxy. */ interfaces = new Class[1 + additionalIfaces.length]; interfaces[0] = iface; System.arraycopy(additionalIfaces, 0, interfaces, 1, additionalIfaces.length); } else { interfaces = new Class[] {iface}; } return iface.cast(Proxy.newProxyInstance(contextClassLoader, interfaces, new ContextManagingInvocationHandler(object, contextClassLoader))); } /** * An InvocationHandler wrapping a specified object and serving to present a * given context ClassLoader to that object on all method invocations. * * @author John C. Bollinger */ private static class ContextManagingInvocationHandler implements InvocationHandler { /* * This class doesn't really need to be nested. This version is nested * mainly for packaging purposes. */ /** * The ClassLoader to set as the current context ClassLoader when * delegating method invocations to the {...@link #wrappedObject} */ private final ClassLoader contextClassLoader; /** * The object to which all method invocations are delegated */ private final Object wrappedObject; /** * Initializes a new ContextManagingInvocationHandler that delegates * method invocations to the specified object while ensuring that the * current context ClassLoader is the specified one within the scope of * the invocation. * * @param wrappedObject the {...@code Object} to which method invocations * should be delegated * @param contextClassLoader the {...@code ClassLoader} that should be the * current context {...@code ClassLoader} within the scope of method * invocations on the wrapped object */ public ContextManagingInvocationHandler(Object wrappedObject, ClassLoader contextClassLoader) { this.contextClassLoader = contextClassLoader; this.wrappedObject = wrappedObject; } /** * {...@inheritdoc}. This implementation sets the context ClassLoader to the * configured one, delegates to the wrapped object, then restores the * context ClassLoader to its value at method entry. * * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(@SuppressWarnings("unused") Object proxy, Method method, Object[] args) throws Throwable { Thread currentThread = Thread.currentThread(); ClassLoader currentContextClassLoader = currentThread.getContextClassLoader(); try { currentThread.setContextClassLoader(contextClassLoader); return method.invoke(wrappedObject, args); } finally { currentThread.setContextClassLoader(currentContextClassLoader); } } } }