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