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 }