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.shiro.util;
20  
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  import java.io.InputStream;
25  import java.lang.annotation.Annotation;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.Method;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  
32  /**
33   * Utility method library used to conveniently interact with <code>Class</code>es, such as acquiring them from the
34   * application <code>ClassLoader</code>s and instantiating Objects from them.
35   *
36   * @since 0.1
37   */
38  public class ClassUtils {
39  
40      //TODO - complete JavaDoc
41  
42      /**
43       * Private internal log instance.
44       */
45      private static final Logger log = LoggerFactory.getLogger(ClassUtils.class);
46  
47      /**
48       * @since 1.0
49       */
50      private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
51          @Override
52          protected ClassLoader doGetClassLoader() throws Throwable {
53              return Thread.currentThread().getContextClassLoader();
54          }
55      };
56  
57      /**
58       * @since 1.0
59       */
60      private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
61          @Override
62          protected ClassLoader doGetClassLoader() throws Throwable {
63              return ClassUtils.class.getClassLoader();
64          }
65      };
66  
67      /**
68       * @since 1.0
69       */
70      private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
71          @Override
72          protected ClassLoader doGetClassLoader() throws Throwable {
73              return ClassLoader.getSystemClassLoader();
74          }
75      };
76  
77      /**
78       * Returns the specified resource by checking the current thread's
79       * {@link Thread#getContextClassLoader() context class loader}, then the
80       * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
81       * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using
82       * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}.
83       *
84       * @param name the name of the resource to acquire from the classloader(s).
85       * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any
86       *         of the three mentioned ClassLoaders.
87       * @since 0.9
88       */
89      public static InputStream getResourceAsStream(String name) {
90  
91          InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name);
92  
93          if (is == null) {
94              if (log.isTraceEnabled()) {
95                  log.trace("Resource [" + name + "] was not found via the thread context ClassLoader.  Trying the " +
96                          "current ClassLoader...");
97              }
98              is = CLASS_CL_ACCESSOR.getResourceStream(name);
99          }
100 
101         if (is == null) {
102             if (log.isTraceEnabled()) {
103                 log.trace("Resource [" + name + "] was not found via the current class loader.  Trying the " +
104                         "system/application ClassLoader...");
105             }
106             is = SYSTEM_CL_ACCESSOR.getResourceStream(name);
107         }
108 
109         if (is == null && log.isTraceEnabled()) {
110             log.trace("Resource [" + name + "] was not found via the thread context, current, or " +
111                     "system/application ClassLoaders.  All heuristics have been exhausted.  Returning null.");
112         }
113 
114         return is;
115     }
116 
117     /**
118      * Attempts to load the specified class name from the current thread's
119      * {@link Thread#getContextClassLoader() context class loader}, then the
120      * current ClassLoader (<code>ClassUtils.class.getClassLoader()</code>), then the system/application
121      * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order.  If any of them cannot locate
122      * the specified class, an <code>UnknownClassException</code> is thrown (our RuntimeException equivalent of
123      * the JRE's <code>ClassNotFoundException</code>.
124      *
125      * @param fqcn the fully qualified class name to load
126      * @return the located class
127      * @throws UnknownClassException if the class cannot be found.
128      */
129     public static Class forName(String fqcn) throws UnknownClassException {
130 
131         Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);
132 
133         if (clazz == null) {
134             if (log.isTraceEnabled()) {
135                 log.trace("Unable to load class named [" + fqcn +
136                         "] from the thread context ClassLoader.  Trying the current ClassLoader...");
137             }
138             clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
139         }
140 
141         if (clazz == null) {
142             if (log.isTraceEnabled()) {
143                 log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader.  " +
144                         "Trying the system/application ClassLoader...");
145             }
146             clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
147         }
148 
149         if (clazz == null) {
150             String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
151                     "system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.";
152             throw new UnknownClassException(msg);
153         }
154 
155         return clazz;
156     }
157 
158     public static boolean isAvailable(String fullyQualifiedClassName) {
159         try {
160             forName(fullyQualifiedClassName);
161             return true;
162         } catch (UnknownClassException e) {
163             return false;
164         }
165     }
166 
167     public static Object newInstance(String fqcn) {
168         return newInstance(forName(fqcn));
169     }
170 
171     public static Object newInstance(String fqcn, Object... args) {
172         return newInstance(forName(fqcn), args);
173     }
174 
175     public static Object newInstance(Class clazz) {
176         if (clazz == null) {
177             String msg = "Class method parameter cannot be null.";
178             throw new IllegalArgumentException(msg);
179         }
180         try {
181             return clazz.newInstance();
182         } catch (Exception e) {
183             throw new InstantiationException("Unable to instantiate class [" + clazz.getName() + "]", e);
184         }
185     }
186 
187     public static Object newInstance(Class clazz, Object... args) {
188         Class[] argTypes = new Class[args.length];
189         for (int i = 0; i < args.length; i++) {
190             argTypes[i] = args[i].getClass();
191         }
192         Constructor ctor = getConstructor(clazz, argTypes);
193         return instantiate(ctor, args);
194     }
195 
196     public static Constructor getConstructor(Class clazz, Class... argTypes) {
197         try {
198             return clazz.getConstructor(argTypes);
199         } catch (NoSuchMethodException e) {
200             throw new IllegalStateException(e);
201         }
202 
203     }
204 
205     public static Object instantiate(Constructor ctor, Object... args) {
206         try {
207             return ctor.newInstance(args);
208         } catch (Exception e) {
209             String msg = "Unable to instantiate Permission instance with constructor [" + ctor + "]";
210             throw new InstantiationException(msg, e);
211         }
212     }
213 
214     /**
215      *
216      * @param type
217      * @param annotation
218      * @return
219      * @since 1.3
220      */
221     public static List<Method> getAnnotatedMethods(final Class<?> type, final Class<? extends Annotation> annotation) {
222         final List<Method> methods = new ArrayList<Method>();
223         Class<?> clazz = type;
224         while (!Object.class.equals(clazz)) {
225             Method[] currentClassMethods = clazz.getDeclaredMethods();
226             for (final Method method : currentClassMethods) {
227                 if (annotation == null || method.isAnnotationPresent(annotation)) {
228                     methods.add(method);
229                 }
230             }
231             // move to the upper class in the hierarchy in search for more methods
232             clazz = clazz.getSuperclass();
233         }
234         return methods;
235     }
236 
237     /**
238      * @since 1.0
239      */
240     private static interface ClassLoaderAccessor {
241         Class loadClass(String fqcn);
242         InputStream getResourceStream(String name);
243     }
244 
245     /**
246      * @since 1.0
247      */
248     private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor {
249 
250         public Class loadClass(String fqcn) {
251             Class clazz = null;
252             ClassLoader cl = getClassLoader();
253             if (cl != null) {
254                 try {
255                     clazz = cl.loadClass(fqcn);
256                 } catch (ClassNotFoundException e) {
257                     if (log.isTraceEnabled()) {
258                         log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]");
259                     }
260                 }
261             }
262             return clazz;
263         }
264 
265         public InputStream getResourceStream(String name) {
266             InputStream is = null;
267             ClassLoader cl = getClassLoader();
268             if (cl != null) {
269                 is = cl.getResourceAsStream(name);
270             }
271             return is;
272         }
273 
274         protected final ClassLoader getClassLoader() {
275             try {
276                 return doGetClassLoader();
277             } catch (Throwable t) {
278                 if (log.isDebugEnabled()) {
279                     log.debug("Unable to acquire ClassLoader.", t);
280                 }
281             }
282             return null;
283         }
284 
285         protected abstract ClassLoader doGetClassLoader() throws Throwable;
286     }
287 }