View Javadoc

1   /*
2    * $Id: ClassUtils.java 21729 2011-04-26 18:02:39Z svacas $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util;
12  
13  import org.mule.routing.filters.WildcardFilter;
14  
15  import java.io.BufferedReader;
16  import java.io.CharArrayReader;
17  import java.io.IOException;
18  import java.io.Reader;
19  import java.lang.reflect.Constructor;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Modifier;
23  import java.net.URL;
24  import java.net.URLClassLoader;
25  import java.security.AccessController;
26  import java.security.CodeSource;
27  import java.security.PrivilegedAction;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Enumeration;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  
39  /**
40   * Extend the Apache Commons ClassUtils to provide additional functionality.
41   * <p/>
42   * <p>This class is useful for loading resources and classes in a fault tolerant manner
43   * that works across different applications servers. The resource and classloading
44   * methods are SecurityManager friendly.</p>
45   */
46  // @ThreadSafe
47  public class ClassUtils extends org.apache.commons.lang.ClassUtils
48  {
49      public static final Object[] NO_ARGS = new Object[]{};
50      public static final Class<?>[] NO_ARGS_TYPE = new Class<?>[]{};
51  
52      private static final Map<Class<?>, Class<?>> wrapperToPrimitiveMap = new HashMap<Class<?>, Class<?>>();
53      private static final Map<String, Class<?>> primitiveTypeNameMap = new HashMap<String, Class<?>>(32);
54  
55      static
56      {
57          wrapperToPrimitiveMap.put(Boolean.class, Boolean.TYPE);
58          wrapperToPrimitiveMap.put(Byte.class, Byte.TYPE);
59          wrapperToPrimitiveMap.put(Character.class, Character.TYPE);
60          wrapperToPrimitiveMap.put(Short.class, Short.TYPE);
61          wrapperToPrimitiveMap.put(Integer.class, Integer.TYPE);
62          wrapperToPrimitiveMap.put(Long.class, Long.TYPE);
63          wrapperToPrimitiveMap.put(Double.class, Double.TYPE);
64          wrapperToPrimitiveMap.put(Float.class, Float.TYPE);
65          wrapperToPrimitiveMap.put(Void.TYPE, Void.TYPE);
66  
67          Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32);
68          primitiveTypes.addAll(wrapperToPrimitiveMap.values());
69          for (Class<?> primitiveType : primitiveTypes)
70          {
71              primitiveTypeNameMap.put(primitiveType.getName(), primitiveType);
72          }
73      }
74  
75      public static boolean isConcrete(Class<?> clazz)
76      {
77          if (clazz == null)
78          {
79              throw new IllegalArgumentException("clazz may not be null");
80          }
81          return !(clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()));
82      }
83  
84      /**
85       * Load a given resource. <p/> This method will try to load the resource using
86       * the following methods (in order):
87       * <ul>
88       * <li>From
89       * {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
90       * <li>From
91       * {@link Class#getClassLoader() ClassUtils.class.getClassLoader()}
92       * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
93       * </ul>
94       *
95       * @param resourceName The name of the resource to load
96       * @param callingClass The Class object of the calling object
97       *
98       * @return A URL pointing to the resource to load or null if the resource is not found
99       */
100     public static URL getResource(final String resourceName, final Class<?> callingClass)
101     {
102         URL url = AccessController.doPrivileged(new PrivilegedAction<URL>()
103         {
104             public URL run()
105             {
106                 final ClassLoader cl = Thread.currentThread().getContextClassLoader();
107                 return cl != null ? cl.getResource(resourceName) : null;
108             }
109         });
110 
111         if (url == null)
112         {
113             url = AccessController.doPrivileged(new PrivilegedAction<URL>()
114             {
115                 public URL run()
116                 {
117                     return ClassUtils.class.getClassLoader().getResource(resourceName);
118                 }
119             });
120         }
121 
122         if (url == null)
123         {
124             url = AccessController.doPrivileged(new PrivilegedAction<URL>()
125             {
126                 public URL run()
127                 {
128                     return callingClass.getClassLoader().getResource(resourceName);
129                 }
130             });
131         }
132 
133         return url;
134     }
135 
136     public static Enumeration<URL> getResources(final String resourceName, final Class<?> callingClass)
137     {
138         Enumeration<URL> enumeration = AccessController.doPrivileged(new PrivilegedAction<Enumeration<URL>>()
139         {
140             public Enumeration<URL> run()
141             {
142                 try
143                 {
144                     final ClassLoader cl = Thread.currentThread().getContextClassLoader();
145                     return cl != null ? cl.getResources(resourceName) : null;
146                 }
147                 catch (IOException e)
148                 {
149                     return null;
150                 }
151             }
152         });
153 
154         if (enumeration == null)
155         {
156             enumeration = AccessController.doPrivileged(new PrivilegedAction<Enumeration<URL>>()
157             {
158                 public Enumeration<URL> run()
159                 {
160                     try
161                     {
162                         return ClassUtils.class.getClassLoader().getResources(resourceName);
163                     }
164                     catch (IOException e)
165                     {
166                         return null;
167                     }
168                 }
169             });
170         }
171 
172         if (enumeration == null)
173         {
174             enumeration = AccessController.doPrivileged(new PrivilegedAction<Enumeration<URL>>()
175             {
176                 public Enumeration<URL> run()
177                 {
178                     try
179                     {
180                         return callingClass.getClassLoader().getResources(resourceName);
181                     }
182                     catch (IOException e)
183                     {
184                         return null;
185                     }
186                 }
187             });
188         }
189 
190         return enumeration;
191     }
192 
193     /**
194      * Load a class with a given name. <p/> It will try to load the class in the
195      * following order:
196      * <ul>
197      * <li>From
198      * {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
199      * <li>Using the basic {@link Class#forName(java.lang.String) }
200      * <li>From
201      * {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
202      * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
203      * </ul>
204      *
205      * @param className    The name of the class to load
206      * @param callingClass The Class object of the calling object
207      * @return The Class instance
208      * @throws ClassNotFoundException If the class cannot be found anywhere.
209      */
210     public static Class loadClass(final String className, final Class<?> callingClass) throws ClassNotFoundException
211     {
212         return loadClass(className, callingClass, Object.class);
213     }
214     /**
215      * Load a class with a given name. <p/> It will try to load the class in the
216      * following order:
217      * <ul>
218      * <li>From
219      * {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
220      * <li>Using the basic {@link Class#forName(java.lang.String) }
221      * <li>From
222      * {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
223      * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
224      * </ul>
225      *
226      * @param className    The name of the class to load
227      * @param callingClass The Class object of the calling object
228      * @param type the class type to expect to load
229      * @return The Class instance
230      * @throws ClassNotFoundException If the class cannot be found anywhere.
231      */
232     public static <T extends Class> T loadClass(final String className, final Class<?> callingClass, T type) throws ClassNotFoundException
233     {
234         if (className.length() <= 8)
235         {
236             // Could be a primitive - likely.
237             if (primitiveTypeNameMap.containsKey(className))
238             {
239                 return (T) primitiveTypeNameMap.get(className);
240             }
241         }
242         
243         Class<?> clazz = AccessController.doPrivileged(new PrivilegedAction<Class<?>>()
244         {
245             public Class<?> run()
246             {
247                 try
248                 {
249                     final ClassLoader cl = Thread.currentThread().getContextClassLoader();
250                     return cl != null ? cl.loadClass(className) : null;
251 
252                 }
253                 catch (ClassNotFoundException e)
254                 {
255                     return null;
256                 }
257             }
258         });
259 
260         if (clazz == null)
261         {
262             clazz = AccessController.doPrivileged(new PrivilegedAction<Class<?>>()
263             {
264                 public Class<?> run()
265                 {
266                     try
267                     {
268                         return Class.forName(className);
269                     }
270                     catch (ClassNotFoundException e)
271                     {
272                         return null;
273                     }
274                 }
275             });
276         }
277 
278         if (clazz == null)
279         {
280             clazz = AccessController.doPrivileged(new PrivilegedAction<Class<?>>()
281             {
282                 public Class<?> run()
283                 {
284                     try
285                     {
286                         return ClassUtils.class.getClassLoader().loadClass(className);
287                     }
288                     catch (ClassNotFoundException e)
289                     {
290                         return null;
291                     }
292                 }
293             });
294         }
295 
296         if (clazz == null)
297         {
298             clazz = AccessController.doPrivileged(new PrivilegedAction<Class<?>>()
299             {
300                 public Class<?> run()
301                 {
302                     try
303                     {
304                         return callingClass.getClassLoader().loadClass(className);
305                     }
306                     catch (ClassNotFoundException e)
307                     {
308                         return null;
309                     }
310                 }
311             });
312         }
313 
314         if (clazz == null)
315         {
316             throw new ClassNotFoundException(className);
317         }
318 
319         if(type.isAssignableFrom(clazz))
320         {
321             return (T)clazz;
322         }
323         else
324         {
325             throw new IllegalArgumentException(String.format("Loaded class '%s' is not assignable from type '%s'", clazz.getName(), type.getName()));
326         }
327     }
328 
329     /**
330      * Load a class with a given name from the given classloader.
331      *
332      * @param className the name of the class to load
333      * @param classLoader the loader to load it from
334      * @return the instance of the class
335      * @throws ClassNotFoundException if the class is not available in the class loader
336      */
337     public static Class loadClass(final String className, final ClassLoader classLoader)
338             throws ClassNotFoundException
339     {
340         return classLoader.loadClass(className);
341     }
342 
343 
344     /**
345      * Ensure that the given class is properly initialized when the argument is passed in
346      * as .class literal. This method can never fail unless the bytecode is corrupted or
347      * the VM is otherwise seriously confused.
348      *
349      * @param clazz the Class to be initialized
350      * @return the same class but initialized
351      */
352     public static Class<?> initializeClass(Class<?> clazz)
353     {
354         try
355         {
356             return getClass(clazz.getName(), true);
357         }
358         catch (ClassNotFoundException e)
359         {
360             IllegalStateException ise = new IllegalStateException();
361             ise.initCause(e);
362             throw ise;
363         }
364     }
365 
366     public static <T> T instanciateClass(Class<? extends T> clazz, Object... constructorArgs)
367             throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException,
368             IllegalAccessException, InvocationTargetException
369     {
370         Class<?>[] args;
371         if (constructorArgs != null)
372         {
373             args = new Class[constructorArgs.length];
374             for (int i = 0; i < constructorArgs.length; i++)
375             {
376                 if (constructorArgs[i] == null)
377                 {
378                     args[i] = null;
379                 }
380                 else
381                 {
382                     args[i] = constructorArgs[i].getClass();
383                 }
384             }
385         }
386         else
387         {
388             args = new Class[0];
389         }
390 
391         // try the arguments as given
392         //Constructor ctor = clazz.getConstructor(args);
393         Constructor<?> ctor = getConstructor(clazz, args);
394 
395         if (ctor == null)
396         {
397             // try again but adapt value classes to primitives
398             ctor = getConstructor(clazz, wrappersToPrimitives(args));
399         }
400 
401         if (ctor == null)
402         {
403             StringBuffer argsString = new StringBuffer(100);
404             for (Class<?> arg : args)
405             {
406                 argsString.append(arg.getName()).append(", ");
407             }
408             throw new NoSuchMethodException("could not find constructor on class: " + clazz + ", with matching arg params: "
409                     + argsString);
410         }
411 
412         return (T)ctor.newInstance(constructorArgs);
413     }
414 
415     public static Object instanciateClass(String name, Object... constructorArgs)
416             throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
417             InstantiationException, IllegalAccessException, InvocationTargetException
418     {
419         return instanciateClass(name, constructorArgs, (ClassLoader) null);
420     }
421 
422     public static Object instanciateClass(String name, Object[] constructorArgs, Class<?> callingClass)
423             throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
424             InstantiationException, IllegalAccessException, InvocationTargetException
425     {
426         Class<?> clazz = loadClass(name, callingClass);
427         return instanciateClass(clazz, constructorArgs);
428     }
429 
430     public static Object instanciateClass(String name, Object[] constructorArgs, ClassLoader classLoader)
431             throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException,
432             InstantiationException, IllegalAccessException, InvocationTargetException
433     {
434         Class<?> clazz;
435         if (classLoader != null)
436         {
437             clazz = loadClass(name, classLoader);
438         }
439         else
440         {
441             clazz = loadClass(name, ClassUtils.class);
442         }
443         if (clazz == null)
444         {
445             throw new ClassNotFoundException(name);
446         }
447         return instanciateClass(clazz, constructorArgs);
448     }
449 
450     public static Class<?>[] getParameterTypes(Object bean, String methodName)
451     {
452         if (!methodName.startsWith("set"))
453         {
454             methodName = "set" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
455         }
456 
457         Method methods[] = bean.getClass().getMethods();
458 
459         for (int i = 0; i < methods.length; i++)
460         {
461             if (methods[i].getName().equals(methodName))
462             {
463                 return methods[i].getParameterTypes();
464             }
465         }
466 
467         return new Class[]{};
468     }
469 
470     /**
471      * Returns a matching method for the given name and parameters on the given class
472      * If the parameterTypes arguments is null it will return the first matching
473      * method on the class.
474      *
475      * @param clazz          the class to find the method on
476      * @param name           the method name to find
477      * @param parameterTypes an array of argument types or null
478      * @return the Method object or null if none was found
479      */
480     public static Method getMethod(Class<?> clazz, String name, Class<?>[] parameterTypes)
481     {
482         return getMethod(clazz, name, parameterTypes, false);
483     }
484 
485     public static Method getMethod(Class clazz, String name, Class[] parameterTypes, boolean acceptNulls)
486     {
487         Method[] methods = clazz.getMethods();
488         for (int i = 0; i < methods.length; i++)
489         {
490             if (methods[i].getName().equals(name))
491             {
492                 if (parameterTypes == null)
493                 {
494                     return methods[i];
495                 }
496                 else if (compare(methods[i].getParameterTypes(), parameterTypes, true, acceptNulls))
497                 {
498                     return methods[i];
499                 }
500             }
501         }
502         return null;
503     }
504 
505     public static Constructor getConstructor(Class clazz, Class[] paramTypes)
506     {
507         return getConstructor(clazz, paramTypes, false);
508     }
509     
510     /**
511      *  Returns available constructor in the target class that as the parameters specified.
512      *
513      * @param clazz the class to search
514      * @param paramTypes the param types to match against
515      * @param exactMatch should exact types be used (i.e. equals rather than isAssignableFrom.)
516      * @return The matching constructor or null if no matching constructor is found
517      */
518     public static Constructor getConstructor(Class clazz, Class[] paramTypes, boolean exactMatch)
519     {
520         Constructor[] ctors = clazz.getConstructors();
521         for (int i = 0; i < ctors.length; i++)
522         {
523             Class[] types = ctors[i].getParameterTypes();
524             if (types.length == paramTypes.length)
525             {
526                 int matchCount = 0;
527                 for (int x = 0; x < types.length; x++)
528                 {
529                     if (paramTypes[x] == null)
530                     {
531                         matchCount++;
532                     }
533                     else
534                     {
535                         if (exactMatch)
536                         {
537                             if (paramTypes[x].equals(types[x]) || types[x].equals(paramTypes[x]))
538                             {
539                                 matchCount++;
540                             }
541                         }
542                         else
543                         {
544                             if (paramTypes[x].isAssignableFrom(types[x])
545                                 || types[x].isAssignableFrom(paramTypes[x]))
546                             {
547                                 matchCount++;
548                             }
549                         }
550                     }
551                 }
552                 if (matchCount == types.length)
553                 {
554                     return ctors[i];
555                 }
556             }
557         }
558         return null;
559     }
560 
561     /**
562      * A helper method that will find all matching methods on a class with the given
563      * parameter type
564      *
565      * @param implementation     the class to build methods on
566      * @param parameterTypes     the argument param types to look for
567      * @param voidOk             whether void methods shouldbe included in the found list
568      * @param matchOnObject      determines whether parameters of Object type are matched
569      *                           when they are of Object.class type
570      * @param ignoredMethodNames a Set of method names to ignore. Often 'equals' is
571      *                           not a desired match. This argument can be null.
572      * @return a List of methods on the class that match the criteria. If there are
573      *         none, an empty list is returned
574      */
575     public static List<Method> getSatisfiableMethods(Class<?> implementation,
576                                              Class<?>[] parameterTypes,
577                                              boolean voidOk,
578                                              boolean matchOnObject,
579                                              Set<String> ignoredMethodNames)
580     {
581         return getSatisfiableMethods(implementation, parameterTypes, voidOk, matchOnObject, ignoredMethodNames, null);
582     }
583 
584     /**
585      * A helper method that will find all matching methods on a class with the given
586      * parameter type
587      *
588      * @param implementation     the class to build methods on
589      * @param parameterTypes     the argument param types to look for
590      * @param voidOk             whether void methods shouldbe included in the found list
591      * @param matchOnObject      determines whether parameters of Object type are matched
592      *                           when they are of Object.class type
593      * @param ignoredMethodNames a Set of method names to ignore. Often 'equals' is
594      *                           not a desired match. This argument can be null.
595      * @param filter             Wildcard expression filter that allows methods to be matched using wildcards i.e. 'get*'
596      * @return a List of methods on the class that match the criteria. If there are
597      *         none, an empty list is returned
598      */
599     public static List<Method> getSatisfiableMethods(Class<?> implementation,
600                                              Class<?>[] parameterTypes,
601                                              boolean voidOk,
602                                              boolean matchOnObject,
603                                              Collection<String> ignoredMethodNames,
604                                              WildcardFilter filter)
605     {
606         List<Method> result = new ArrayList<Method>();
607 
608         if (ignoredMethodNames == null)
609         {
610             ignoredMethodNames = Collections.emptySet();
611         }
612 
613         Method[] methods = implementation.getMethods();
614         for (int i = 0; i < methods.length; i++)
615         {
616             Method method = methods[i];
617             //supporting wildcards
618             if (filter != null && filter.accept(method.getName()))
619             {
620                 continue;
621             }
622             Class<?>[] methodParams = method.getParameterTypes();
623 
624             if (compare(methodParams, parameterTypes, matchOnObject))
625             {
626                 if (!ignoredMethodNames.contains(method.getName()))
627                 {
628                     String returnType = method.getReturnType().getName();
629                     if ((returnType.equals("void") && voidOk) || !returnType.equals("void"))
630                     {
631                         result.add(method);
632                     }
633                 }
634             }
635         }
636 
637         return result;
638     }
639 
640     /**
641      * Match all method son a class with a defined return type
642      * @param implementation the class to search
643      * @param returnType the return type to match
644      * @param matchOnObject whether {@link Object} methods should be matched
645      * @param ignoredMethodNames a set of method names to ignore
646      * @return the list of methods that matched the return type and criteria. If none are found an empty result is returned
647      */
648     public static List<Method> getSatisfiableMethodsWithReturnType(Class implementation,
649                                                            Class returnType,
650                                                            boolean matchOnObject,
651                                                            Set<String> ignoredMethodNames)
652     {
653         List<Method> result = new ArrayList<Method>();
654 
655         if (ignoredMethodNames == null)
656         {
657             ignoredMethodNames = Collections.emptySet();
658         }
659 
660         Method[] methods = implementation.getMethods();
661         for (int i = 0; i < methods.length; i++)
662         {
663             Method method = methods[i];
664             Class returns = method.getReturnType();
665 
666             if (compare(new Class[]{returns}, new Class[]{returnType}, matchOnObject))
667             {
668                 if (!ignoredMethodNames.contains(method.getName()))
669                 {
670                     result.add(method);
671                 }
672             }
673         }
674 
675         return result;
676     }
677 
678     /**
679      * Can be used by serice endpoints to select which service to use based on what's
680      * loaded on the classpath
681      *
682      * @param className    The class name to look for
683      * @param currentClass the calling class
684      * @return true if the class is on the path
685      */
686     public static boolean isClassOnPath(String className, Class currentClass)
687     {
688         try
689         {
690             return (loadClass(className, currentClass) != null);
691         }
692         catch (ClassNotFoundException e)
693         {
694             return false;
695         }
696     }
697 
698     /**
699      * Used for creating an array of class types for an array or single object
700      *
701      * @param object single object or array. If this parameter is null or a zero length
702      *               array then {@link #NO_ARGS_TYPE} is returned
703      * @return an array of class types for the object
704      */
705     public static Class<?>[] getClassTypes(Object object)
706     {
707         if (object == null)
708         {
709             return NO_ARGS_TYPE;
710         }
711 
712         Class<?>[] types;
713 
714         if (object instanceof Object[])
715         {
716             Object[] objects = (Object[]) object;
717             if (objects.length == 0)
718             {
719                 return NO_ARGS_TYPE;
720             }
721             types = new Class[objects.length];
722             for (int i = 0; i < objects.length; i++)
723             {
724                 Object o = objects[i];
725                 if (o != null)
726                 {
727                     types[i] = o.getClass();
728                 }
729             }
730         }
731         else
732         {
733             types = new Class[]{object.getClass()};
734         }
735 
736         return types;
737     }
738 
739     public static String getClassName(Class clazz)
740     {
741         if (clazz == null)
742         {
743             return null;
744         }
745         String name = clazz.getName();
746         return name.substring(name.lastIndexOf(".") + 1);
747     }
748 
749     public static boolean compare(Class[] c1, Class[] c2, boolean matchOnObject)
750     {
751         return compare(c1, c2, matchOnObject, false);
752     }
753 
754     /**
755      * Returns true if the types from array c2 are assignable to the types from c1
756      * and the arrays are the same size. If matchOnObject argument is true and there
757      * is a parameter of type Object in c1 then the method returns false. If
758      * acceptNulls argument is true, null values are accepted in c2.
759      * 
760      * @param c1 parameter types array
761      * @param c2 parameter types array
762      * @param matchOnObject return false if there is a parameter of type Object in c1
763      * @param acceptNulls allows null parameter types in c2
764      * @return true if arrays are the same size and the types assignable from c2 to
765      *         c1
766      */
767     public static boolean compare(Class[] c1, Class[] c2, boolean matchOnObject, boolean acceptNulls)
768     {
769         if (c1.length != c2.length)
770         {
771             return false;
772         }
773         for (int i = 0; i < c1.length; i++)
774         {
775             if (!acceptNulls)
776             {
777                 if ((c1[i] == null) || (c2[i] == null))
778                 {
779                     return false;
780                 }
781             } 
782             else 
783             {
784                 if (c1[i] == null)
785                 {
786                     return false;
787                 }
788                 if ((c2[i] == null) && (c1[i].isPrimitive()))
789                 {
790                     return false;
791                 }
792                 if (c2[i] == null)
793                 {
794                     return true;
795                 }
796             }
797             if (c1[i].equals(Object.class) && !matchOnObject)
798             {
799                 return false;
800             }
801             if (!c1[i].isAssignableFrom(c2[i]))
802             {
803                 return false;
804             }
805         }
806         return true;
807     }
808 
809     public static Class wrapperToPrimitive(Class wrapper)
810     {
811         return (Class) MapUtils.getObject(wrapperToPrimitiveMap, wrapper, wrapper);
812     }
813 
814     public static Class[] wrappersToPrimitives(Class[] wrappers)
815     {
816         if (wrappers == null)
817         {
818             return null;
819         }
820 
821         if (wrappers.length == 0)
822         {
823             return wrappers;
824         }
825 
826         Class[] primitives = new Class[wrappers.length];
827 
828         for (int i = 0; i < wrappers.length; i++)
829         {
830             primitives[i] = (Class) MapUtils.getObject(wrapperToPrimitiveMap, wrappers[i], wrappers[i]);
831         }
832 
833         return primitives;
834     }
835 
836     /**
837      * Provide a simple-to-understand class name (with access to only Java 1.4 API).
838      *
839      * @param clazz The class whose name we will generate
840      * @return A readable name for the class
841      */
842     public static String getSimpleName(Class clazz)
843     {
844         if (null == clazz)
845         {
846             return "null";
847         }
848         else
849         {
850             return classNameHelper(new BufferedReader(new CharArrayReader(clazz.getName().toCharArray())));
851         }
852     }
853 
854     private static String classNameHelper(Reader encodedName)
855     {
856         // I did consider separating this data from the code, but I could not find a
857         // solution that was as clear to read, or clearly motivated (these data are not
858         // used elsewhere).
859 
860         try
861         {
862             encodedName.mark(1);
863             switch (encodedName.read())
864             {
865                 case -1:
866                     return "null";
867                 case 'Z':
868                     return "boolean";
869                 case 'B':
870                     return "byte";
871                 case 'C':
872                     return "char";
873                 case 'D':
874                     return "double";
875                 case 'F':
876                     return "float";
877                 case 'I':
878                     return "int";
879                 case 'J':
880                     return "long";
881                 case 'S':
882                     return "short";
883                 case '[':
884                     return classNameHelper(encodedName) + "[]";
885                 case 'L':
886                     return shorten(new BufferedReader(encodedName).readLine());
887                 default:
888                     encodedName.reset();
889                     return shorten(new BufferedReader(encodedName).readLine());
890             }
891         }
892         catch (IOException e)
893         {
894             return "unknown type: " + e.getMessage();
895         }
896     }
897 
898     /**
899      * @param clazz A class name (with possible package and trailing semicolon)
900      * @return The short name for the class
901      */
902     private static String shorten(String clazz)
903     {
904         if (null != clazz && clazz.endsWith(";"))
905         {
906             clazz = clazz.substring(0, clazz.length() - 1);
907         }
908         if (null != clazz && clazz.lastIndexOf(".") > -1)
909         {
910             clazz = clazz.substring(clazz.lastIndexOf(".") + 1, clazz.length());
911         }
912         return clazz;
913     }
914 
915     /**
916      * Simple helper for writing object equalities.
917      *
918      * TODO Is there a better place for this?
919      * @param a object to compare
920      * @param b object to be compared to
921      * @return true if the objects are equal (value or reference), false otherwise
922      */
923     public static boolean equal(Object a, Object b)
924     {
925         if (null == a)
926         {
927             return null == b;
928         }
929         else
930         {
931             return null != b && a.equals(b);
932         }
933     }
934 
935     public static int hash(Object[] state)
936     {
937         int hash = 0;
938         for (int i = 0; i < state.length; ++i)
939         {
940             hash = hash * 31 + (null == state[i] ? 0 : state[i].hashCode());
941         }
942         return hash;
943     }
944 
945     public static void addLibrariesToClasspath(List urls) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
946     {
947         ClassLoader sys = ClassLoader.getSystemClassLoader();
948         if (!(sys instanceof URLClassLoader))
949         {
950             throw new IllegalArgumentException(
951                     "PANIC: Mule has been started with an unsupported classloader: " + sys.getClass().getName()
952                             + ". " + "Please report this error to user<at>mule<dot>codehaus<dot>org");
953         }
954 
955         // system classloader is in this case the one that launched the application,
956         // which is usually something like a JDK-vendor proprietary AppClassLoader
957         URLClassLoader sysCl = (URLClassLoader) sys;
958 
959         /*
960         * IMPORTANT NOTE: The more 'natural' way would be to create a custom
961         * URLClassLoader and configure it, but then there's a chicken-and-egg
962         * problem, as all classes MuleBootstrap depends on would have been loaded by
963         * a parent classloader, and not ours. There's no straightforward way to
964         * change this, and is documented in a Sun's classloader guide. The solution
965         * would've involved overriding the ClassLoader.findClass() method and
966         * modifying the semantics to be child-first, but that way we are calling for
967         * trouble. Hacking the primordial classloader is a bit brutal, but works
968         * perfectly in case of running from the command-line as a standalone app.
969         * All Mule embedding options then delegate the classpath config to the
970         * embedder (a developer embedding Mule in the app), thus classloaders are
971         * not modified in those scenarios.
972         */
973 
974         // get a Method ref from the normal class, but invoke on a proprietary parent
975         // object,
976         // as this method is usually protected in those classloaders
977         Class refClass = URLClassLoader.class;
978         Method methodAddUrl = refClass.getDeclaredMethod("addURL", new Class[]{URL.class});
979         methodAddUrl.setAccessible(true);
980         for (Iterator it = urls.iterator(); it.hasNext();)
981         {
982             URL url = (URL) it.next();
983             methodAddUrl.invoke(sysCl, url);
984         }
985     }
986 
987     // this is a shorter version of the snippet from:
988     // http://www.davidflanagan.com/blog/2005_06.html#000060
989     // (see comments; DF's "manual" version works fine too)
990     public static URL getClassPathRoot(Class clazz)
991     {
992         CodeSource cs = clazz.getProtectionDomain().getCodeSource();
993         return (cs != null ? cs.getLocation() : null);
994     }
995 }