001: /*
002: * $Id: ClassUtils.java 10787 2008-02-12 18:51:50Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.util;
012:
013: import org.mule.routing.filters.WildcardFilter;
014:
015: import java.io.BufferedReader;
016: import java.io.CharArrayReader;
017: import java.io.IOException;
018: import java.io.Reader;
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Modifier;
023: import java.net.URL;
024: import java.security.AccessController;
025: import java.security.PrivilegedAction;
026: import java.util.ArrayList;
027: import java.util.Collection;
028: import java.util.Collections;
029: import java.util.Enumeration;
030: import java.util.HashMap;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.Set;
034:
035: /**
036: * Extend the Apache Commons ClassUtils to provide additional functionality.
037: * <p/>
038: * <p>This class is useful for loading resources and classes in a fault tolerant manner
039: * that works across different applications servers. The resource and classloading
040: * methods are SecurityManager friendly.</p>
041: */
042: // @ThreadSafe
043: public class ClassUtils extends org.apache.commons.lang.ClassUtils {
044: public static final Object[] NO_ARGS = new Object[] {};
045: public static final Class[] NO_ARGS_TYPE = new Class[] {};
046:
047: private static final Map wrapperToPrimitiveMap = new HashMap();
048:
049: static {
050: wrapperToPrimitiveMap.put(Boolean.class, Boolean.TYPE);
051: wrapperToPrimitiveMap.put(Byte.class, Byte.TYPE);
052: wrapperToPrimitiveMap.put(Character.class, Character.TYPE);
053: wrapperToPrimitiveMap.put(Short.class, Short.TYPE);
054: wrapperToPrimitiveMap.put(Integer.class, Integer.TYPE);
055: wrapperToPrimitiveMap.put(Long.class, Long.TYPE);
056: wrapperToPrimitiveMap.put(Double.class, Double.TYPE);
057: wrapperToPrimitiveMap.put(Float.class, Float.TYPE);
058: wrapperToPrimitiveMap.put(Void.TYPE, Void.TYPE);
059: }
060:
061: public static boolean isConcrete(Class clazz) {
062: if (clazz == null) {
063: throw new IllegalArgumentException("clazz may not be null");
064: }
065: return !(clazz.isInterface() || Modifier.isAbstract(clazz
066: .getModifiers()));
067: }
068:
069: /**
070: * Load a given resource. <p/> This method will try to load the resource using
071: * the following methods (in order):
072: * <ul>
073: * <li>From
074: * {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
075: * <li>From
076: * {@link Class#getClassLoader() ClassUtils.class.getClassLoader()}
077: * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
078: * </ul>
079: *
080: * @param resourceName The name of the resource to load
081: * @param callingClass The Class object of the calling object
082: */
083: public static URL getResource(final String resourceName,
084: final Class callingClass) {
085: URL url = (URL) AccessController
086: .doPrivileged(new PrivilegedAction() {
087: public Object run() {
088: final ClassLoader cl = Thread.currentThread()
089: .getContextClassLoader();
090: return cl != null ? cl
091: .getResource(resourceName) : null;
092: }
093: });
094:
095: if (url == null) {
096: url = (URL) AccessController
097: .doPrivileged(new PrivilegedAction() {
098: public Object run() {
099: return ClassUtils.class.getClassLoader()
100: .getResource(resourceName);
101: }
102: });
103: }
104:
105: if (url == null) {
106: url = (URL) AccessController
107: .doPrivileged(new PrivilegedAction() {
108: public Object run() {
109: return callingClass.getClassLoader()
110: .getResource(resourceName);
111: }
112: });
113: }
114:
115: return url;
116: }
117:
118: public static Enumeration getResources(final String resourceName,
119: final Class callingClass) {
120: Enumeration enumeration = (Enumeration) AccessController
121: .doPrivileged(new PrivilegedAction() {
122: public Object run() {
123: try {
124: final ClassLoader cl = Thread
125: .currentThread()
126: .getContextClassLoader();
127: return cl != null ? cl
128: .getResources(resourceName) : null;
129: } catch (IOException e) {
130: return null;
131: }
132: }
133: });
134:
135: if (enumeration == null) {
136: enumeration = (Enumeration) AccessController
137: .doPrivileged(new PrivilegedAction() {
138: public Object run() {
139: try {
140: return ClassUtils.class
141: .getClassLoader().getResources(
142: resourceName);
143: } catch (IOException e) {
144: return null;
145: }
146: }
147: });
148: }
149:
150: if (enumeration == null) {
151: enumeration = (Enumeration) AccessController
152: .doPrivileged(new PrivilegedAction() {
153: public Object run() {
154: try {
155: return callingClass.getClassLoader()
156: .getResources(resourceName);
157: } catch (IOException e) {
158: return null;
159: }
160: }
161: });
162: }
163:
164: return enumeration;
165: }
166:
167: /**
168: * Load a class with a given name. <p/> It will try to load the class in the
169: * following order:
170: * <ul>
171: * <li>From
172: * {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
173: * <li>Using the basic {@link Class#forName(java.lang.String) }
174: * <li>From
175: * {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
176: * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
177: * </ul>
178: *
179: * @param className The name of the class to load
180: * @param callingClass The Class object of the calling object
181: * @throws ClassNotFoundException If the class cannot be found anywhere.
182: */
183: public static Class loadClass(final String className,
184: final Class callingClass) throws ClassNotFoundException {
185: Class clazz = (Class) AccessController
186: .doPrivileged(new PrivilegedAction() {
187: public Object run() {
188: try {
189: final ClassLoader cl = Thread
190: .currentThread()
191: .getContextClassLoader();
192: return cl != null ? cl.loadClass(className)
193: : null;
194:
195: } catch (ClassNotFoundException e) {
196: return null;
197: }
198: }
199: });
200:
201: if (clazz == null) {
202: clazz = (Class) AccessController
203: .doPrivileged(new PrivilegedAction() {
204: public Object run() {
205: try {
206: return Class.forName(className);
207: } catch (ClassNotFoundException e) {
208: return null;
209: }
210: }
211: });
212: }
213:
214: if (clazz == null) {
215: clazz = (Class) AccessController
216: .doPrivileged(new PrivilegedAction() {
217: public Object run() {
218: try {
219: return ClassUtils.class
220: .getClassLoader().loadClass(
221: className);
222: } catch (ClassNotFoundException e) {
223: return null;
224: }
225: }
226: });
227: }
228:
229: if (clazz == null) {
230: clazz = (Class) AccessController
231: .doPrivileged(new PrivilegedAction() {
232: public Object run() {
233: try {
234: return callingClass.getClassLoader()
235: .loadClass(className);
236: } catch (ClassNotFoundException e) {
237: return null;
238: }
239: }
240: });
241: }
242:
243: if (clazz == null) {
244: throw new ClassNotFoundException(className);
245: }
246:
247: return clazz;
248: }
249:
250: /** Prints the current classloader hierarchy - useful for debugging. */
251: public static void printClassLoader() {
252: System.out.println("ClassLoaderUtils.printClassLoader");
253: printClassLoader(Thread.currentThread().getContextClassLoader());
254: }
255:
256: /**
257: * Prints the classloader hierarchy from a given classloader - useful for
258: * debugging.
259: */
260: public static void printClassLoader(ClassLoader cl) {
261: System.out.println("ClassLoaderUtils.printClassLoader(cl = "
262: + cl + ")");
263:
264: if (cl != null) {
265: printClassLoader(cl.getParent());
266: }
267: }
268:
269: /**
270: * Ensure that the given class is properly initialized when the argument is passed in
271: * as .class literal. This method can never fail unless the bytecode is corrupted or
272: * the VM is otherwise seriously confused.
273: *
274: * @param clazz the Class to be initialized
275: * @return the same class but initialized
276: */
277: public static Class initializeClass(Class clazz) {
278: try {
279: return getClass(clazz.getName(), true);
280: } catch (ClassNotFoundException e) {
281: throw new IllegalStateException();
282: }
283: }
284:
285: public static Object instanciateClass(Class clazz,
286: Object[] constructorArgs) throws SecurityException,
287: NoSuchMethodException, IllegalArgumentException,
288: InstantiationException, IllegalAccessException,
289: InvocationTargetException {
290: Class[] args;
291: if (constructorArgs != null) {
292: args = new Class[constructorArgs.length];
293: for (int i = 0; i < constructorArgs.length; i++) {
294: if (constructorArgs[i] == null) {
295: args[i] = null;
296: } else {
297: args[i] = constructorArgs[i].getClass();
298: }
299: }
300: } else {
301: args = new Class[0];
302: }
303:
304: // try the arguments as given
305: Constructor ctor = getConstructor(clazz, args);
306:
307: if (ctor == null) {
308: // try again but adapt value classes to primitives
309: ctor = getConstructor(clazz, wrappersToPrimitives(args));
310: }
311:
312: if (ctor == null) {
313: StringBuffer argsString = new StringBuffer(100);
314: for (int i = 0; i < args.length; i++) {
315: argsString.append(args[i].getName()).append(", ");
316: }
317: throw new NoSuchMethodException(
318: "could not find constructor with matching arg params: "
319: + argsString);
320: }
321:
322: return ctor.newInstance(constructorArgs);
323: }
324:
325: public static Object instanciateClass(String name,
326: Object[] constructorArgs) throws ClassNotFoundException,
327: SecurityException, NoSuchMethodException,
328: IllegalArgumentException, InstantiationException,
329: IllegalAccessException, InvocationTargetException {
330: Class clazz = loadClass(name, ClassUtils.class);
331: return instanciateClass(clazz, constructorArgs);
332:
333: }
334:
335: public static Object instanciateClass(String name,
336: Object[] constructorArgs, Class callingClass)
337: throws ClassNotFoundException, SecurityException,
338: NoSuchMethodException, IllegalArgumentException,
339: InstantiationException, IllegalAccessException,
340: InvocationTargetException {
341: Class clazz = loadClass(name, callingClass);
342: return instanciateClass(clazz, constructorArgs);
343: }
344:
345: public static Class[] getParameterTypes(Object bean,
346: String methodName) {
347: if (!methodName.startsWith("set")) {
348: methodName = "set"
349: + methodName.substring(0, 1).toUpperCase()
350: + methodName.substring(1);
351: }
352:
353: Method methods[] = bean.getClass().getMethods();
354:
355: for (int i = 0; i < methods.length; i++) {
356: if (methods[i].getName().equals(methodName)) {
357: return methods[i].getParameterTypes();
358: }
359: }
360:
361: return new Class[] {};
362: }
363:
364: /**
365: * Returns a matching method for the given name and parameters on the given class
366: * If the parameterTypes arguments is null it will return the first matching
367: * method on the class.
368: *
369: * @param clazz the class to find the method on
370: * @param name the method name to find
371: * @param parameterTypes an array of argument types or null
372: * @return the Method object or null if none was found
373: */
374: public static Method getMethod(Class clazz, String name,
375: Class[] parameterTypes) {
376: Method[] methods = clazz.getMethods();
377: for (int i = 0; i < methods.length; i++) {
378: if (methods[i].getName().equals(name)) {
379: if (parameterTypes == null) {
380: return methods[i];
381: } else if (compare(methods[i].getParameterTypes(),
382: parameterTypes, true)) {
383: return methods[i];
384: }
385: }
386: }
387: return null;
388: }
389:
390: public static Constructor getConstructor(Class clazz,
391: Class[] paramTypes) {
392: Constructor[] ctors = clazz.getConstructors();
393: for (int i = 0; i < ctors.length; i++) {
394: Class[] types = ctors[i].getParameterTypes();
395: if (types.length == paramTypes.length) {
396: boolean match = true;
397: for (int x = 0; x < types.length; x++) {
398: if (paramTypes[x] == null) {
399: match = true;
400: } else {
401: match = types[x]
402: .isAssignableFrom(paramTypes[x]);
403: }
404: }
405: if (match) {
406: return ctors[i];
407: }
408: }
409: }
410: return null;
411: }
412:
413: /**
414: * A helper method that will find all matching methods on a class with the given
415: * parameter type
416: *
417: * @param implementation the class to build methods on
418: * @param parameterTypes the argument param types to look for
419: * @param voidOk whether void methods shouldbe included in the found list
420: * @param matchOnObject determines whether parameters of Object type are matched
421: * when they are of Object.class type
422: * @param ignoredMethodNames a Set of method names to ignore. Often 'equals' is
423: * not a desired match. This argument can be null.
424: * @return a List of methods on the class that match the criteria. If there are
425: * none, an empty list is returned
426: */
427: public static List getSatisfiableMethods(Class implementation,
428: Class[] parameterTypes, boolean voidOk,
429: boolean matchOnObject, Set ignoredMethodNames) {
430: return getSatisfiableMethods(implementation, parameterTypes,
431: voidOk, matchOnObject, ignoredMethodNames, null);
432: }
433:
434: /**
435: * A helper method that will find all matching methods on a class with the given
436: * parameter type
437: *
438: * @param implementation the class to build methods on
439: * @param parameterTypes the argument param types to look for
440: * @param voidOk whether void methods shouldbe included in the found list
441: * @param matchOnObject determines whether parameters of Object type are matched
442: * when they are of Object.class type
443: * @param ignoredMethodNames a Set of method names to ignore. Often 'equals' is
444: * not a desired match. This argument can be null.
445: * @return a List of methods on the class that match the criteria. If there are
446: * none, an empty list is returned
447: */
448: public static List getSatisfiableMethods(Class implementation,
449: Class[] parameterTypes, boolean voidOk,
450: boolean matchOnObject, Collection ignoredMethodNames,
451: WildcardFilter filter) {
452:
453: List result = new ArrayList();
454:
455: if (ignoredMethodNames == null) {
456: ignoredMethodNames = Collections.EMPTY_SET;
457: }
458:
459: Method[] methods = implementation.getMethods();
460: for (int i = 0; i < methods.length; i++) {
461: Method method = methods[i];
462: //supporting wildcards
463: if (filter != null && filter.accept(method.getName())) {
464: continue;
465: }
466: Class[] methodParams = method.getParameterTypes();
467:
468: if (compare(methodParams, parameterTypes, matchOnObject)) {
469: if (!ignoredMethodNames.contains(method.getName())) {
470: String returnType = method.getReturnType()
471: .getName();
472: if ((returnType.equals("void") && voidOk)
473: || !returnType.equals("void")) {
474: result.add(method);
475: }
476: }
477: }
478: }
479:
480: return result;
481: }
482:
483: public static List getSatisfiableMethodsWithReturnType(
484: Class implementation, Class returnType,
485: boolean matchOnObject, Set ignoredMethodNames) {
486: List result = new ArrayList();
487:
488: if (ignoredMethodNames == null) {
489: ignoredMethodNames = Collections.EMPTY_SET;
490: }
491:
492: Method[] methods = implementation.getMethods();
493: for (int i = 0; i < methods.length; i++) {
494: Method method = methods[i];
495: Class returns = method.getReturnType();
496:
497: if (compare(new Class[] { returns },
498: new Class[] { returnType }, matchOnObject)) {
499: if (!ignoredMethodNames.contains(method.getName())) {
500: result.add(method);
501: }
502: }
503: }
504:
505: return result;
506: }
507:
508: /**
509: * Can be used by serice endpoints to select which service to use based on what's
510: * loaded on the classpath
511: *
512: * @param className The class name to look for
513: * @param currentClass the calling class
514: * @return true if the class is on the path
515: */
516: public static boolean isClassOnPath(String className,
517: Class currentClass) {
518: try {
519: return (loadClass(className, currentClass) != null);
520: } catch (ClassNotFoundException e) {
521: return false;
522: }
523: }
524:
525: /**
526: * Used for creating an array of class types for an array or single object
527: *
528: * @param object single object or array. If this parameter is null or a zero length
529: * array then {@link #NO_ARGS_TYPE} is returned
530: * @return an array of class types for the object
531: */
532: public static Class[] getClassTypes(Object object) {
533: if (object == null) {
534: return NO_ARGS_TYPE;
535: }
536:
537: Class[] types;
538:
539: if (object instanceof Object[]) {
540: Object[] objects = (Object[]) object;
541: if (objects.length == 0) {
542: return NO_ARGS_TYPE;
543: }
544: types = new Class[objects.length];
545: for (int i = 0; i < objects.length; i++) {
546: types[i] = objects[i].getClass();
547: }
548: } else {
549: types = new Class[] { object.getClass() };
550: }
551:
552: return types;
553: }
554:
555: public static String getClassName(Class clazz) {
556: if (clazz == null) {
557: return null;
558: }
559: String name = clazz.getName();
560: return name.substring(name.lastIndexOf(".") + 1);
561: }
562:
563: public static boolean compare(Class[] c1, Class[] c2,
564: boolean matchOnObject) {
565: if (c1.length != c2.length) {
566: return false;
567: }
568: for (int i = 0; i < c1.length; i++) {
569: if (c1[i].equals(Object.class) && !matchOnObject) {
570: return false;
571: }
572: if (!c1[i].isAssignableFrom(c2[i])) {
573:
574: return false;
575: }
576: }
577: return true;
578: }
579:
580: public static Class wrapperToPrimitive(Class wrapper) {
581: return (Class) MapUtils.getObject(wrapperToPrimitiveMap,
582: wrapper, wrapper);
583: }
584:
585: public static Class[] wrappersToPrimitives(Class[] wrappers) {
586: if (wrappers == null) {
587: return null;
588: }
589:
590: if (wrappers.length == 0) {
591: return wrappers;
592: }
593:
594: Class[] primitives = new Class[wrappers.length];
595:
596: for (int i = 0; i < wrappers.length; i++) {
597: primitives[i] = (Class) MapUtils.getObject(
598: wrapperToPrimitiveMap, wrappers[i], wrappers[i]);
599: }
600:
601: return primitives;
602: }
603:
604: /**
605: * Provide a simple-to-understand class name (with access to only Java 1.4 API).
606: *
607: * @param clazz The class whose name we will generate
608: * @return A readable name for the class
609: */
610: public static String getSimpleName(Class clazz) {
611: if (null == clazz) {
612: return "null";
613: } else {
614: return classNameHelper(new BufferedReader(
615: new CharArrayReader(clazz.getName().toCharArray())));
616: }
617: }
618:
619: private static String classNameHelper(Reader encodedName) {
620: // I did consider separating this data from the code, but I could not find a
621: // solution that was as clear to read, or clearly motivated (these data are not
622: // used elsewhere).
623:
624: try {
625: encodedName.mark(1);
626: switch (encodedName.read()) {
627: case -1:
628: return "null";
629: case 'Z':
630: return "boolean";
631: case 'B':
632: return "byte";
633: case 'C':
634: return "char";
635: case 'D':
636: return "double";
637: case 'F':
638: return "float";
639: case 'I':
640: return "int";
641: case 'J':
642: return "long";
643: case 'S':
644: return "short";
645: case '[':
646: return classNameHelper(encodedName) + "[]";
647: case 'L':
648: return shorten(new BufferedReader(encodedName)
649: .readLine());
650: default:
651: encodedName.reset();
652: return shorten(new BufferedReader(encodedName)
653: .readLine());
654: }
655: } catch (IOException e) {
656: return "unknown type: " + e.getMessage();
657: }
658: }
659:
660: /**
661: * @param clazz A class name (with possible package and trailing semicolon)
662: * @return The short name for the class
663: */
664: private static String shorten(String clazz) {
665: if (null != clazz && clazz.endsWith(";")) {
666: clazz = clazz.substring(0, clazz.length() - 1);
667: }
668: if (null != clazz && clazz.lastIndexOf(".") > -1) {
669: clazz = clazz.substring(clazz.lastIndexOf(".") + 1, clazz
670: .length());
671: }
672: return clazz;
673: }
674:
675: /**
676: * Is there a better place for this? Simple helper for writing object equalities.
677: */
678: public static boolean equal(Object a, Object b) {
679: if (null == a) {
680: return null == b;
681: } else {
682: return null != b && a.equals(b);
683: }
684: }
685:
686: public static int hash(Object[] state) {
687: int hash = 0;
688: for (int i = 0; i < state.length; ++i) {
689: hash = hash * 31
690: + (null == state[i] ? 0 : state[i].hashCode());
691: }
692: return hash;
693: }
694:
695: }
|