View Javadoc

1   /*
2    * Copyright 2006-2011 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  
17  package org.kuali.rice.core.impl.resourceloader;
18  
19  import org.kuali.rice.core.util.ClassLoaderUtils;
20  import org.kuali.rice.core.util.reflect.BaseTargetedInvocationHandler;
21  
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Proxy;
25  
26  /**
27   * A Proxy that sets the thread Context ClassLoader before invocation of the
28   * proxied object, and resets it back afterwards.
29   *
30   * @author Kuali Rice Team (rice.collab@kuali.org)
31   */
32  public class ContextClassLoaderProxy extends BaseTargetedInvocationHandler {
33  
34      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ContextClassLoaderProxy.class);
35  
36      /**
37       * Convenience method that wraps a specified object with a ContextClassLoaderProxy, with a specified
38       * handler classloader and proxy classloader.  If the specified object is null, or the object classloader
39       * equals the proxy classloader, the object is returned unwrapped.
40       * @param proxiedObject the object to proxy
41       * @param proxyClassLoader the classloader OF THE PROXY INSTANCE
42       * @param objectClassLoader the classloader to set as the context classloader prior to any invocations on the proxiedObject
43       * @return a ContextClassLoaderProxy Proxy for the proxiedObject
44       */
45      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy, ClassLoader proxyClassLoader, ClassLoader objectClassLoader) {
46          if (proxiedObject == null) {
47          	return null;
48          }
49      	if (proxyClassLoader == null) {
50              //proxyClassLoader = Thread.currentThread().getContextClassLoader();
51          	proxyClassLoader = proxiedObject.getClass().getClassLoader();
52          }
53          if (objectClassLoader == null) {
54              objectClassLoader = proxiedObject.getClass().getClassLoader();
55          }
56          if (classesToProxy == null) {
57              classesToProxy = getInterfacesToProxy(proxyClassLoader, proxiedObject);
58          }
59          // this classloader comparison looks fishy
60          // it is testing the classloader of the proxy against the intended *context* classloader of the proxiedObject
61          // if these are the same the proxiedObject is not wrapped.  However, that implies that if the proxy
62          // classloader may equal the intended context class loader, that the context class loader is actually not set,
63          // and could theoretically NOT be the intended one in the future
64          // somebody who understands this better than me should investigate this, and this method which I have
65          // now applied to all prior uses of ContextClassLoaderProxy as a convenience
66          //if (proxiedObject != null) { //&& !objectClassLoader.equals(proxyClassLoader)) {
67          ContextClassLoaderProxy handler = new ContextClassLoaderProxy(objectClassLoader, proxiedObject);
68          LOG.debug("Installed a ContextClassLoaderProxy on object: " + proxiedObject.getClass().getName());
69          proxiedObject = Proxy.newProxyInstance(proxyClassLoader, classesToProxy, handler);
70          //}
71  
72          return proxiedObject;
73      }
74  
75      public static Object wrap(Object proxiedObject, ClassLoader proxyClassLoader, ClassLoader objectClassLoader) {
76      	return wrap(proxiedObject, null, proxyClassLoader, objectClassLoader);
77      }
78  
79      public static Object wrap(Object proxiedObject, ClassLoader classLoader) {
80      	return wrap(proxiedObject, classLoader, classLoader);
81      }
82  
83      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy) {
84      	return wrap(proxiedObject, classesToProxy, null, null);
85      }
86  
87      public static Object wrap(Object proxiedObject, Class<?>[] classesToProxy, ClassLoader classLoader) {
88      	return wrap(proxiedObject, classesToProxy, classLoader, classLoader);
89      }
90  
91      public static Object wrap(Object proxiedObject) {
92      	return wrap(proxiedObject, null, null, null);
93      }
94  
95      public static Class<?>[] getInterfacesToProxy(Object proxiedObject) {
96  	return getInterfacesToProxy(null, proxiedObject);
97      }
98  
99      /**
100      * Determines the interfaces which need to be proxied and are visable to the given proxy ClassLoader.
101      */
102     public static Class<?>[] getInterfacesToProxy(ClassLoader proxyClassLoader, Object proxiedObject) {
103     	return ClassLoaderUtils.getInterfacesToProxy(proxiedObject, proxyClassLoader, null);
104     }
105 
106     private ClassLoader classLoader;
107 
108     public ContextClassLoaderProxy(ClassLoader classLoader, Object target) {
109     	super(target);
110         this.classLoader = classLoader;
111     }
112 
113     protected Object invokeInternal(Object proxy, Method m, Object[] args) throws Throwable {
114         ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
115         try {
116             Thread.currentThread().setContextClassLoader(this.classLoader);
117             return m.invoke(getTarget(), args);
118         } catch (InvocationTargetException e) {
119         	throw (e.getCause() != null ? e.getCause() : e);
120         } finally {
121             Thread.currentThread().setContextClassLoader(oldCl);
122         }
123     }
124 }