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