View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.kuali.rice.core.util;
18  
19  import java.lang.reflect.InvocationHandler;
20  import java.lang.reflect.Proxy;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.apache.commons.lang.ClassUtils;
25  import org.apache.commons.lang.StringUtils;
26  import org.kuali.rice.core.api.exception.RiceRuntimeException;
27  import org.kuali.rice.core.util.reflect.TargetedInvocationHandler;
28  
29  /**
30   * Provides common utility methods for dealing with Classloaders.
31   *
32   * @author Kuali Rice Team (rice.collab@kuali.org)
33   */
34  public final class ClassLoaderUtils {
35  
36      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ClassLoaderUtils.class);
37      
38  	private ClassLoaderUtils() {
39  		throw new UnsupportedOperationException("do not call");
40  	}
41  
42  	/**
43  	 * Returns the default class loader within the current context.  If there is a context classloader
44  	 * it is returned, otherwise the classloader which loaded the ClassLoaderUtil Class is returned.
45  	 *
46  	 * @return the appropriate default classloader which is guaranteed to be non-null
47  	 */
48  	public static ClassLoader getDefaultClassLoader() {
49  		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
50  		if (classLoader == null) {
51  			classLoader = ClassLoaderUtils.class.getClassLoader();
52  		}
53  		return classLoader;
54  	}
55  
56  	/**
57  	 * Checks if the given object is an instance of the given class, unwrapping any proxies if
58  	 * necessary to get to the underlying object.
59  	 */
60  	public static boolean isInstanceOf(Object object, Class<?> instanceClass) {
61  		if (object == null) {
62  			return false;
63  		}
64  		if (instanceClass.isInstance(object)) {
65  			return true;
66  		}
67  		return isInstanceOf(unwrapFromProxyOnce(object), instanceClass);
68  	}
69  
70  	public static Object unwrapFromProxy(Object proxy) {
71  		Object unwrapped = unwrapFromProxyOnce(proxy);
72  		if (unwrapped == null) {
73  			return proxy;
74  		}
75  		return unwrapFromProxy(unwrapped);
76  	}
77  
78  	/**
79  	 * Unwraps the underlying object from the given proxy (which may itself be a proxy).  If the
80  	 * given object is not a valid proxy, then null is returned.
81  	 */
82  	private static Object unwrapFromProxyOnce(Object proxy) {
83  		if (proxy != null && Proxy.isProxyClass(proxy.getClass())) {
84  			InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
85  			if (invocationHandler instanceof TargetedInvocationHandler) {
86  				return ((TargetedInvocationHandler)invocationHandler).getTarget();
87  			}
88  		}
89  		return null;
90  	}
91  
92  	/**
93  	 * Checks if the given Class is visible to the given ClassLoader.
94  	 */
95  	public static boolean isClassVisible(ClassLoader classLoader, Class<?> classToCheck) {
96  	    try {
97  		Class<?> classFound = classLoader.loadClass(classToCheck.getName());
98  		return classFound.equals(classToCheck);
99  	    } catch (ClassNotFoundException e) {
100 		return false;
101 	    }
102 	}
103 
104 	/**
105 	 * Determines the interfaces which need to be proxied and are visible to the given proxy ClassLoader.
106 	 */
107 	public static Class[] getInterfacesToProxy(Object object, ClassLoader proxyClassLoader, String[] packageNamesToFilter) {
108 	    List interfaces = ClassUtils.getAllInterfaces(object.getClass());
109 	    outer:for (Iterator iterator = interfaces.iterator(); iterator.hasNext();) {
110 		Class objectInterface = (Class) iterator.next();
111 		// check package name filters
112 		if (packageNamesToFilter != null) {
113 		    for (String packageNames : packageNamesToFilter) {
114 			if (objectInterface.getName().startsWith(packageNames)) {
115 			    iterator.remove();
116 			    continue outer;
117 			}
118 		    }
119 		}
120 		// check that the interface is visible from the given classloader
121 		if (proxyClassLoader != null) {
122 		    if (!ClassLoaderUtils.isClassVisible(proxyClassLoader, objectInterface)) {
123 			if (LOG.isDebugEnabled()) {
124 			    LOG.debug("The interface " + objectInterface + " was not visible from the proxy ClassLoader when attempting to proxy: " + object);
125 			}
126 			iterator.remove();
127 			continue outer;
128 		    }
129 		}
130 	    }
131 	    Class[] interfaceArray = new Class[interfaces.size()];
132 	    return (Class[]) interfaces.toArray(interfaceArray);
133 	}
134 	
135 	// TODO why not make this so that it throws ClassNoteFoundException and doesn't return null?
136 	// this is more standard behavior...
137 	public static Class<?> getClass(String className) {
138 		if (StringUtils.isEmpty(className)) {
139 			return null;
140 		}
141 		try {
142 			return ClassUtils.getClass(getDefaultClassLoader(), className);
143 		} catch (ClassNotFoundException e) {
144 			throw new RiceRuntimeException(e);
145 		}
146 	}
147 	
148 	public static <T> Class<? extends T> getClass(String className, Class<T> type) throws ClassNotFoundException {
149 		Class<?> theClass = ClassUtils.getClass(getDefaultClassLoader(), className);
150 		return theClass.asSubclass(type);
151 	}
152 	
153 }