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