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 */
017package org.apache.logging.log4j.core.util;
018
019
020import java.io.InputStream;
021import java.lang.reflect.InvocationTargetException;
022import java.lang.reflect.ReflectPermission;
023import java.net.URL;
024
025import org.apache.logging.log4j.Logger;
026import org.apache.logging.log4j.status.StatusLogger;
027import org.apache.logging.log4j.util.LoaderUtil;
028
029/**
030 * Load resources (or images) from various sources.
031 */
032public final class Loader {
033
034    private static final Logger LOGGER = StatusLogger.getLogger();
035
036    private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
037
038    static {
039        final SecurityManager sm = System.getSecurityManager();
040        if (sm != null) {
041            sm.checkPermission(new RuntimePermission("getStackTrace"));
042            sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
043        }
044    }
045
046    /**
047     * Returns the ClassLoader to use.
048     * @return the ClassLoader.
049     */
050    public static ClassLoader getClassLoader() {
051        return getClassLoader(Loader.class, null);
052    }
053
054    /**
055     * Returns the ClassLoader of current thread if possible, or falls back to the system ClassLoader if none is
056     * available.
057     *
058     * @return the TCCL.
059     * @see org.apache.logging.log4j.util.LoaderUtil#getThreadContextClassLoader()
060     */
061    public static ClassLoader getThreadContextClassLoader() {
062        return LoaderUtil.getThreadContextClassLoader();
063    }
064
065    // TODO: this method could use some explanation
066    public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
067        final ClassLoader threadContextClassLoader = getTcl();
068        final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
069        final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
070
071        if (isChild(threadContextClassLoader, loader1)) {
072            return isChild(threadContextClassLoader, loader2) ? threadContextClassLoader : loader2;
073        }
074        return isChild(loader1, loader2) ? loader1 : loader2;
075    }
076
077    /**
078     * This method will search for {@code resource} in different
079     * places. The search order is as follows:
080     *
081     * <ol>
082     *
083     * <li>Search for {@code resource} using the thread context
084     * class loader under Java2. If that fails, search for
085     * {@code resource} using the class loader that loaded this
086     * class ({@code Loader}). Under JDK 1.1, only the the class
087     * loader that loaded this class ({@code Loader}) is used.</li>
088     * <li>Try one last time with
089     * {@code ClassLoader.getSystemResource(resource)}, that is is
090     * using the system class loader in JDK 1.2 and virtual machine's
091     * built-in class loader in JDK 1.1.</li>
092     * </ol>
093     * @param resource The resource to load.
094     * @param defaultLoader The default ClassLoader.
095     * @return A URL to the resource.
096     */
097    public static URL getResource(final String resource, final ClassLoader defaultLoader) {
098        try {
099            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}