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