001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 package org.apache.logging.log4j.core.util; 018 019 020 import java.io.InputStream; 021 import java.lang.reflect.InvocationTargetException; 022 import java.lang.reflect.ReflectPermission; 023 import java.net.URL; 024 import java.security.AccessController; 025 import java.security.PrivilegedAction; 026 027 import org.apache.logging.log4j.Logger; 028 import org.apache.logging.log4j.status.StatusLogger; 029 import org.apache.logging.log4j.util.PropertiesUtil; 030 031 /** 032 * Load resources (or images) from various sources. 033 */ 034 public final class Loader { 035 036 private static boolean ignoreTCL = false; 037 038 private static final Logger LOGGER = StatusLogger.getLogger(); 039 040 private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous."; 041 042 private static final PrivilegedAction<ClassLoader> THREAD_CONTEXT_CLASS_LOADER_GETTER = 043 new ThreadContextClassLoaderGetter(); 044 045 static { 046 final String ignoreTCLProp = PropertiesUtil.getProperties().getStringProperty("log4j.ignoreTCL", null); 047 if (ignoreTCLProp != null) { 048 ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true); 049 } 050 final SecurityManager sm = System.getSecurityManager(); 051 if (sm != null) { 052 sm.checkPermission(new RuntimePermission("getClassLoader")); 053 sm.checkPermission(new RuntimePermission("getStackTrace")); 054 sm.checkPermission(new ReflectPermission("suppressAccessChecks")); 055 } 056 } 057 058 /** 059 * Returns the ClassLoader to use. 060 * @return the ClassLoader. 061 */ 062 public static ClassLoader getClassLoader() { 063 064 return getClassLoader(Loader.class, null); 065 } 066 067 /** 068 * Returns the ClassLoader of current thread if possible, or falls back to the system ClassLoader if none is 069 * available. 070 * 071 * @return the TCCL. 072 */ 073 public static ClassLoader getThreadContextClassLoader() { 074 return getTcl(); 075 } 076 077 // TODO: this method could use some explanation 078 public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) { 079 080 ClassLoader threadContextClassLoader = null; 081 try { 082 threadContextClassLoader = getTcl(); 083 } catch (final Exception ex) { 084 LOGGER.warn("Caught exception locating thread ClassLoader {}", ex.getMessage()); 085 } 086 final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader(); 087 final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader(); 088 089 if (isChild(threadContextClassLoader, loader1)) { 090 return isChild(threadContextClassLoader, loader2) ? threadContextClassLoader : loader2; 091 } 092 return isChild(loader1, loader2) ? loader1 : loader2; 093 } 094 095 /** 096 * This method will search for {@code resource} in different 097 * places. The search order is as follows: 098 * <p/> 099 * <ol> 100 * <p/> 101 * <p><li>Search for {@code resource} using the thread context 102 * class loader under Java2. If that fails, search for 103 * {@code resource} using the class loader that loaded this 104 * class ({@code Loader}). Under JDK 1.1, only the the class 105 * loader that loaded this class ({@code Loader}) is used. 106 * <p/> 107 * <p><li>Try one last time with 108 * {@code ClassLoader.getSystemResource(resource)}, that is is 109 * using the system class loader in JDK 1.2 and virtual machine's 110 * built-in class loader in JDK 1.1. 111 * <p/> 112 * </ol> 113 * @param resource The resource to load. 114 * @param defaultLoader The default ClassLoader. 115 * @return A URL to the resource. 116 */ 117 public static URL getResource(final String resource, final ClassLoader defaultLoader) { 118 try { 119 ClassLoader classLoader = getTcl(); 120 if (classLoader != null) { 121 LOGGER.trace("Trying to find [{}] using context class loader {}.", resource, classLoader); 122 final URL url = classLoader.getResource(resource); 123 if (url != null) { 124 return url; 125 } 126 } 127 128 // We could not find resource. Let us now try with the classloader that loaded this class. 129 classLoader = Loader.class.getClassLoader(); 130 if (classLoader != null) { 131 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, classLoader); 132 final URL url = classLoader.getResource(resource); 133 if (url != null) { 134 return url; 135 } 136 } 137 // We could not find resource. Finally try with the default ClassLoader. 138 if (defaultLoader != null) { 139 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, defaultLoader); 140 final URL url = defaultLoader.getResource(resource); 141 if (url != null) { 142 return url; 143 } 144 } 145 } catch (final Throwable t) { 146 // 147 // can't be InterruptedException or InterruptedIOException 148 // since not declared, must be error or RuntimeError. 149 LOGGER.warn(TSTR, t); 150 } 151 152 // Last ditch attempt: get the resource from the class path. It 153 // may be the case that clazz was loaded by the Extension class 154 // loader which the parent of the system class loader. Hence the 155 // code below. 156 LOGGER.trace("Trying to find [{}] using ClassLoader.getSystemResource().", resource); 157 return ClassLoader.getSystemResource(resource); 158 } 159 160 /** 161 * This method will search for {@code resource} in different 162 * places. The search order is as follows: 163 * <p/> 164 * <ol> 165 * <p/> 166 * <p><li>Search for {@code resource} using the thread context 167 * class loader under Java2. If that fails, search for 168 * {@code resource} using the class loader that loaded this 169 * class ({@code Loader}). Under JDK 1.1, only the the class 170 * loader that loaded this class ({@code Loader}) is used. 171 * <p/> 172 * <p><li>Try one last time with 173 * {@code ClassLoader.getSystemResource(resource)}, that is is 174 * using the system class loader in JDK 1.2 and virtual machine's 175 * built-in class loader in JDK 1.1. 176 * <p/> 177 * </ol> 178 * @param resource The resource to load. 179 * @param defaultLoader The default ClassLoader. 180 * @return An InputStream to read the resouce. 181 */ 182 public static InputStream getResourceAsStream(final String resource, final ClassLoader defaultLoader) { 183 try { 184 ClassLoader classLoader = getTcl(); 185 InputStream is; 186 if (classLoader != null) { 187 LOGGER.trace("Trying to find [{}] using context class loader {}.", resource, classLoader); 188 is = classLoader.getResourceAsStream(resource); 189 if (is != null) { 190 return is; 191 } 192 } 193 194 // We could not find resource. Let us now try with the classloader that loaded this class. 195 classLoader = Loader.class.getClassLoader(); 196 if (classLoader != null) { 197 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, classLoader); 198 is = classLoader.getResourceAsStream(resource); 199 if (is != null) { 200 return is; 201 } 202 } 203 204 // We could not find resource. Finally try with the default ClassLoader. 205 if (defaultLoader != null) { 206 LOGGER.trace("Trying to find [{}] using {} class loader.", resource, defaultLoader); 207 is = defaultLoader.getResourceAsStream(resource); 208 if (is != null) { 209 return is; 210 } 211 } 212 } catch (final Throwable t) { 213 // 214 // can't be InterruptedException or InterruptedIOException 215 // since not declared, must be error or RuntimeError. 216 LOGGER.warn(TSTR, t); 217 } 218 219 // Last ditch attempt: get the resource from the class path. It 220 // may be the case that clazz was loaded by the Extension class 221 // loader which the parent of the system class loader. Hence the 222 // code below. 223 LOGGER.trace("Trying to find [{}] using ClassLoader.getSystemResource().", resource); 224 return ClassLoader.getSystemResourceAsStream(resource); 225 } 226 227 private static ClassLoader getTcl() { 228 return System.getSecurityManager() == null 229 ? THREAD_CONTEXT_CLASS_LOADER_GETTER.run() 230 : AccessController.doPrivileged(THREAD_CONTEXT_CLASS_LOADER_GETTER); 231 } 232 233 private static class ThreadContextClassLoaderGetter implements PrivilegedAction<ClassLoader> { 234 @Override 235 public ClassLoader run() { 236 final ClassLoader cl = Thread.currentThread().getContextClassLoader(); 237 // if the TCCL is null, that means we're using the system CL 238 return cl == null ? ClassLoader.getSystemClassLoader() : cl; 239 } 240 } 241 242 /** 243 * Determines if one ClassLoader is a child of another ClassLoader. Note that a {@code null} ClassLoader is 244 * interpreted as the system ClassLoader as per convention. 245 * 246 * @param loader1 the ClassLoader to check for childhood. 247 * @param loader2 the ClassLoader to check for parenthood. 248 * @return {@code true} if the first ClassLoader is a strict descendant of the second ClassLoader. 249 */ 250 private static boolean isChild(final ClassLoader loader1, final ClassLoader loader2) { 251 if (loader1 != null && loader2 != null) { 252 ClassLoader parent = loader1.getParent(); 253 while (parent != null && parent != loader2) { 254 parent = parent.getParent(); 255 } 256 // once parent is null, we're at the system CL, which would indicate they have separate ancestry 257 return parent != null; 258 } 259 return loader1 != null; 260 } 261 262 /** 263 * Load a Class by name. Note that unlike {@link ClassLoader#loadClass(String) ClassLoader.loadClass}, this method 264 * will initialize the class as well if it hasn't been already. This is equivalent to the calling the 265 * {@link ClassLoader#loadClass(String, boolean) protected version} with the second parameter equal to {@code true}. 266 * 267 * @param className The class name. 268 * @return The Class. 269 * @throws ClassNotFoundException if the Class could not be found. 270 */ 271 public static Class<?> loadClass(final String className) throws ClassNotFoundException { 272 // Just call Class.forName(className) if we are instructed to ignore the TCL. 273 if (ignoreTCL) { 274 LOGGER.trace("Ignoring TCCL. Trying Class.forName({}).", className); 275 return loadClassWithDefaultClassLoader(className); 276 } 277 try { 278 LOGGER.trace("Trying TCCL for class {}.", className); 279 // using the TCCL should work the same as the default ClassLoader (i.e., init or not) 280 return Class.forName(className, true, getTcl()); 281 } catch (final Throwable e) { 282 LOGGER.trace("TCCL didn't work for class {}: {}.", className, e.toString()); 283 return loadClassWithDefaultClassLoader(className); 284 } 285 } 286 287 private static Class<?> loadClassWithDefaultClassLoader(final String className) throws ClassNotFoundException { 288 return Class.forName(className); 289 } 290 291 /** 292 * Loads and initializes a named Class using a given ClassLoader. 293 * 294 * @param className The class name. 295 * @param loader The class loader. 296 * @return The class. 297 * @throws ClassNotFoundException if the class could not be found. 298 */ 299 public static Class<?> initializeClass(final String className, final ClassLoader loader) 300 throws ClassNotFoundException { 301 return Class.forName(className, true, loader); 302 } 303 304 /** 305 * Load a Class in the {@code java.*} namespace by name. Useful for peculiar scenarios typically involving 306 * Google App Engine. 307 * 308 * @param className The class name. 309 * @return The Class. 310 * @throws ClassNotFoundException if the Class could not be found. 311 */ 312 public static Class<?> loadSystemClass(final String className) throws ClassNotFoundException { 313 try { 314 return Class.forName(className, true, ClassLoader.getSystemClassLoader()); 315 } catch (final Throwable t) { 316 LOGGER.trace("Couldn't use SystemClassLoader. Trying Class.forName({}).", className, t); 317 return Class.forName(className); 318 } 319 } 320 321 /** 322 * Loads and instantiates a Class using the default constructor. 323 * 324 * @param className The class name. 325 * @return new instance of the class. 326 * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders 327 * @throws IllegalAccessException if the class can't be instantiated through a public constructor 328 * @throws InstantiationException if there was an exception whilst instantiating the class 329 * @throws NoSuchMethodException if there isn't a no-args constructor on the class 330 * @throws InvocationTargetException if there was an exception whilst constructing the class 331 */ 332 public static Object newInstanceOf(final String className) 333 throws ClassNotFoundException, 334 IllegalAccessException, 335 InstantiationException, 336 NoSuchMethodException, 337 InvocationTargetException { 338 final Class<?> clazz = loadClass(className); 339 try { 340 return clazz.getConstructor().newInstance(); 341 } catch (final NoSuchMethodException e) { 342 // try the default-default constructor 343 //noinspection ClassNewInstance 344 return clazz.newInstance(); 345 } 346 } 347 348 /** 349 * Loads, instantiates, and casts a Class using the default constructor. 350 * 351 * @param className The class name. 352 * @param clazz The class to cast it to. 353 * @param <T> The type to cast it to. 354 * @return new instance of the class cast to {@code T} 355 * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders 356 * @throws IllegalAccessException if the class can't be instantiated through a public constructor 357 * @throws InstantiationException if there was an exception whilst instantiating the class 358 * @throws NoSuchMethodException if there isn't a no-args constructor on the class 359 * @throws InvocationTargetException if there was an exception whilst constructing the class 360 * @throws ClassCastException if the constructed object isn't type compatible with {@code T} 361 */ 362 public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz) 363 throws ClassNotFoundException, 364 NoSuchMethodException, 365 IllegalAccessException, 366 InvocationTargetException, 367 InstantiationException { 368 return clazz.cast(newInstanceOf(className)); 369 } 370 371 /** 372 * Determines if a named Class can be loaded or not. 373 * 374 * @param className The class name. 375 * @return {@code true} if the class could be found or {@code false} otherwise. 376 */ 377 public static boolean isClassAvailable(final String className) { 378 try { 379 final Class<?> clazz = loadClass(className); 380 return clazz != null; 381 } catch (final ClassNotFoundException e) { 382 return false; 383 } catch (final Throwable e) { 384 LOGGER.trace("Unknown error checking for existence of class [{}].", className, e); 385 return false; 386 } 387 } 388 389 private Loader() { 390 } 391 }