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