View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.core.util;
    18
    19
    20  import java.io.InputStream;
    21  import java.lang.reflect.InvocationTargetException;
    22  import java.lang.reflect.ReflectPermission;
    23  import java.net.URL;
    24
    25  import org.apache.logging.log4j.Logger;
    26  import org.apache.logging.log4j.status.StatusLogger;
    27  import org.apache.logging.log4j.util.LoaderUtil;
    28
    29  /**
    30   * Load resources (or images) from various sources.
    31   */
    32  public final class Loader {
    33
    34      private static final Logger LOGGER = StatusLogger.getLogger();
    35
    36      private static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
    37
    38      static {
    39          final SecurityManager sm = System.getSecurityManager();
    40          if (sm != null) {
    41              sm.checkPermission(new RuntimePermission("getStackTrace"));
    42              sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
    43          }
    44      }
    45
    46      /**
    47       * Returns the ClassLoader to use.
    48       * @return the ClassLoader.
    49       */
    50      public static ClassLoader getClassLoader() {
    51          return getClassLoader(Loader.class, null);
    52      }
    53
    54      /**
    55       * Returns the ClassLoader of current thread if possible, or falls back to the system ClassLoader if none is
    56       * available.
    57       *
    58       * @return the TCCL.
    59       * @see org.apache.logging.log4j.util.LoaderUtil#getThreadContextClassLoader()
    60       */
    61      public static ClassLoader getThreadContextClassLoader() {
    62          return LoaderUtil.getThreadContextClassLoader();
    63      }
    64
    65      // TODO: this method could use some explanation
    66      public static ClassLoader getClassLoader(final Class<?> class1, final Class<?> class2) {
    67          final ClassLoader threadContextClassLoader = getTcl();
    68          final ClassLoader loader1 = class1 == null ? null : class1.getClassLoader();
    69          final ClassLoader loader2 = class2 == null ? null : class2.getClassLoader();
    70
    71          if (isChild(threadContextClassLoader, loader1)) {
    72              return isChild(threadContextClassLoader, loader2) ? threadContextClassLoader : loader2;
    73          }
    74          return isChild(loader1, loader2) ? loader1 : loader2;
    75      }
    76
    77      /**
    78       * This method will search for {@code resource} in different
    79       * places. The search order is as follows:
    80       *
    81       * <ol>
    82       *
    83       * <li>Search for {@code resource} using the thread context
    84       * class loader under Java2. If that fails, search for
    85       * {@code resource} using the class loader that loaded this
    86       * class ({@code Loader}). Under JDK 1.1, only the the class
    87       * loader that loaded this class ({@code Loader}) is used.</li>
    88       * <li>Try one last time with
    89       * {@code ClassLoader.getSystemResource(resource)}, that is is
    90       * using the system class loader in JDK 1.2 and virtual machine's
    91       * built-in class loader in JDK 1.1.</li>
    92       * </ol>
    93       * @param resource The resource to load.
    94       * @param defaultLoader The default ClassLoader.
    95       * @return A URL to the resource.
    96       */
    97      public static URL getResource(final String resource, final ClassLoader defaultLoader) {
    98          try {
    99              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 }