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.helpers;
018
019import java.io.InputStream;
020import java.net.URL;
021
022import org.apache.logging.log4j.Logger;
023import org.apache.logging.log4j.status.StatusLogger;
024import org.apache.logging.log4j.util.PropertiesUtil;
025
026/**
027 * Load resources (or images) from various sources.
028 */
029public final class Loader {
030
031    private static boolean ignoreTCL = false;
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    static {
038        final String ignoreTCLProp = PropertiesUtil.getProperties().getStringProperty("log4j.ignoreTCL", null);
039        if (ignoreTCLProp != null) {
040            ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);
041        }
042    }
043
044    /**
045     * Returns the ClassLoader to use.
046     * @return the ClassLoader.
047     */
048    public static ClassLoader getClassLoader() {
049
050        return getClassLoader(Loader.class, null);
051    }
052
053    public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
054
055        ClassLoader loader1 = null;
056        try {
057            loader1 = getTCL();
058        } catch (final Exception ex) {
059            LOGGER.warn("Caught exception locating thread ClassLoader {}", ex.getMessage());
060        }
061        final ClassLoader loader2 = class1 == null ? null : class1.getClassLoader();
062        final ClassLoader loader3 = class2 == null ? null : class2.getClass().getClassLoader();
063
064        if (isChild(loader1, loader2)) {
065            return isChild(loader1, loader3) ? loader1 : loader3;
066        }
067        return isChild(loader2, loader3) ? loader2 : loader3;
068    }
069
070    /**
071     * This method will search for <code>resource</code> in different
072     * places. The search order is as follows:
073     * <p/>
074     * <ol>
075     * <p/>
076     * <p><li>Search for <code>resource</code> using the thread context
077     * class loader under Java2. If that fails, search for
078     * <code>resource</code> using the class loader that loaded this
079     * class (<code>Loader</code>). Under JDK 1.1, only the the class
080     * loader that loaded this class (<code>Loader</code>) is used.
081     * <p/>
082     * <p><li>Try one last time with
083     * <code>ClassLoader.getSystemResource(resource)</code>, that is is
084     * using the system class loader in JDK 1.2 and virtual machine's
085     * built-in class loader in JDK 1.1.
086     * <p/>
087     * </ol>
088     * @param resource The resource to load.
089     * @param defaultLoader The default ClassLoader.
090     * @return A URL to the resource.
091     */
092    public static URL getResource(final String resource, final ClassLoader defaultLoader) {
093        try {
094            ClassLoader classLoader = getTCL();
095            if (classLoader != null) {
096                LOGGER.trace("Trying to find [" + resource + "] using context classloader "
097                        + classLoader + '.');
098                final URL url = classLoader.getResource(resource);
099                if (url != null) {
100                    return url;
101                }
102            }
103
104            // We could not find resource. Let us now try with the classloader that loaded this class.
105            classLoader = Loader.class.getClassLoader();
106            if (classLoader != null) {
107                LOGGER.trace("Trying to find [" + resource + "] using " + classLoader + " class loader.");
108                final URL url = classLoader.getResource(resource);
109                if (url != null) {
110                    return url;
111                }
112            }
113            // We could not find resource. Finally try with the default ClassLoader.
114            if (defaultLoader != null) {
115                LOGGER.trace("Trying to find [" + resource + "] using " + defaultLoader + " class loader.");
116                final URL url = defaultLoader.getResource(resource);
117                if (url != null) {
118                    return url;
119                }
120            }
121        } catch (final Throwable t) {
122            //
123            //  can't be InterruptedException or InterruptedIOException
124            //    since not declared, must be error or RuntimeError.
125            LOGGER.warn(TSTR, t);
126        }
127
128        // Last ditch attempt: get the resource from the class path. It
129        // may be the case that clazz was loaded by the Extension class
130        // loader which the parent of the system class loader. Hence the
131        // code below.
132        LOGGER.trace("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
133        return ClassLoader.getSystemResource(resource);
134    }
135
136    /**
137     * This method will search for <code>resource</code> in different
138     * places. The search order is as follows:
139     * <p/>
140     * <ol>
141     * <p/>
142     * <p><li>Search for <code>resource</code> using the thread context
143     * class loader under Java2. If that fails, search for
144     * <code>resource</code> using the class loader that loaded this
145     * class (<code>Loader</code>). Under JDK 1.1, only the the class
146     * loader that loaded this class (<code>Loader</code>) is used.
147     * <p/>
148     * <p><li>Try one last time with
149     * <code>ClassLoader.getSystemResource(resource)</code>, that is is
150     * using the system class loader in JDK 1.2 and virtual machine's
151     * built-in class loader in JDK 1.1.
152     * <p/>
153     * </ol>
154     * @param resource The resource to load.
155     * @param defaultLoader The default ClassLoader.
156     * @return An InputStream to read the resouce.
157     */
158    public static InputStream getResourceAsStream(final String resource, final ClassLoader defaultLoader) {
159        ClassLoader classLoader;
160        InputStream is;
161
162        try {
163            classLoader = getTCL();
164            if (classLoader != null) {
165                LOGGER.trace("Trying to find [" + resource + "] using context classloader " + classLoader + '.');
166                is = classLoader.getResourceAsStream(resource);
167                if (is != null) {
168                    return is;
169                }
170            }
171
172            // We could not find resource. Let us now try with the classloader that loaded this class.
173            classLoader = Loader.class.getClassLoader();
174            if (classLoader != null) {
175                LOGGER.trace("Trying to find [" + resource + "] using " + classLoader + " class loader.");
176                is = classLoader.getResourceAsStream(resource);
177                if (is != null) {
178                    return is;
179                }
180            }
181
182            // We could not find resource. Finally try with the default ClassLoader.
183            if (defaultLoader != null) {
184                LOGGER.trace("Trying to find [" + resource + "] using " + defaultLoader + " class loader.");
185                is = defaultLoader.getResourceAsStream(resource);
186                if (is != null) {
187                    return is;
188                }
189            }
190        } catch (final Throwable t) {
191            //
192            //  can't be InterruptedException or InterruptedIOException
193            //    since not declared, must be error or RuntimeError.
194            LOGGER.warn(TSTR, t);
195        }
196
197        // Last ditch attempt: get the resource from the class path. It
198        // may be the case that clazz was loaded by the Extension class
199        // loader which the parent of the system class loader. Hence the
200        // code below.
201        LOGGER.trace("Trying to find [" + resource + "] using ClassLoader.getSystemResource().");
202        return ClassLoader.getSystemResourceAsStream(resource);
203    }
204
205    private static ClassLoader getTCL() {
206        ClassLoader cl;
207        if (System.getSecurityManager() == null) {
208            cl = Thread.currentThread().getContextClassLoader();
209        } else {
210            cl = java.security.AccessController.doPrivileged(
211                new java.security.PrivilegedAction<ClassLoader>() {
212                    @Override
213                    public ClassLoader run() {
214                        return Thread.currentThread().getContextClassLoader();
215                    }
216                }
217            );
218        }
219
220        return cl;
221    }
222
223    private static boolean isChild(final ClassLoader loader1, final ClassLoader loader2) {
224        if (loader1 != null && loader2 != null) {
225            ClassLoader parent = loader1.getParent();
226            while (parent != null && parent != loader2) {
227                parent = parent.getParent();
228            }
229            return parent != null;
230        }
231        return loader1 != null;
232    }
233
234    /**
235     * Load a Class by name.
236     * @param className The class name.
237     * @return The Class.
238     * @throws ClassNotFoundException if the Class could not be found.
239     */
240    public static Class<?> loadClass(final String className) throws ClassNotFoundException {
241        // Just call Class.forName(className) if we are instructed to ignore the TCL.
242        if (ignoreTCL) {
243            return Class.forName(className);
244        }
245        try {
246            return getTCL().loadClass(className);
247        } catch (final Throwable e) {
248            return Class.forName(className);
249        }
250    }
251
252    private Loader() {
253    }
254}