View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.shared.util;
20  
21  import javax.el.ExpressionFactory;
22  import javax.faces.FacesException;
23  import javax.faces.context.FacesContext;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.lang.reflect.Array;
27  import java.lang.reflect.Constructor;
28  import java.lang.reflect.InvocationTargetException;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.Arrays;
32  import java.util.Collection;
33  import java.util.Enumeration;
34  import java.util.HashMap;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.logging.Level;
39  import java.util.logging.Logger;
40  
41  
42  public final class ClassUtils
43  {
44      //~ Static fields/initializers -----------------------------------------------------------------
45  
46      //private static final Log log                  = LogFactory.getLog(ClassUtils.class);
47      private static final Logger log                  = Logger.getLogger(ClassUtils.class.getName());
48  
49      public static final Class BOOLEAN_ARRAY_CLASS = boolean[].class;
50      public static final Class BYTE_ARRAY_CLASS    = byte[].class;
51      public static final Class CHAR_ARRAY_CLASS    = char[].class;
52      public static final Class SHORT_ARRAY_CLASS   = short[].class;
53      public static final Class INT_ARRAY_CLASS     = int[].class;
54      public static final Class LONG_ARRAY_CLASS    = long[].class;
55      public static final Class FLOAT_ARRAY_CLASS   = float[].class;
56      public static final Class DOUBLE_ARRAY_CLASS  = double[].class;
57      public static final Class OBJECT_ARRAY_CLASS  = Object[].class;
58      public static final Class BOOLEAN_OBJECT_ARRAY_CLASS = Boolean[].class;
59      public static final Class BYTE_OBJECT_ARRAY_CLASS = Byte[].class;
60      public static final Class CHARACTER_OBJECT_ARRAY_CLASS = Character[].class;
61      public static final Class SHORT_OBJECT_ARRAY_CLASS = Short[].class;
62      public static final Class INTEGER_OBJECT_ARRAY_CLASS = Integer[].class;
63      public static final Class LONG_OBJECT_ARRAY_CLASS = Long[].class;
64      public static final Class FLOAT_OBJECT_ARRAY_CLASS = Float[].class;
65      public static final Class DOUBLE_OBJECT_ARRAY_CLASS = Double[].class;
66      public static final Class STRING_OBJECT_ARRAY_CLASS = String[].class;
67  
68      public static ClassLoaderExtension [] classLoadingExtensions = new ClassLoaderExtension[0];
69  
70  
71  
72      public static final Map COMMON_TYPES = new HashMap(64);
73      static
74      {
75          COMMON_TYPES.put("byte", Byte.TYPE);
76          COMMON_TYPES.put("char", Character.TYPE);
77          COMMON_TYPES.put("double", Double.TYPE);
78          COMMON_TYPES.put("float", Float.TYPE);
79          COMMON_TYPES.put("int", Integer.TYPE);
80          COMMON_TYPES.put("long", Long.TYPE);
81          COMMON_TYPES.put("short", Short.TYPE);
82          COMMON_TYPES.put("boolean", Boolean.TYPE);
83          COMMON_TYPES.put("void", Void.TYPE);
84          COMMON_TYPES.put("java.lang.Object", Object.class);
85          COMMON_TYPES.put("java.lang.Boolean", Boolean.class);
86          COMMON_TYPES.put("java.lang.Byte", Byte.class);
87          COMMON_TYPES.put("java.lang.Character", Character.class);
88          COMMON_TYPES.put("java.lang.Short", Short.class);
89          COMMON_TYPES.put("java.lang.Integer", Integer.class);
90          COMMON_TYPES.put("java.lang.Long", Long.class);
91          COMMON_TYPES.put("java.lang.Float", Float.class);
92          COMMON_TYPES.put("java.lang.Double", Double.class);
93          COMMON_TYPES.put("java.lang.String", String.class);
94  
95          COMMON_TYPES.put("byte[]", BYTE_ARRAY_CLASS);
96          COMMON_TYPES.put("char[]", CHAR_ARRAY_CLASS);
97          COMMON_TYPES.put("double[]", DOUBLE_ARRAY_CLASS);
98          COMMON_TYPES.put("float[]", FLOAT_ARRAY_CLASS);
99          COMMON_TYPES.put("int[]", INT_ARRAY_CLASS);
100         COMMON_TYPES.put("long[]", LONG_ARRAY_CLASS);
101         COMMON_TYPES.put("short[]", SHORT_ARRAY_CLASS);
102         COMMON_TYPES.put("boolean[]", BOOLEAN_ARRAY_CLASS);
103         COMMON_TYPES.put("java.lang.Object[]", OBJECT_ARRAY_CLASS);
104         COMMON_TYPES.put("java.lang.Boolean[]", BOOLEAN_OBJECT_ARRAY_CLASS);
105         COMMON_TYPES.put("java.lang.Byte[]", BYTE_OBJECT_ARRAY_CLASS);
106         COMMON_TYPES.put("java.lang.Character[]", CHARACTER_OBJECT_ARRAY_CLASS);
107         COMMON_TYPES.put("java.lang.Short[]", SHORT_OBJECT_ARRAY_CLASS);
108         COMMON_TYPES.put("java.lang.Integer[]", INTEGER_OBJECT_ARRAY_CLASS);
109         COMMON_TYPES.put("java.lang.Long[]", LONG_OBJECT_ARRAY_CLASS);
110         COMMON_TYPES.put("java.lang.Float[]", FLOAT_OBJECT_ARRAY_CLASS);
111         COMMON_TYPES.put("java.lang.Double[]", DOUBLE_OBJECT_ARRAY_CLASS);
112         COMMON_TYPES.put("java.lang.String[]", STRING_OBJECT_ARRAY_CLASS);
113         // array of void is not a valid type
114     }
115 
116     /** utility class, do not instantiate */
117     private ClassUtils()
118     {
119         // utility class, disable instantiation
120     }
121 
122     //~ Methods ------------------------------------------------------------------------------------
123 
124     public synchronized static void addClassLoadingExtension(ClassLoaderExtension extension, boolean top)
125     {
126       /**
127        * now at the first look this looks somewhat strange
128        * but to get the best performance access we assign new native
129        * arrays to our static variable
130        * 
131        * we have to synchronized nevertheless because if two threads try to register
132        * loaders at the same time none of them should get lost
133        */
134         ClassLoaderExtension [] retVal = new ClassLoaderExtension[classLoadingExtensions.length+1];
135         ArrayList extensions = new ArrayList(classLoadingExtensions.length+1);
136 
137         if(!top)
138         {
139             extensions.addAll(Arrays.asList(classLoadingExtensions));
140         }
141         extensions.add(extension);
142         if(top)
143         {
144             extensions.addAll(Arrays.asList(classLoadingExtensions));
145         }    
146 
147         classLoadingExtensions = (ClassLoaderExtension []) extensions.toArray(retVal);
148     }
149 
150     /**
151      * Tries a Class.loadClass with the context class loader of the current thread first and
152      * automatically falls back to the ClassUtils class loader (i.e. the loader of the
153      * myfaces.jar lib) if necessary.
154      *
155      * @param type fully qualified name of a non-primitive non-array class
156      * @return the corresponding Class
157      * @throws NullPointerException if type is null
158      * @throws ClassNotFoundException
159      */
160     public static Class classForName(String type)
161         throws ClassNotFoundException
162     {
163         //we now assign the array to safekeep the reference on
164         // the local variable stack, that way
165         //we can avoid synchronisation calls
166         ClassLoaderExtension [] loaderPlugins = classLoadingExtensions;
167         for (int cnt = 0; cnt < loaderPlugins.length; cnt ++)
168         {
169             ClassLoaderExtension extension = loaderPlugins[cnt];
170             Class retVal = extension.forName(type);
171             if(retVal != null)
172             {
173                 return retVal;
174             }
175         }
176 
177         if (type == null)
178         {
179             throw new NullPointerException("type");
180         }
181         try
182         {
183             // Try WebApp ClassLoader first
184             return Class.forName(type,
185                                  false, // do not initialize for faster startup
186                                  getContextClassLoader());
187         }
188         catch (ClassNotFoundException ignore)
189         {
190             // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
191             return Class.forName(type,
192                                  false, // do not initialize for faster startup
193                                  ClassUtils.class.getClassLoader());
194         }
195     }
196 
197     /**
198      * Same as {@link #classForName(String)}, but throws a RuntimeException
199      * (FacesException) instead of a ClassNotFoundException.
200      *
201      * @return the corresponding Class
202      * @throws NullPointerException if type is null
203      * @throws FacesException if class not found
204      */
205     public static Class simpleClassForName(String type)
206     {
207         return simpleClassForName(type, true);
208     }
209 
210     /**
211      * Same as {link {@link #simpleClassForName(String)}, but will only
212      * log the exception and rethrow a RunTimeException if logException is true.
213      *
214      * @param type
215      * @param logException - true to log/throw FacesException, false to avoid logging/throwing FacesException
216      * @return the corresponding Class
217      * @throws FacesException if class not found and logException is true
218      */
219     public static Class simpleClassForName(String type, boolean logException)
220     {
221         Class returnClass = null;
222         try
223         {
224             returnClass = classForName(type);
225         }
226         catch (ClassNotFoundException e)
227         {
228             if (logException)
229             {
230                 log.log(Level.SEVERE, "Class " + type + " not found", e);
231                 throw new FacesException(e);
232             }
233         }
234         return returnClass;
235     }
236 
237 
238     /**
239      * Similar as {@link #classForName(String)}, but also supports primitive types
240      * and arrays as specified for the JavaType element in the JavaServer Faces Config DTD.
241      *
242      * @param type fully qualified class name or name of a primitive type, both optionally
243      *             followed by "[]" to indicate an array type
244      * @return the corresponding Class
245      * @throws NullPointerException if type is null
246      * @throws ClassNotFoundException
247      */
248     public static Class javaTypeToClass(String type)
249         throws ClassNotFoundException
250     {
251         if (type == null)
252         {
253             throw new NullPointerException("type");
254         }
255 
256         // try common types and arrays of common types first
257         Class clazz = (Class) COMMON_TYPES.get(type);
258         if (clazz != null)
259         {
260             return clazz;
261         }
262 
263         int len = type.length();
264         if (len > 2 && type.charAt(len - 1) == ']' && type.charAt(len - 2) == '[')
265         {
266             String componentType = type.substring(0, len - 2);
267             Class componentTypeClass = classForName(componentType);
268             return Array.newInstance(componentTypeClass, 0).getClass();
269         }
270 
271         return classForName(type);
272         
273     }
274 
275     /**
276      * This method is similar to shared ClassUtils.javaTypeToClass,
277      * but the default package for the type is java.lang
278      *
279      * @param type
280      * @return
281      * @throws ClassNotFoundException
282      */
283     public static Class javaDefaultTypeToClass(String type)
284             throws ClassNotFoundException
285     {
286         if (type == null)
287         {
288             throw new NullPointerException("type");
289         }
290 
291         // try common types and arrays of common types first
292         Class clazz = (Class) ClassUtils.COMMON_TYPES.get(type);
293         if (clazz != null)
294         {
295             return clazz;
296         }
297 
298         int len = type.length();
299         if (len > 2 && type.charAt(len - 1) == ']' && type.charAt(len - 2) == '[')
300         {
301             String componentType = type.substring(0, len - 2);
302             Class componentTypeClass = ClassUtils.classForName(componentType);
303             return Array.newInstance(componentTypeClass, 0).getClass();
304         }
305 
306         if (type.indexOf('.') == -1)
307         {
308             type = "java.lang." + type;
309         }
310         return ClassUtils.classForName(type);
311     }
312 
313     /**
314      * Same as {@link #javaTypeToClass(String)}, but throws a RuntimeException
315      * (FacesException) instead of a ClassNotFoundException.
316      *
317      * @return the corresponding Class
318      * @throws NullPointerException if type is null
319      * @throws FacesException if class not found
320      */
321     public static Class simpleJavaTypeToClass(String type)
322     {
323         try
324         {
325             return javaTypeToClass(type);
326         }
327         catch (ClassNotFoundException e)
328         {
329             log.log(Level.SEVERE, "Class " + type + " not found", e);
330             throw new FacesException(e);
331         }
332     }
333 
334     public static URL getResource(String resource)
335     {
336         URL url = getContextClassLoader().getResource(resource);
337         if (url == null)
338         {
339             url = ClassUtils.class.getClassLoader().getResource(resource);
340         }
341         return url;
342     }
343 
344     public static InputStream getResourceAsStream(String resource)
345     {
346         InputStream stream = getContextClassLoader()
347                                 .getResourceAsStream(resource);
348         if (stream == null)
349         {
350             // fallback
351             stream = ClassUtils.class.getClassLoader().getResourceAsStream(resource);
352         }
353         return stream;
354     }
355 
356     /**
357      * @param resource       Name of resource(s) to find in classpath
358      * @param defaultObject  The default object to use to determine the class loader 
359      *                       (if none associated with current thread.)
360      * @return Iterator over URL Objects
361      */
362     public static Iterator getResources(String resource, Object defaultObject)
363     {
364         try
365         {
366             Enumeration resources = getCurrentLoader(defaultObject).getResources(resource);
367             List lst = new ArrayList();
368             while (resources.hasMoreElements())
369             {
370                 lst.add(resources.nextElement());
371             }
372             return lst.iterator();
373         }
374         catch (IOException e)
375         {
376             log.log(Level.SEVERE, e.getMessage(), e);
377             throw new FacesException(e);
378         }
379     }
380 
381 
382     public static Object newInstance(String type)
383         throws FacesException
384     {
385         if (type == null)
386         {
387             return null;
388         }
389         return newInstance(simpleClassForName(type));
390     }
391 
392     public static Object newInstance(String type, Class expectedType) throws FacesException
393     {
394         return newInstance(type, expectedType == null ? null : new Class[] {expectedType});
395     }
396 
397     public static Object newInstance(String type, Class[] expectedTypes)
398     {
399         if (type == null)
400         {
401             return null;
402         }
403         
404         Class clazzForName = simpleClassForName(type);
405         
406         if(expectedTypes != null)
407         {
408             for (int i = 0, size = expectedTypes.length; i < size; i++)
409             {
410                 if (!expectedTypes[i].isAssignableFrom(clazzForName))
411                 {
412                     throw new FacesException("'" + type + "' does not implement expected type '" + expectedTypes[i]
413                             + "'");
414                 }
415             }
416         }
417         
418         return newInstance(clazzForName);
419     }
420 
421     public static <T> T newInstance(Class<T> clazz)
422         throws FacesException
423     {
424         try
425         {
426             return clazz.newInstance();
427         }
428         catch(NoClassDefFoundError e)
429         {
430             log.log(Level.SEVERE, "Class : "+clazz.getName()+" not found.",e);
431             throw new FacesException(e);
432         }
433         catch (InstantiationException e)
434         {
435             log.log(Level.SEVERE, e.getMessage(), e);
436             throw new FacesException(e);
437         }
438         catch (IllegalAccessException e)
439         {
440             log.log(Level.SEVERE, e.getMessage(), e);
441             throw new FacesException(e);
442         }
443     }
444 
445     public static <T> T newInstance(Class<T> clazz,
446                                     Class<?>[] constructorArgClasses,
447                                     Object... constructorArgs) throws NoSuchMethodException
448     {
449         if (constructorArgs.length == 0)
450         {
451             // no args given - use normal newInstance()
452             return newInstance(clazz);
453         }
454 
455         // try to get a fitting constructor (throws NoSuchMethodException)
456         Constructor constructor = clazz.getConstructor(constructorArgClasses);
457 
458         try
459         {
460             // actually create instance
461             return (T) constructor.newInstance(constructorArgs);
462         }
463         catch (Exception e)
464         {
465             throw new FacesException(e);
466         }
467     }
468 
469     public static Object convertToType(Object value, Class desiredClass)
470     {
471         if (value == null)
472         {
473             return null;
474         }
475 
476         try
477         {
478             ExpressionFactory expFactory = FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
479             return expFactory.coerceToType(value, desiredClass);
480         }
481         catch (Exception e)
482         {
483             String message = "Cannot coerce " + value.getClass().getName()
484                              + " to " + desiredClass.getName();
485             log.log(Level.SEVERE, message, e);
486             throw new FacesException(message, e);
487         }
488     }
489 
490     /**
491      * Gets the ClassLoader associated with the current thread.  Returns the class loader associated with
492      * the specified default object if no context loader is associated with the current thread.
493      *
494      * @param defaultObject The default object to use to determine the class loader 
495      *        (if none associated with current thread.)
496      * @return ClassLoader
497      */
498     protected static ClassLoader getCurrentLoader(Object defaultObject)
499     {
500         ClassLoader loader = getContextClassLoader();
501         if(loader == null)
502         {
503             loader = defaultObject.getClass().getClassLoader();
504         }
505         return loader;
506     }
507     
508     /**
509      * Gets the ClassLoader associated with the current thread.  Includes a check for priviledges 
510      * against java2 security to ensure no security related exceptions are encountered. 
511      *
512      * @since 3.0.6
513      * @return ClassLoader
514      */
515     public static ClassLoader getContextClassLoader()
516     {
517         // call into the same method on ClassLoaderUtils.  no need for duplicate code maintenance. 
518         return ClassLoaderUtils.getContextClassLoader();
519     }
520     
521     /**
522      * Creates ApplicationObjects like NavigationHandler or StateManager and creates 
523      * the right wrapping chain of the ApplicationObjects known as the decorator pattern. 
524      * @param <T>
525      * @param interfaceClass The class from which the implementation has to inherit from.
526      * @param classNamesIterator All the class names of the actual ApplicationObject implementations
527      *                           from the faces-config.xml.
528      * @param defaultObject The default implementation for the given ApplicationObject.
529      * @return
530      */    
531     public static <T> T buildApplicationObject(Class<T> interfaceClass, 
532             Collection<String> classNamesIterator, T defaultObject)
533     {
534         return buildApplicationObject(interfaceClass, null, null, classNamesIterator, defaultObject);
535     }
536 
537     /**
538      * Creates ApplicationObjects like NavigationHandler or StateManager and creates 
539      * the right wrapping chain of the ApplicationObjects known as the decorator pattern. 
540      * @param <T>
541      * @param interfaceClass The class from which the implementation has to inherit from.
542      * @param extendedInterfaceClass A subclass of interfaceClass which specifies a more
543      *                               detailed implementation.
544      * @param extendedInterfaceWrapperClass A wrapper class for the case that you have an ApplicationObject
545      *                                      which only implements the interfaceClass but not the 
546      *                                      extendedInterfaceClass.
547      * @param classNamesIterator All the class names of the actual ApplicationObject implementations
548      *                           from the faces-config.xml.
549      * @param defaultObject The default implementation for the given ApplicationObject.
550      * @return
551      */
552     @SuppressWarnings("unchecked")
553     public static <T> T buildApplicationObject(Class<T> interfaceClass, Class<? extends T> extendedInterfaceClass,
554             Class<? extends T> extendedInterfaceWrapperClass,
555             Collection<String> classNamesIterator, T defaultObject)
556     {
557         T current = defaultObject;
558         
559 
560         for (String implClassName : classNamesIterator)
561         {
562             Class<? extends T> implClass = ClassUtils.simpleClassForName(implClassName);
563 
564             // check, if class is of expected interface type
565             if (!interfaceClass.isAssignableFrom(implClass))
566             {
567                 throw new IllegalArgumentException("Class " + implClassName + " is no " + interfaceClass.getName());
568             }
569 
570             if (current == null)
571             {
572                 // nothing to decorate
573                 current = (T) ClassUtils.newInstance(implClass);
574             }
575             else
576             {
577                 // let's check if class supports the decorator pattern
578                 T newCurrent = null;
579                 try
580                 {
581                     Constructor<? extends T> delegationConstructor = null;
582                     
583                     // first, if there is a extendedInterfaceClass,
584                     // try to find a constructor that uses that
585                     if (extendedInterfaceClass != null 
586                             && extendedInterfaceClass.isAssignableFrom(current.getClass()))
587                     {
588                         try
589                         {
590                             delegationConstructor = 
591                                     implClass.getConstructor(new Class[] {extendedInterfaceClass});
592                         }
593                         catch (NoSuchMethodException mnfe)
594                         {
595                             // just eat it
596                         }
597                     }
598                     if (delegationConstructor == null)
599                     {
600                         // try to find the constructor with the "normal" interfaceClass
601                         delegationConstructor = 
602                                 implClass.getConstructor(new Class[] {interfaceClass});
603                     }
604                     // impl class supports decorator pattern at this point
605                     try
606                     {
607                         // create new decorator wrapping current
608                         newCurrent = delegationConstructor.newInstance(new Object[] { current });
609                     }
610                     catch (InstantiationException e)
611                     {
612                         log.log(Level.SEVERE, e.getMessage(), e);
613                         throw new FacesException(e);
614                     }
615                     catch (IllegalAccessException e)
616                     {
617                         log.log(Level.SEVERE, e.getMessage(), e);
618                         throw new FacesException(e);
619                     }
620                     catch (InvocationTargetException e)
621                     {
622                         log.log(Level.SEVERE, e.getMessage(), e);
623                         throw new FacesException(e);
624                     }
625                 }
626                 catch (NoSuchMethodException e)
627                 {
628                     // no decorator pattern support
629                     newCurrent = (T) ClassUtils.newInstance(implClass);
630                 }
631                 
632                 current = wrapBackwardCompatible(interfaceClass, extendedInterfaceClass, 
633                                                     extendedInterfaceWrapperClass, current, newCurrent);
634             }
635         }
636 
637         return current;
638     }
639     
640     
641     /**
642      * Wrap an object using a backwards compatible wrapper if available
643      * @param interfaceClass The class from which the implementation has to inherit from.
644      * @param extendedInterfaceClass A subclass of interfaceClass which specifies a more
645      *                               detailed implementation.
646      * @param extendedInterfaceWrapperClass A wrapper class for the case that you have an ApplicationObject
647      *                                      which only implements the interfaceClass but not the 
648      *                                      extendedInterfaceClass.
649      * @param defaultObject The default implementation for the given ApplicationObject.
650      * @param newCurrent The new current object
651      * @return
652      */
653     public static <T> T wrapBackwardCompatible(Class<T> interfaceClass, Class<? extends T> extendedInterfaceClass,
654                                                Class<? extends T> extendedInterfaceWrapperClass, 
655                                                T defaultObject, T newCurrent)
656     {
657         
658         T current = newCurrent;
659         
660         // now we have a new current object (newCurrent)
661         // --> find out if it is assignable from extendedInterfaceClass
662         // and if not, wrap it in a backwards compatible wrapper (if available)
663         if (extendedInterfaceWrapperClass != null
664                 && !extendedInterfaceClass.isAssignableFrom(current.getClass()))
665         {
666             try
667             {
668                 Constructor<? extends T> wrapperConstructor
669                         = extendedInterfaceWrapperClass.getConstructor(
670                                 new Class[] {interfaceClass, extendedInterfaceClass});
671                 current = wrapperConstructor.newInstance(new Object[] {newCurrent, defaultObject});
672             }
673             catch (NoSuchMethodException e)
674             {
675                 log.log(Level.SEVERE, e.getMessage(), e);
676                 throw new FacesException(e);
677             }
678             catch (InstantiationException e)
679             {
680                 log.log(Level.SEVERE, e.getMessage(), e);
681                 throw new FacesException(e);
682             }
683             catch (IllegalAccessException e)
684             {
685                 log.log(Level.SEVERE, e.getMessage(), e);
686                 throw new FacesException(e);
687             }
688             catch (InvocationTargetException e)
689             {
690                 log.log(Level.SEVERE, e.getMessage(), e);
691                 throw new FacesException(e);
692             }
693         }
694         
695         return current;
696     }
697     
698 }