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 }