View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.util;
18  
19  
20  import java.io.InputStream;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.ReflectPermission;
23  import java.net.URL;
24  
25  import org.apache.logging.log4j.Logger;
26  import org.apache.logging.log4j.status.StatusLogger;
27  import org.apache.logging.log4j.util.LoaderUtil;
28  
29  /**
30   * Load resources (or images) from various sources.
31   */
32  public final class Loader {
33  
34      private static final Logger LOGGER = StatusLogger.getLogger();
35  
36      private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
37  
38      static {
39          final SecurityManager sm = System.getSecurityManager();
40          if (sm != null) {
41              sm.checkPermission(new RuntimePermission("getStackTrace"));
42              sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
43          }
44      }
45  
46      /**
47       * Returns the ClassLoader to use.
48       * @return the ClassLoader.
49       */
50      public static ClassLoader getClassLoader() {
51          return getClassLoader(Loader.class, null);
52      }
53  
54      /**
55       * Returns the ClassLoader of current thread if possible, or falls back to the system ClassLoader if none is
56       * available.
57       *
58       * @return the TCCL.
59       * @see org.apache.logging.log4j.util.LoaderUtil#getThreadContextClassLoader()
60       */
61      public static ClassLoader getThreadContextClassLoader() {
62          return LoaderUtil.getThreadContextClassLoader();
63      }
64  
65      // TODO: this method could use some explanation
66      public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
67          final ClassLoader threadContextClassLoader = getTcl();
68          final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
69          final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
70  
71          if (isChild(threadContextClassLoader, loader1)) {
72              return isChild(threadContextClassLoader, loader2) ? threadContextClassLoader : loader2;
73          }
74          return isChild(loader1, loader2) ? loader1 : loader2;
75      }
76  
77      /**
78       * This method will search for {@code resource} in different
79       * places. The search order is as follows:
80       *
81       * <ol>
82       *
83       * <li>Search for {@code resource} using the thread context
84       * class loader under Java2. If that fails, search for
85       * {@code resource} using the class loader that loaded this
86       * class ({@code Loader}). Under JDK 1.1, only the the class
87       * loader that loaded this class ({@code Loader}) is used.</li>
88       * <li>Try one last time with
89       * {@code ClassLoader.getSystemResource(resource)}, that is is
90       * using the system class loader in JDK 1.2 and virtual machine's
91       * built-in class loader in JDK 1.1.</li>
92       * </ol>
93       * @param resource The resource to load.
94       * @param defaultLoader The default ClassLoader.
95       * @return A URL to the resource.
96       */
97      public static URL getResource(final String resource, final ClassLoader defaultLoader) {
98          try {
99              ClassLoader classLoader = getTcl();
100             if (classLoader != null) {
101                 LOGGER.trace("Trying to find [{}] using context class loader {}.", resource, classLoader);
102                 final URL url = classLoader.getResource(resource);
103                 if (url != null) {
104                     return url;
105                 }
106             }
107 
108             // We could not find resource. Let us now try with the classloader that loaded this class.
109             classLoader = Loader.class.getClassLoader();
110             if (classLoader != null) {
111                 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, classLoader);
112                 final URL url = classLoader.getResource(resource);
113                 if (url != null) {
114                     return url;
115                 }
116             }
117             // We could not find resource. Finally try with the default ClassLoader.
118             if (defaultLoader != null) {
119                 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, defaultLoader);
120                 final URL url = defaultLoader.getResource(resource);
121                 if (url != null) {
122                     return url;
123                 }
124             }
125         } catch (final Throwable t) {
126             //
127             //  can't be InterruptedException or InterruptedIOException
128             //    since not declared, must be error or RuntimeError.
129             LOGGER.warn(TSTR, t);
130         }
131 
132         // Last ditch attempt: get the resource from the class path. It
133         // may be the case that clazz was loaded by the Extension class
134         // loader which the parent of the system class loader. Hence the
135         // code below.
136         LOGGER.trace("Trying to find [{}] using ClassLoader.getSystemResource().", resource);
137         return ClassLoader.getSystemResource(resource);
138     }
139 
140     /**
141      * This method will search for {@code resource} in different
142      * places. The search order is as follows:
143      *
144      * <ol>
145      * <li>Search for {@code resource} using the thread context
146      * class loader under Java2. If that fails, search for
147      * {@code resource} using the class loader that loaded this
148      * class ({@code Loader}). Under JDK 1.1, only the the class
149      * loader that loaded this class ({@code Loader}) is used.</li>
150      * <li>Try one last time with
151      * {@code ClassLoader.getSystemResource(resource)}, that is is
152      * using the system class loader in JDK 1.2 and virtual machine's
153      * built-in class loader in JDK 1.1.</li>
154      * </ol>
155      * @param resource The resource to load.
156      * @param defaultLoader The default ClassLoader.
157      * @return An InputStream to read the resouce.
158      */
159     public static InputStream getResourceAsStream(final String resource, final ClassLoader defaultLoader) {
160         try {
161             ClassLoader classLoader = getTcl();
162             InputStream is;
163             if (classLoader != null) {
164                 LOGGER.trace("Trying to find [{}] using context class loader {}.", resource, classLoader);
165                 is = classLoader.getResourceAsStream(resource);
166                 if (is != null) {
167                     return is;
168                 }
169             }
170 
171             // We could not find resource. Let us now try with the classloader that loaded this class.
172             classLoader = Loader.class.getClassLoader();
173             if (classLoader != null) {
174                 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, classLoader);
175                 is = classLoader.getResourceAsStream(resource);
176                 if (is != null) {
177                     return is;
178                 }
179             }
180 
181             // We could not find resource. Finally try with the default ClassLoader.
182             if (defaultLoader != null) {
183                 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, defaultLoader);
184                 is = defaultLoader.getResourceAsStream(resource);
185                 if (is != null) {
186                     return is;
187                 }
188             }
189         } catch (final Throwable t) {
190             //
191             //  can't be InterruptedException or InterruptedIOException
192             //    since not declared, must be error or RuntimeError.
193             LOGGER.warn(TSTR, t);
194         }
195 
196         // Last ditch attempt: get the resource from the class path. It
197         // may be the case that clazz was loaded by the Extension class
198         // loader which the parent of the system class loader. Hence the
199         // code below.
200         LOGGER.trace("Trying to find [{}] using ClassLoader.getSystemResource().", resource);
201         return ClassLoader.getSystemResourceAsStream(resource);
202     }
203 
204     private static ClassLoader getTcl() {
205         return LoaderUtil.getThreadContextClassLoader();
206     }
207 
208     /**
209      * Determines if one ClassLoader is a child of another ClassLoader. Note that a {@code null} ClassLoader is
210      * interpreted as the system ClassLoader as per convention.
211      *
212      * @param loader1 the ClassLoader to check for childhood.
213      * @param loader2 the ClassLoader to check for parenthood.
214      * @return {@code true} if the first ClassLoader is a strict descendant of the second ClassLoader.
215      */
216     private static boolean isChild(final ClassLoader loader1, final ClassLoader loader2) {
217         if (loader1 != null && loader2 != null) {
218             ClassLoader parent = loader1.getParent();
219             while (parent != null && parent != loader2) {
220                 parent = parent.getParent();
221             }
222             // once parent is null, we're at the system CL, which would indicate they have separate ancestry
223             return parent != null;
224         }
225         return loader1 != null;
226     }
227 
228     /**
229      * Load a Class by name. Note that unlike {@link ClassLoader#loadClass(String) ClassLoader.loadClass}, this method
230      * will initialize the class as well if it hasn't been already. This is equivalent to the calling the
231      * {@link ClassLoader#loadClass(String, boolean) protected version} with the second parameter equal to {@code true}.
232      *
233      * @param className The class name.
234      * @return The Class.
235      * @throws ClassNotFoundException if the Class could not be found.
236      */
237     public static Class<?> loadClass(final String className) throws ClassNotFoundException {
238         return LoaderUtil.loadClass(className);
239     }
240 
241     /**
242      * Loads and initializes a named Class using a given ClassLoader.
243      *
244      * @param className The class name.
245      * @param loader The class loader.
246      * @return The class.
247      * @throws ClassNotFoundException if the class could not be found.
248      */
249     public static Class<?> initializeClass(final String className, final ClassLoader loader)
250             throws ClassNotFoundException {
251         return Class.forName(className, true, loader);
252     }
253 
254     /**
255      * Load a Class in the {@code java.*} namespace by name. Useful for peculiar scenarios typically involving
256      * Google App Engine.
257      *
258      * @param className The class name.
259      * @return The Class.
260      * @throws ClassNotFoundException if the Class could not be found.
261      */
262     public static Class<?> loadSystemClass(final String className) throws ClassNotFoundException {
263         try {
264             return Class.forName(className, true, ClassLoader.getSystemClassLoader());
265         } catch (final Throwable t) {
266             LOGGER.trace("Couldn't use SystemClassLoader. Trying Class.forName({}).", className, t);
267             return Class.forName(className);
268         }
269     }
270 
271     /**
272      * Loads and instantiates a Class using the default constructor.
273      *
274      * @param className The class name.
275      * @return new instance of the class.
276      * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
277      * @throws IllegalAccessException if the class can't be instantiated through a public constructor
278      * @throws InstantiationException if there was an exception whilst instantiating the class
279      * @throws NoSuchMethodException if there isn't a no-args constructor on the class
280      * @throws InvocationTargetException if there was an exception whilst constructing the class
281      */
282     public static Object newInstanceOf(final String className)
283             throws ClassNotFoundException,
284                    IllegalAccessException,
285                    InstantiationException,
286                    NoSuchMethodException,
287                    InvocationTargetException {
288         return LoaderUtil.newInstanceOf(className);
289     }
290 
291     /**
292      * Loads, instantiates, and casts a Class using the default constructor.
293      *
294      * @param className The class name.
295      * @param clazz The class to cast it to.
296      * @param <T> The type to cast it to.
297      * @return new instance of the class cast to {@code T}
298      * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
299      * @throws IllegalAccessException if the class can't be instantiated through a public constructor
300      * @throws InstantiationException if there was an exception whilst instantiating the class
301      * @throws NoSuchMethodException if there isn't a no-args constructor on the class
302      * @throws InvocationTargetException if there was an exception whilst constructing the class
303      * @throws ClassCastException if the constructed object isn't type compatible with {@code T}
304      */
305     public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz)
306             throws ClassNotFoundException,
307                    NoSuchMethodException,
308                    IllegalAccessException,
309                    InvocationTargetException,
310                    InstantiationException {
311         return LoaderUtil.newCheckedInstanceOf(className, clazz);
312     }
313 
314     /**
315      * Determines if a named Class can be loaded or not.
316      *
317      * @param className The class name.
318      * @return {@code true} if the class could be found or {@code false} otherwise.
319      */
320     public static boolean isClassAvailable(final String className) {
321         try {
322             final Class<?> clazz = loadClass(className);
323             return clazz != null;
324         } catch (final ClassNotFoundException e) {
325             return false;
326         } catch (final Throwable e) {
327             LOGGER.trace("Unknown error checking for existence of class [{}].", className, e);
328             return false;
329         }
330     }
331 
332     private Loader() {
333     }
334 }