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


      

Reply via email to