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