View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.api.util;
17  
18  import org.kuali.rice.core.api.util.ClassLoaderUtils;
19  import org.kuali.rice.core.api.util.reflect.BaseTargetedInvocationHandler;
20  
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.lang.reflect.Proxy;
24  
25  /**
26   * A Proxy that sets the thread Context ClassLoader before invocation of the
27   * proxied object, and resets it back afterwards.
28   *
29   * @author Kuali Rice Team (rice.collab@kuali.org)
30   */
31  public class ContextClassLoaderProxy extends BaseTargetedInvocationHandler<Object> {
32  
33      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ContextClassLoaderProxy.class);
34  
35      /**
36       * Convenience method that wraps a specified object with a ContextClassLoaderProxy, with a specified
37       * handler classloader and proxy classloader.  If the specified object is null, or the object classloader
38       * equals the proxy classloader, the object is returned unwrapped.
39       * @param proxiedObject the object to proxy
40       * @param proxyClassLoader the classloader OF THE PROXY INSTANCE
41       * @param objectClassLoader the classloader to set as the context classloader prior to any invocations on the proxiedObject
42       * @return a ContextClassLoaderProxy Proxy for the proxiedObject
43       */
44      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy, ClassLoader proxyClassLoader, ClassLoader objectClassLoader) {
45          if (proxiedObject == null) {
46          	return null;
47          }
48      	if (proxyClassLoader == null) {
49              //proxyClassLoader = Thread.currentThread().getContextClassLoader();
50          	proxyClassLoader = proxiedObject.getClass().getClassLoader();
51          }
52          if (objectClassLoader == null) {
53              objectClassLoader = proxiedObject.getClass().getClassLoader();
54          }
55          if (classesToProxy == null) {
56              classesToProxy = getInterfacesToProxy(proxyClassLoader, proxiedObject);
57          }
58          // this classloader comparison looks fishy
59          // it is testing the classloader of the proxy against the intended *context* classloader of the proxiedObject
60          // if these are the same the proxiedObject is not wrapped.  However, that implies that if the proxy
61          // classloader may equal the intended context class loader, that the context class loader is actually not set,
62          // and could theoretically NOT be the intended one in the future
63          // somebody who understands this better than me should investigate this, and this method which I have
64          // now applied to all prior uses of ContextClassLoaderProxy as a convenience
65          //if (proxiedObject != null) { //&& !objectClassLoader.equals(proxyClassLoader)) {
66          ContextClassLoaderProxy handler = new ContextClassLoaderProxy(objectClassLoader, proxiedObject);
67          LOG.debug("Installed a ContextClassLoaderProxy on object: " + proxiedObject.getClass().getName());
68          proxiedObject = Proxy.newProxyInstance(proxyClassLoader, classesToProxy, handler);
69          //}
70  
71          return proxiedObject;
72      }
73  
74      public static Object wrap(Object proxiedObject, ClassLoader proxyClassLoader, ClassLoader objectClassLoader) {
75      	return wrap(proxiedObject, null, proxyClassLoader, objectClassLoader);
76      }
77  
78      public static Object wrap(Object proxiedObject, ClassLoader classLoader) {
79      	return wrap(proxiedObject, classLoader, classLoader);
80      }
81  
82      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy) {
83      	return wrap(proxiedObject, classesToProxy, null, null);
84      }
85  
86      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy, ClassLoader classLoader) {
87      	return wrap(proxiedObject, classesToProxy, classLoader, classLoader);
88      }
89  
90      public static Object wrap(Object proxiedObject) {
91      	return wrap(proxiedObject, null, null, null);
92      }
93  
94      public static Class<?>[] getInterfacesToProxy(Object proxiedObject) {
95  	return getInterfacesToProxy(null, proxiedObject);
96      }
97  
98      /**
99       * Determines the interfaces which need to be proxied and are visable to the given proxy ClassLoader.
100      */
101     public static Class<?>[] getInterfacesToProxy(ClassLoader proxyClassLoader, Object proxiedObject) {
102     	return ClassLoaderUtils.getInterfacesToProxy(proxiedObject, proxyClassLoader, null);
103     }
104 
105     private ClassLoader classLoader;
106 
107     public ContextClassLoaderProxy(ClassLoader classLoader, Object target) {
108     	super(target);
109         this.classLoader = classLoader;
110     }
111 
112     protected Object invokeInternal(Object proxy, Method m, Object[] args) throws Throwable {
113         ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
114         try {
115             Thread.currentThread().setContextClassLoader(this.classLoader);
116             return m.invoke(getTarget(), args);
117         } catch (InvocationTargetException e) {
118         	throw (e.getCause() != null ? e.getCause() : e);
119         } finally {
120             Thread.currentThread().setContextClassLoader(oldCl);
121         }
122     }
123 }