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