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