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