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.helpers;
18  
19  import java.io.InputStream;
20  import java.net.URL;
21  
22  import org.apache.logging.log4j.Logger;
23  import org.apache.logging.log4j.status.StatusLogger;
24  import org.apache.logging.log4j.util.PropertiesUtil;
25  
26  /**
27   * Load resources (or images) from various sources.
28   */
29  public final class Loader {
30  
31      private static boolean ignoreTCL = false;
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      static {
38          final String ignoreTCLProp = PropertiesUtil.getProperties().getStringProperty("log4j.ignoreTCL", null);
39          if (ignoreTCLProp != null) {
40              ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);
41          }
42      }
43  
44      /**
45       * Returns the ClassLoader to use.
46       * @return the ClassLoader.
47       */
48      public static ClassLoader getClassLoader() {
49  
50          return getClassLoader(Loader.class, null);
51      }
52  
53      public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
54  
55          ClassLoader loader1 = null;
56          try {
57              loader1 = getTCL();
58          } catch (final Exception ex) {
59              LOGGER.warn("Caught exception locating thread ClassLoader {}", ex.getMessage());
60          }
61          final ClassLoader loader2 = class1 == null ? null : class1.getClassLoader();
62          final ClassLoader loader3 = class2 == null ? null : class2.getClass().getClassLoader();
63  
64          if (isChild(loader1, loader2)) {
65              return isChild(loader1, loader3) ? loader1 : loader3;
66          }
67          return isChild(loader2, loader3) ? loader2 : loader3;
68      }
69  
70      /**
71       * This method will search for <code>resource</code> in different
72       * places. The search order is as follows:
73       * <p/>
74       * <ol>
75       * <p/>
76       * <p><li>Search for <code>resource</code> using the thread context
77       * class loader under Java2. If that fails, search for
78       * <code>resource</code> using the class loader that loaded this
79       * class (<code>Loader</code>). Under JDK 1.1, only the the class
80       * loader that loaded this class (<code>Loader</code>) is used.
81       * <p/>
82       * <p><li>Try one last time with
83       * <code>ClassLoader.getSystemResource(resource)</code>, that is is
84       * using the system class loader in JDK 1.2 and virtual machine's
85       * built-in class loader in JDK 1.1.
86       * <p/>
87       * </ol>
88       * @param resource The resource to load.
89       * @param defaultLoader The default ClassLoader.
90       * @return A URL to the resource.
91       */
92      public static URL getResource(final String resource, final ClassLoader defaultLoader) {
93          try {
94              ClassLoader classLoader = getTCL();
95              if (classLoader != null) {
96                  LOGGER.trace("Trying to find [" + resource + "] using context classloader "
97                          + classLoader + '.');
98                  final URL url = classLoader.getResource(resource);
99                  if (url != null) {
100                     return url;
101                 }
102             }
103 
104             // We could not find resource. Let us now try with the classloader that loaded this class.
105             classLoader = Loader.class.getClassLoader();
106             if (classLoader != null) {
107                 LOGGER.trace("Trying to find [" + resource + "] using " + classLoader + " class loader.");
108                 final URL url = classLoader.getResource(resource);
109                 if (url != null) {
110                     return url;
111                 }
112             }
113             // We could not find resource. Finally try with the default ClassLoader.
114             if (defaultLoader != null) {
115                 LOGGER.trace("Trying to find [" + resource + "] using " + defaultLoader + " class loader.");
116                 final URL url = defaultLoader.getResource(resource);
117                 if (url != null) {
118                     return url;
119                 }
120             }
121         } catch (final Throwable t) {
122             //
123             //  can't be InterruptedException or InterruptedIOException
124             //    since not declared, must be error or RuntimeError.
125             LOGGER.warn(TSTR, t);
126         }
127 
128         // Last ditch attempt: get the resource from the class path. It
129         // may be the case that clazz was loaded by the Extension class
130         // loader which the parent of the system class loader. Hence the
131         // code below.
132         LOGGER.trace("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
133         return ClassLoader.getSystemResource(resource);
134     }
135 
136     /**
137      * This method will search for <code>resource</code> in different
138      * places. The search order is as follows:
139      * <p/>
140      * <ol>
141      * <p/>
142      * <p><li>Search for <code>resource</code> using the thread context
143      * class loader under Java2. If that fails, search for
144      * <code>resource</code> using the class loader that loaded this
145      * class (<code>Loader</code>). Under JDK 1.1, only the the class
146      * loader that loaded this class (<code>Loader</code>) is used.
147      * <p/>
148      * <p><li>Try one last time with
149      * <code>ClassLoader.getSystemResource(resource)</code>, that is is
150      * using the system class loader in JDK 1.2 and virtual machine's
151      * built-in class loader in JDK 1.1.
152      * <p/>
153      * </ol>
154      * @param resource The resource to load.
155      * @param defaultLoader The default ClassLoader.
156      * @return An InputStream to read the resouce.
157      */
158     public static InputStream getResourceAsStream(final String resource, final ClassLoader defaultLoader) {
159         ClassLoader classLoader;
160         InputStream is;
161 
162         try {
163             classLoader = getTCL();
164             if (classLoader != null) {
165                 LOGGER.trace("Trying to find [" + resource + "] using context classloader " + classLoader + '.');
166                 is = classLoader.getResourceAsStream(resource);
167                 if (is != null) {
168                     return is;
169                 }
170             }
171 
172             // We could not find resource. Let us now try with the classloader that loaded this class.
173             classLoader = Loader.class.getClassLoader();
174             if (classLoader != null) {
175                 LOGGER.trace("Trying to find [" + resource + "] using " + classLoader + " class loader.");
176                 is = classLoader.getResourceAsStream(resource);
177                 if (is != null) {
178                     return is;
179                 }
180             }
181 
182             // We could not find resource. Finally try with the default ClassLoader.
183             if (defaultLoader != null) {
184                 LOGGER.trace("Trying to find [" + resource + "] using " + defaultLoader + " class loader.");
185                 is = defaultLoader.getResourceAsStream(resource);
186                 if (is != null) {
187                     return is;
188                 }
189             }
190         } catch (final Throwable t) {
191             //
192             //  can't be InterruptedException or InterruptedIOException
193             //    since not declared, must be error or RuntimeError.
194             LOGGER.warn(TSTR, t);
195         }
196 
197         // Last ditch attempt: get the resource from the class path. It
198         // may be the case that clazz was loaded by the Extension class
199         // loader which the parent of the system class loader. Hence the
200         // code below.
201         LOGGER.trace("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
202         return ClassLoader.getSystemResourceAsStream(resource);
203     }
204 
205     private static ClassLoader getTCL() {
206         ClassLoader cl;
207         if (System.getSecurityManager() == null) {
208             cl = Thread.currentThread().getContextClassLoader();
209         } else {
210             cl = java.security.AccessController.doPrivileged(
211                 new java.security.PrivilegedAction<ClassLoader>() {
212                     @Override
213                     public ClassLoader run() {
214                         return Thread.currentThread().getContextClassLoader();
215                     }
216                 }
217             );
218         }
219 
220         return cl;
221     }
222 
223     private static boolean isChild(final ClassLoader loader1, final ClassLoader loader2) {
224         if (loader1 != null && loader2 != null) {
225             ClassLoader parent = loader1.getParent();
226             while (parent != null && parent != loader2) {
227                 parent = parent.getParent();
228             }
229             return parent != null;
230         }
231         return loader1 != null;
232     }
233 
234     /**
235      * Load a Class by name.
236      * @param className The class name.
237      * @return The Class.
238      * @throws ClassNotFoundException if the Class could not be found.
239      */
240     public static Class<?> loadClass(final String className) throws ClassNotFoundException {
241         // Just call Class.forName(className) if we are instructed to ignore the TCL.
242         if (ignoreTCL) {
243             return Class.forName(className);
244         }
245         try {
246             return getTCL().loadClass(className);
247         } catch (final Throwable e) {
248             return Class.forName(className);
249         }
250     }
251 
252     private Loader() {
253     }
254 }