/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.util;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;

Consider this class private. Utility class for ClassLoaders.
See Also:
/** * <em>Consider this class private.</em> Utility class for ClassLoaders. * * @see ClassLoader * @see RuntimePermission * @see Thread#getContextClassLoader() * @see ClassLoader#getSystemClassLoader() */
public final class LoaderUtil {
System property to set to ignore the thread context ClassLoader.
Since:2.1
/** * System property to set to ignore the thread context ClassLoader. * * @since 2.1 */
public static final String IGNORE_TCCL_PROPERTY = "log4j.ignoreTCL"; private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager(); // this variable must be lazily loaded; otherwise, we get a nice circular class loading problem where LoaderUtil // wants to use PropertiesUtil, but then PropertiesUtil wants to use LoaderUtil. private static Boolean ignoreTCCL; private static final boolean GET_CLASS_LOADER_DISABLED; private static final PrivilegedAction<ClassLoader> TCCL_GETTER = new ThreadContextClassLoaderGetter(); static { if (SECURITY_MANAGER != null) { boolean getClassLoaderDisabled; try { SECURITY_MANAGER.checkPermission(new RuntimePermission("getClassLoader")); getClassLoaderDisabled = false; } catch (final SecurityException ignored) { getClassLoaderDisabled = true; } GET_CLASS_LOADER_DISABLED = getClassLoaderDisabled; } else { GET_CLASS_LOADER_DISABLED = false; } } private LoaderUtil() { }
Gets the current Thread ClassLoader. Returns the system ClassLoader if the TCCL is null. If the system ClassLoader is null as well, then the ClassLoader for this class is returned. If running with a SecurityManager that does not allow access to the Thread ClassLoader or system ClassLoader, then the ClassLoader for this class is returned.
Returns:the current ThreadContextClassLoader.
/** * Gets the current Thread ClassLoader. Returns the system ClassLoader if the TCCL is {@code null}. If the system * ClassLoader is {@code null} as well, then the ClassLoader for this class is returned. If running with a * {@link SecurityManager} that does not allow access to the Thread ClassLoader or system ClassLoader, then the * ClassLoader for this class is returned. * * @return the current ThreadContextClassLoader. */
public static ClassLoader getThreadContextClassLoader() { if (GET_CLASS_LOADER_DISABLED) { // we can at least get this class's ClassLoader regardless of security context // however, if this is null, there's really no option left at this point return LoaderUtil.class.getClassLoader(); } return SECURITY_MANAGER == null ? TCCL_GETTER.run() : AccessController.doPrivileged(TCCL_GETTER); } /** * */ private static class ThreadContextClassLoaderGetter implements PrivilegedAction<ClassLoader> { @Override public ClassLoader run() { final ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl != null) { return cl; } final ClassLoader ccl = LoaderUtil.class.getClassLoader(); return ccl == null && !GET_CLASS_LOADER_DISABLED ? ClassLoader.getSystemClassLoader() : ccl; } } public static ClassLoader[] getClassLoaders() { final List<ClassLoader> classLoaders = new ArrayList<>(); final ClassLoader tcl = getThreadContextClassLoader(); classLoaders.add(tcl); // Some implementations may use null to represent the bootstrap class loader. final ClassLoader current = LoaderUtil.class.getClassLoader(); if (current != null && current != tcl) { classLoaders.add(current); final ClassLoader parent = current.getParent(); while (parent != null && !classLoaders.contains(parent)) { classLoaders.add(parent); } } ClassLoader parent = tcl == null ? null : tcl.getParent(); while (parent != null && !classLoaders.contains(parent)) { classLoaders.add(parent); parent = parent.getParent(); } final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); if (!classLoaders.contains(systemClassLoader)) { classLoaders.add(systemClassLoader); } return classLoaders.toArray(new ClassLoader[classLoaders.size()]); }
Determines if a named Class can be loaded or not.
Params:
  • className – The class name.
Returns:true if the class could be found or false otherwise.
Since:2.7
/** * Determines if a named Class can be loaded or not. * * @param className The class name. * @return {@code true} if the class could be found or {@code false} otherwise. * @since 2.7 */
public static boolean isClassAvailable(final String className) { try { final Class<?> clazz = loadClass(className); return clazz != null; } catch (final ClassNotFoundException | LinkageError e) { return false; } catch (final Throwable e) { LowLevelLogUtil.logException("Unknown error checking for existence of class: " + className, e); return false; } }
Loads a class by name. This method respects the IGNORE_TCCL_PROPERTY Log4j property. If this property is specified and set to anything besides false, then the default ClassLoader will be used.
Params:
  • className – The class name.
Throws:
Returns:the Class for the given name.
Since:2.1
/** * Loads a class by name. This method respects the {@link #IGNORE_TCCL_PROPERTY} Log4j property. If this property is * specified and set to anything besides {@code false}, then the default ClassLoader will be used. * * @param className The class name. * @return the Class for the given name. * @throws ClassNotFoundException if the specified class name could not be found * @since 2.1 */
public static Class<?> loadClass(final String className) throws ClassNotFoundException { if (isIgnoreTccl()) { return Class.forName(className); } try { return getThreadContextClassLoader().loadClass(className); } catch (final Throwable ignored) { return Class.forName(className); } }
Loads and instantiates a Class using the default constructor.
Params:
  • clazz – The class.
Throws:
Returns:new instance of the class.
Since:2.7
/** * Loads and instantiates a Class using the default constructor. * * @param clazz The class. * @return new instance of the class. * @throws IllegalAccessException if the class can't be instantiated through a public constructor * @throws InstantiationException if there was an exception whilst instantiating the class * @throws InvocationTargetException if there was an exception whilst constructing the class * @since 2.7 */
public static <T> T newInstanceOf(final Class<T> clazz) throws InstantiationException, IllegalAccessException, InvocationTargetException { try { return clazz.getConstructor().newInstance(); } catch (final NoSuchMethodException ignored) { // FIXME: looking at the code for Class.newInstance(), this seems to do the same thing as above return clazz.newInstance(); } }
Loads and instantiates a Class using the default constructor.
Params:
  • className – The class name.
Throws:
Returns:new instance of the class.
Since:2.1
/** * Loads and instantiates a Class using the default constructor. * * @param className The class name. * @return new instance of the class. * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders * @throws IllegalAccessException if the class can't be instantiated through a public constructor * @throws InstantiationException if there was an exception whilst instantiating the class * @throws NoSuchMethodException if there isn't a no-args constructor on the class * @throws InvocationTargetException if there was an exception whilst constructing the class * @since 2.1 */
@SuppressWarnings("unchecked") public static <T> T newInstanceOf(final String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { return newInstanceOf((Class<T>) loadClass(className)); }
Loads and instantiates a derived class using its default constructor.
Params:
  • className – The class name.
  • clazz – The class to cast it to.
Type parameters:
  • <T> – The type of the class to check.
Throws:
Returns:new instance of the class cast to T
Since:2.1
/** * Loads and instantiates a derived class using its default constructor. * * @param className The class name. * @param clazz The class to cast it to. * @param <T> The type of the class to check. * @return new instance of the class cast to {@code T} * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders * @throws IllegalAccessException if the class can't be instantiated through a public constructor * @throws InstantiationException if there was an exception whilst instantiating the class * @throws NoSuchMethodException if there isn't a no-args constructor on the class * @throws InvocationTargetException if there was an exception whilst constructing the class * @throws ClassCastException if the constructed object isn't type compatible with {@code T} * @since 2.1 */
public static <T> T newCheckedInstanceOf(final String className, final Class<T> clazz) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { return clazz.cast(newInstanceOf(className)); }
Loads and instantiates a class given by a property name.
Params:
  • propertyName – The property name to look up a class name for.
  • clazz – The class to cast it to.
Type parameters:
  • <T> – The type to cast it to.
Throws:
Returns:new instance of the class given in the property or null if the property was unset.
Since:2.5
/** * Loads and instantiates a class given by a property name. * * @param propertyName The property name to look up a class name for. * @param clazz The class to cast it to. * @param <T> The type to cast it to. * @return new instance of the class given in the property or {@code null} if the property was unset. * @throws ClassNotFoundException if the class isn't available to the usual ClassLoaders * @throws IllegalAccessException if the class can't be instantiated through a public constructor * @throws InstantiationException if there was an exception whilst instantiating the class * @throws NoSuchMethodException if there isn't a no-args constructor on the class * @throws InvocationTargetException if there was an exception whilst constructing the class * @throws ClassCastException if the constructed object isn't type compatible with {@code T} * @since 2.5 */
public static <T> T newCheckedInstanceOfProperty(final String propertyName, final Class<T> clazz) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { final String className = PropertiesUtil.getProperties().getStringProperty(propertyName); if (className == null) { return null; } return newCheckedInstanceOf(className, clazz); } private static boolean isIgnoreTccl() { // we need to lazily initialize this, but concurrent access is not an issue if (ignoreTCCL == null) { final String ignoreTccl = PropertiesUtil.getProperties().getStringProperty(IGNORE_TCCL_PROPERTY, null); ignoreTCCL = ignoreTccl != null && !"false".equalsIgnoreCase(ignoreTccl.trim()); } return ignoreTCCL; }
Finds classpath resources.
Params:
  • resource – the name of the resource to find.
Returns:a Collection of URLs matching the resource name. If no resources could be found, then this will be empty.
Since:2.1
/** * Finds classpath {@linkplain URL resources}. * * @param resource the name of the resource to find. * @return a Collection of URLs matching the resource name. If no resources could be found, then this will be empty. * @since 2.1 */
public static Collection<URL> findResources(final String resource) { final Collection<UrlResource> urlResources = findUrlResources(resource); final Collection<URL> resources = new LinkedHashSet<>(urlResources.size()); for (final UrlResource urlResource : urlResources) { resources.add(urlResource.getUrl()); } return resources; } static Collection<UrlResource> findUrlResources(final String resource) { // @formatter:off final ClassLoader[] candidates = { getThreadContextClassLoader(), LoaderUtil.class.getClassLoader(), GET_CLASS_LOADER_DISABLED ? null : ClassLoader.getSystemClassLoader()}; // @formatter:on final Collection<UrlResource> resources = new LinkedHashSet<>(); for (final ClassLoader cl : candidates) { if (cl != null) { try { final Enumeration<URL> resourceEnum = cl.getResources(resource); while (resourceEnum.hasMoreElements()) { resources.add(new UrlResource(cl, resourceEnum.nextElement())); } } catch (final IOException e) { LowLevelLogUtil.logException(e); } } } return resources; }
URL and ClassLoader pair.
/** * {@link URL} and {@link ClassLoader} pair. */
static class UrlResource { private final ClassLoader classLoader; private final URL url; UrlResource(final ClassLoader classLoader, final URL url) { this.classLoader = classLoader; this.url = url; } public ClassLoader getClassLoader() { return classLoader; } public URL getUrl() { return url; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final UrlResource that = (UrlResource) o; if (classLoader != null ? !classLoader.equals(that.classLoader) : that.classLoader != null) { return false; } if (url != null ? !url.equals(that.url) : that.url != null) { return false; } return true; } @Override public int hashCode() { return Objects.hashCode(classLoader) + Objects.hashCode(url); } } }