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