    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.util;
    018
    019    import java.io.IOException;
    020    import java.lang.reflect.InvocationTargetException;
    021    import java.net.URL;
    022    import java.security.AccessController;
    023    import java.security.PrivilegedAction;
    024    import java.util.Collection;
    025    import java.util.Enumeration;
    026    import java.util.LinkedHashSet;
    027
    028    /**
    029     * <em>Consider this class private.</em> Utility class for ClassLoaders.
    030     * @see ClassLoader
    031     * @see RuntimePermission
    032     * @see Thread#getContextClassLoader()
    033     * @see ClassLoader#getSystemClassLoader()
    034     */
    035    public final class LoaderUtil {
    036        private LoaderUtil() {}
    037
    038        /**
    039         * System property to set to ignore the thread context ClassLoader.
    040         *
    041         * @since 2.1
    042         */
    043        public static final String IGNORE_TCCL_PROPERTY = "log4j.ignoreTCL";
    044
    045        private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
    046
    047        // this variable must be lazily loaded; otherwise, we get a nice circular class loading problem where LoaderUtil
    048        // wants to use PropertiesUtil, but then PropertiesUtil wants to use LoaderUtil.
    049        private static Boolean ignoreTCCL;
    050
    051        private static final boolean GET_CLASS_LOADER_DISABLED;
    052
    053        private static final PrivilegedAction<ClassLoader> TCCL_GETTER = new ThreadContextClassLoaderGetter();
    054
    055        static {
    056            if (SECURITY_MANAGER != null) {
    057                boolean getClassLoaderDisabled;
    058                try {
    059                    SECURITY_MANAGER.checkPermission(new RuntimePermission("getClassLoader"));
    060                    getClassLoaderDisabled = false;
    061                } catch (final SecurityException ignored) {
    062                    getClassLoaderDisabled = true;
    063                }
    064                GET_CLASS_LOADER_DISABLED = getClassLoaderDisabled;
    065            } else {
    066                GET_CLASS_LOADER_DISABLED = false;
    067            }
    068        }
    069
    070        /**
    071         * Gets the current Thread ClassLoader. Returns the system ClassLoader if the TCCL is {@code null}. If the
    072         * system ClassLoader is {@code null} as well, then the ClassLoader for this class is returned.
    073         * If running with a {@link SecurityManager} that does not allow access to the Thread ClassLoader or system
    074         * ClassLoader, then the ClassLoader for this class is returned.
    075         *
    076         * @return the current ThreadContextClassLoader.
    077         */
    078        public static ClassLoader getThreadContextClassLoader() {
    079            if (GET_CLASS_LOADER_DISABLED) {
    080                // we can at least get this class's ClassLoader regardless of security context
    081                // however, if this is null, there's really no option left at this point
    082                return LoaderUtil.class.getClassLoader();
    083            }
    084            return SECURITY_MANAGER == null
    085                ? TCCL_GETTER.run()
    086                : AccessController.doPrivileged(TCCL_GETTER);
    087        }
    088
    089        private static class ThreadContextClassLoaderGetter implements PrivilegedAction<ClassLoader> {
    090            @Override
    091            public ClassLoader run() {
    092                final ClassLoader cl = Thread.currentThread().getContextClassLoader();
    093                if (cl != null) {
    094                    return cl;
    095                }
    096                final ClassLoader ccl = LoaderUtil.class.getClassLoader();
    097                return ccl == null ? ClassLoader.getSystemClassLoader() : ccl;
    098            }
    099        }
    100
    101        /**
    102         * Loads a class by name. This method respects the {@link #IGNORE_TCCL_PROPERTY} Log4j property. If this property
    103         * is specified and set to anything besides {@code false}, then the default ClassLoader will be used.
    104         *
    105         * @param className The class name.
    106         * @return the Class for the given name.
    107         * @throws ClassNotFoundException if the specified class name could not be found
    108         * @since 2.1
    109         */
    110        public static Class<?> loadClass(final String className) throws ClassNotFoundException {
    111            if (isIgnoreTccl()) {
    112                return Class.forName(className);
    113            }
    114            try {
    115                return getThreadContextClassLoader().loadClass(className);
    116            } catch (final Throwable ignored) {
    117                return Class.forName(className);
    118            }
    119        }
    120
    121        /**
    122         * Loads and instantiates a Class using the default constructor.
    123         *
    124         * @param className The class name.
    125         * @return new instance of the class.
    126         * @throws ClassNotFoundException    if the class isn't available to the usual ClassLoaders
    127         * @throws IllegalAccessException    if the class can't be instantiated through a public constructor
    128         * @throws InstantiationException    if there was an exception whilst instantiating the class
    129         * @throws NoSuchMethodException     if there isn't a no-args constructor on the class
    130         * @throws InvocationTargetException if there was an exception whilst constructing the class
    131         * @since 2.1
    132         */
    133        public static Object newInstanceOf(final String className)
    134            throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException,
    135            InvocationTargetException {
    136            final Class<?> clazz = loadClass(className);
    137            try {
    138                return clazz.getConstructor().newInstance();
    139            } catch (final NoSuchMethodException ignored) {
    140                // FIXME: looking at the code for Class.newInstance(), this seems to do the same thing as above
    141                return clazz.newInstance();
    142            }
    143        }
    144
    145        /**
    146         * Loads and instantiates a derived class using its default constructor.
    147         *
    148         * @param className The class name.
    149         * @param clazz The class to cast it to.
    150         * @param <T> The type of the class to check.
    151         * @return new instance of the class cast to {@code T}
    152         * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders
    153         * @throws IllegalAccessException if the class can't be instantiated through a public constructor
    154         * @throws InstantiationException if there was an exception whilst instantiating the class
    155         * @throws NoSuchMethodException if there isn't a no-args constructor on the class
    156         * @throws InvocationTargetException if there was an exception whilst constructing the class
    157         * @throws ClassCastException if the constructed object isn't type compatible with {@code T}
    158         * @since 2.1
    159         */
    160        public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz)
    161            throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException,
    162            IllegalAccessException {
    163            return clazz.cast(newInstanceOf(className));
    164        }
    165
    166        private static boolean isIgnoreTccl() {
    167            // we need to lazily initialize this, but concurrent access is not an issue
    168            if (ignoreTCCL == null) {
    169                final String ignoreTccl = PropertiesUtil.getProperties().getStringProperty(IGNORE_TCCL_PROPERTY, null);
    170                ignoreTCCL = ignoreTccl != null && !"false".equalsIgnoreCase(ignoreTccl.trim());
    171            }
    172            return ignoreTCCL;
    173        }
    174
    175        /**
    176         * Finds classpath {@linkplain URL resources}.
    177         *
    178         * @param resource the name of the resource to find.
    179         * @return a Collection of URLs matching the resource name. If no resources could be found, then this will be empty.
    180         * @since 2.1
    181         */
    182        public static Collection<URL> findResources(final String resource) {
    183            final Collection<UrlResource> urlResources = findUrlResources(resource);
    184            final Collection<URL> resources = new LinkedHashSet<URL>(urlResources.size());
    185            for (final UrlResource urlResource : urlResources) {
    186                resources.add(urlResource.getUrl());
    187            }
    188            return resources;
    189        }
    190
    191        static Collection<UrlResource> findUrlResources(final String resource) {
    192            final ClassLoader[] candidates = {
    193                getThreadContextClassLoader(),
    194                LoaderUtil.class.getClassLoader(),
    195                ClassLoader.getSystemClassLoader()
    196            };
    197            final Collection<UrlResource> resources = new LinkedHashSet<UrlResource>();
    198            for (final ClassLoader cl : candidates) {
    199                if (cl != null) {
    200                    try {
    201                        final Enumeration<URL> resourceEnum = cl.getResources(resource);
    202                        while (resourceEnum.hasMoreElements()) {
    203                            resources.add(new UrlResource(cl, resourceEnum.nextElement()));
    204                        }
    205                    } catch (final IOException e) {
    206                        e.printStackTrace();
    207                    }
    208                }
    209            }
    210            return resources;
    211        }
    212
    213        /**
    214         * {@link URL} and {@link ClassLoader} pair.
    215         */
    216        static class UrlResource {
    217            private final ClassLoader classLoader;
    218            private final URL url;
    219
    220            public UrlResource(final ClassLoader classLoader, final URL url) {
    221                this.classLoader = classLoader;
    222                this.url = url;
    223            }
    224
    225            public ClassLoader getClassLoader() {
    226                return classLoader;
    227            }
    228
    229            public URL getUrl() {
    230                return url;
    231            }
    232
    233            @Override
    234            public boolean equals(final Object o) {
    235                if (this == o) {
    236                    return true;
    237                }
    238                if (o == null || getClass() != o.getClass()) {
    239                    return false;
    240                }
    241
    242                final UrlResource that = (UrlResource) o;
    243
    244                if (classLoader != null ? !classLoader.equals(that.classLoader) : that.classLoader != null) {
    245                    return false;
    246                }
    247                if (url != null ? !url.equals(that.url) : that.url != null) {
    248                    return false;
    249                }
    250
    251                return true;
    252            }
    253
    254            @Override
    255            public int hashCode() {
    256                int result = classLoader != null ? classLoader.hashCode() : 0;
    257                result = 31 * result + (url != null ? url.hashCode() : 0);
    258                return result;
    259            }
    260        }
    261    }