    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    }