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      * Load a Class in the {@code java.*} namespace by name. Useful for peculiar scenarios typically involving
232      * Google App Engine.
233      *
234      * @param className The class name.
235      * @return The Class.
236      * @throws ClassNotFoundException if the Class could not be found.
237      */
238     public static Class<?> loadSystemClass(final String className) throws ClassNotFoundException {
239         try {
240             return Class.forName(className, true, ClassLoader.getSystemClassLoader());
241         } catch (final Throwable t) {
242             LOGGER.trace("Couldn't use SystemClassLoader. Trying Class.forName({}).", className, t);
243             return Class.forName(className);
244         }
245     }
246 
247     /**
248      * Loads and instantiates a Class using the default constructor.
249      *
250      * @param className The class name.
251      * @return new instance of the class.
252      * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
253      * @throws IllegalAccessException if the class can't be instantiated through a public constructor
254      * @throws InstantiationException if there was an exception whilst instantiating the class
255      * @throws NoSuchMethodException if there isn't a no-args constructor on the class
256      * @throws InvocationTargetException if there was an exception whilst constructing the class
257      */
258     public static Object newInstanceOf(final String className)
259             throws ClassNotFoundException,
260                    IllegalAccessException,
261                    InstantiationException,
262                    NoSuchMethodException,
263                    InvocationTargetException {
264         return LoaderUtil.newInstanceOf(className);
265     }
266 
267     /**
268      * Loads, instantiates, and casts a Class using the default constructor.
269      *
270      * @param className The class name.
271      * @param clazz The class to cast it to.
272      * @param <T> The type to cast it to.
273      * @return new instance of the class cast to {@code T}
274      * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
275      * @throws IllegalAccessException if the class can't be instantiated through a public constructor
276      * @throws InstantiationException if there was an exception whilst instantiating the class
277      * @throws NoSuchMethodException if there isn't a no-args constructor on the class
278      * @throws InvocationTargetException if there was an exception whilst constructing the class
279      * @throws ClassCastException if the constructed object isn't type compatible with {@code T}
280      */
281     public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz)
282             throws ClassNotFoundException,
283                    NoSuchMethodException,
284                    IllegalAccessException,
285                    InvocationTargetException,
286                    InstantiationException {
287         return LoaderUtil.newCheckedInstanceOf(className, clazz);
288     }
289 
290     /**
291      * Determines if a named Class can be loaded or not.
292      *
293      * @param className The class name.
294      * @return {@code true} if the class could be found or {@code false} otherwise.
295      */
296     public static boolean isClassAvailable(final String className) {
297         return LoaderUtil.isClassAvailable(className);
298     }
299 
300     public static boolean isJansiAvailable() {
301         return isClassAvailable("org.fusesource.jansi.AnsiRenderer");
302     }
303 
304 }