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