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);
}
}
}
}