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 * Load a Class in the {@code java.*} namespace by name. Useful for peculiar scenarios typically involving 232 * Google App Engine. 233 * 234 * @param className The class name. 235 * @return The Class. 236 * @throws ClassNotFoundException if the Class could not be found. 237 */ 238 public static Class<?> loadSystemClass(final String className) throws ClassNotFoundException { 239 try { 240 return Class.forName(className, true, ClassLoader.getSystemClassLoader()); 241 } catch (final Throwable t) { 242 LOGGER.trace("Couldn't use SystemClassLoader. Trying Class.forName({}).", className, t); 243 return Class.forName(className); 244 } 245 } 246 247 /** 248 * Loads and instantiates a Class using the default constructor. 249 * 250 * @param className The class name. 251 * @return new instance of the class. 252 * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders 253 * @throws IllegalAccessException if the class can't be instantiated through a public constructor 254 * @throws InstantiationException if there was an exception whilst instantiating the class 255 * @throws NoSuchMethodException if there isn't a no-args constructor on the class 256 * @throws InvocationTargetException if there was an exception whilst constructing the class 257 */ 258 public static Object newInstanceOf(final String className) 259 throws ClassNotFoundException, 260 IllegalAccessException, 261 InstantiationException, 262 NoSuchMethodException, 263 InvocationTargetException { 264 return LoaderUtil.newInstanceOf(className); 265 } 266 267 /** 268 * Loads, instantiates, and casts a Class using the default constructor. 269 * 270 * @param className The class name. 271 * @param clazz The class to cast it to. 272 * @param <T> The type to cast it to. 273 * @return new instance of the class cast to {@code T} 274 * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders 275 * @throws IllegalAccessException if the class can't be instantiated through a public constructor 276 * @throws InstantiationException if there was an exception whilst instantiating the class 277 * @throws NoSuchMethodException if there isn't a no-args constructor on the class 278 * @throws InvocationTargetException if there was an exception whilst constructing the class 279 * @throws ClassCastException if the constructed object isn't type compatible with {@code T} 280 */ 281 public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz) 282 throws ClassNotFoundException, 283 NoSuchMethodException, 284 IllegalAccessException, 285 InvocationTargetException, 286 InstantiationException { 287 return LoaderUtil.newCheckedInstanceOf(className, clazz); 288 } 289 290 /** 291 * Determines if a named Class can be loaded or not. 292 * 293 * @param className The class name. 294 * @return {@code true} if the class could be found or {@code false} otherwise. 295 */ 296 public static boolean isClassAvailable(final String className) { 297 return LoaderUtil.isClassAvailable(className); 298 } 299 300 public static boolean isJansiAvailable() { 301 return isClassAvailable("org.fusesource.jansi.AnsiRenderer"); 302 } 303 304 }