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
019import java.io.InputStream;
020import java.lang.reflect.InvocationTargetException;
021import java.net.URL;
022
023import org.apache.logging.log4j.Logger;
024import org.apache.logging.log4j.status.StatusLogger;
025import org.apache.logging.log4j.util.LoaderUtil;
026
027/**
028 * Load resources (or images) from various sources.
029 */
030public final class Loader {
031
032    private static final Logger LOGGER = StatusLogger.getLogger();
033
034    private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
035
036    private Loader() {
037    }
038
039    /**
040     * Returns the ClassLoader to use.
041     * @return the ClassLoader.
042     */
043    public static ClassLoader getClassLoader() {
044        return getClassLoader(Loader.class, null);
045    }
046
047    /**
048     * Returns the ClassLoader of current thread if possible, or falls back to the system ClassLoader if none is
049     * available.
050     *
051     * @return the TCCL.
052     * @see org.apache.logging.log4j.util.LoaderUtil#getThreadContextClassLoader()
053     */
054    public static ClassLoader getThreadContextClassLoader() {
055        return LoaderUtil.getThreadContextClassLoader();
056    }
057
058    // TODO: this method could use some explanation
059    public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
060        final ClassLoader threadContextClassLoader = getThreadContextClassLoader();
061        final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
062        final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
063
064        if (isChild(threadContextClassLoader, loader1)) {
065            return isChild(threadContextClassLoader, loader2) ? threadContextClassLoader : loader2;
066        }
067        return isChild(loader1, loader2) ? loader1 : loader2;
068    }
069
070    /**
071     * This method will search for {@code resource} in different
072     * places. The search order is as follows:
073     *
074     * <ol>
075     *
076     * <li>Search for {@code resource} using the thread context
077     * class loader under Java2. If that fails, search for
078     * {@code resource} using the class loader that loaded this
079     * class ({@code Loader}). Under JDK 1.1, only the the class
080     * loader that loaded this class ({@code Loader}) is used.</li>
081     * <li>Try one last time with
082     * {@code ClassLoader.getSystemResource(resource)}, that is is
083     * using the system class loader in JDK 1.2 and virtual machine's
084     * built-in class loader in JDK 1.1.</li>
085     * </ol>
086     * @param resource The resource to load.
087     * @param defaultLoader The default ClassLoader.
088     * @return A URL to the resource.
089     */
090    public static URL getResource(final String resource, final ClassLoader defaultLoader) {
091        try {
092            ClassLoader classLoader = getThreadContextClassLoader();
093            if (classLoader != null) {
094                LOGGER.trace("Trying to find [{}] using context class loader {}.", resource, classLoader);
095                final URL url = classLoader.getResource(resource);
096                if (url != null) {
097                    return url;
098                }
099            }
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     * Loads a named Class using a given ClassLoader.
232     *
233     * @param className The class name.
234     * @param loader The class loader.
235     * @return The class, or null if loader is null.
236     * @throws ClassNotFoundException if the class could not be found.
237     */
238    public static Class<?> loadClass(final String className, final ClassLoader loader)
239            throws ClassNotFoundException {
240        return loader != null ? loader.loadClass(className) : null;
241    }    
242    
243    /**
244     * Load a Class in the {@code java.*} namespace by name. Useful for peculiar scenarios typically involving
245     * Google App Engine.
246     *
247     * @param className The class name.
248     * @return The Class.
249     * @throws ClassNotFoundException if the Class could not be found.
250     */
251    public static Class<?> loadSystemClass(final String className) throws ClassNotFoundException {
252        try {
253            return Class.forName(className, true, ClassLoader.getSystemClassLoader());
254        } catch (final Throwable t) {
255            LOGGER.trace("Couldn't use SystemClassLoader. Trying Class.forName({}).", className, t);
256            return Class.forName(className);
257        }
258    }
259
260    /**
261     * Loads and instantiates a Class using the default constructor.
262     *
263     * @param className The class name.
264     * @return new instance of the class.
265     * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
266     * @throws IllegalAccessException if the class can't be instantiated through a public constructor
267     * @throws InstantiationException if there was an exception whilst instantiating the class
268     * @throws NoSuchMethodException if there isn't a no-args constructor on the class
269     * @throws InvocationTargetException if there was an exception whilst constructing the class
270     */
271    public static Object newInstanceOf(final String className)
272            throws ClassNotFoundException,
273                   IllegalAccessException,
274                   InstantiationException,
275                   NoSuchMethodException,
276                   InvocationTargetException {
277        return LoaderUtil.newInstanceOf(className);
278    }
279
280    /**
281     * Loads, instantiates, and casts a Class using the default constructor.
282     *
283     * @param className The class name.
284     * @param clazz The class to cast it to.
285     * @param <T> The type to cast it to.
286     * @return new instance of the class cast to {@code T}
287     * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
288     * @throws IllegalAccessException if the class can't be instantiated through a public constructor
289     * @throws InstantiationException if there was an exception whilst instantiating the class
290     * @throws NoSuchMethodException if there isn't a no-args constructor on the class
291     * @throws InvocationTargetException if there was an exception whilst constructing the class
292     * @throws ClassCastException if the constructed object isn't type compatible with {@code T}
293     */
294    public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz)
295            throws ClassNotFoundException,
296                   NoSuchMethodException,
297                   IllegalAccessException,
298                   InvocationTargetException,
299                   InstantiationException {
300        return LoaderUtil.newCheckedInstanceOf(className, clazz);
301    }
302
303    /**
304     * Determines if a named Class can be loaded or not.
305     *
306     * @param className The class name.
307     * @return {@code true} if the class could be found or {@code false} otherwise.
308     */
309    public static boolean isClassAvailable(final String className) {
310        return LoaderUtil.isClassAvailable(className);
311    }
312
313    public static boolean isJansiAvailable() {
314        return isClassAvailable("org.fusesource.jansi.AnsiRenderer");
315    }
316
317}