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