/*
* =============================================================================
*
* Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org)
*
* Licensed 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.thymeleaf.util;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* <p>
* Utility class for obtaining a correct classloader on which to operate from a
* specific class.
* </p>
*
* @author Daniel Fernández
*
* @since 2.0.6
*
*/
public final class ClassLoaderUtils {
private static final ClassLoader classClassLoader;
private static final ClassLoader systemClassLoader;
private static final boolean systemClassLoaderAccessibleFromClassClassLoader;
static {
classClassLoader = getClassClassLoader(ClassLoaderUtils.class);
systemClassLoader = getSystemClassLoader();
systemClassLoaderAccessibleFromClassClassLoader = isKnownClassLoaderAccessibleFrom(systemClassLoader, classClassLoader);
}
/**
* <p>
* Try to obtain a classloader, following these priorities:
* </p>
* <ol>
* <li>If there is a <i>thread context class loader</i>, return it.</li>
* <li>Else if there is a class loader related to the class passed as argument, return it.</li>
* <li>Else return the <i>system class loader</i>.</li>
* </ol>
*
* @param clazz the class which loader will be obtained in the second step. Can be null (that will
* skip that second step).
* @return a non-null, safe classloader to use.
*/
public static ClassLoader getClassLoader(final Class<?> clazz) {
// Context class loader can be null
final ClassLoader contextClassLoader = getThreadContextClassLoader();
if (contextClassLoader != null) {
return contextClassLoader;
}
if (clazz != null) {
// The class loader for a specific class can also be null
final ClassLoader clazzClassLoader = getClassClassLoader(clazz);
if (clazzClassLoader != null) {
return clazzClassLoader;
}
}
// The only class loader we can rely on for not being null is the system one
return systemClassLoader;
}
/**
* <p>
* Obtain a class by name, throwing an exception if it is not present.
* </p>
* <p>
* First the <em>context class loader</em> will be used. If this class loader is not
* able to load the class, then the <em>class class loader</em>
* (<tt>ClassLoaderUtils.class.getClassLoader()</tt>) will be used if it is different from
* the thread context one. Last, the System class loader will be tried.
* </p>
* <p>
* This method does never return <tt>null</tt>.
* </p>
*
* @param className the name of the class to be obtained.
* @return the loaded class (null never returned).
* @throws ClassNotFoundException if the class could not be loaded.
*
* @since 3.0.3
*
*/
public static Class<?> loadClass(final String className) throws ClassNotFoundException {
ClassNotFoundException notFoundException = null;
// First try the context class loader
final ClassLoader contextClassLoader = getThreadContextClassLoader();
if (contextClassLoader != null) {
try {
return Class.forName(className, false, contextClassLoader);
} catch (final ClassNotFoundException cnfe) {
notFoundException = cnfe;
// Pass-through, there might be other ways of obtaining it
// note anyway that this is not really normal: the context class loader should be
// either able to resolve any of our application's classes, or to delegate to a class
// loader that can do that.
}
}
// The thread context class loader might have already delegated to both the class
// and system class loaders, in which case it makes little sense to query them too.
if (!isKnownLeafClassLoader(contextClassLoader)) {
// The context class loader didn't help, so... maybe the class one?
if (classClassLoader != null && classClassLoader != contextClassLoader) {
try {
return Class.forName(className, false, classClassLoader);
} catch (final ClassNotFoundException cnfe) {
if (notFoundException == null) {
notFoundException = cnfe;
}
// Pass-through, maybe the system class loader can do it? - though it would be *really* weird...
}
}
if (!systemClassLoaderAccessibleFromClassClassLoader) {
// The only class loader we can rely on for not being null is the system one
if (systemClassLoader != null && systemClassLoader != contextClassLoader && systemClassLoader != classClassLoader) {
try {
return Class.forName(className, false, systemClassLoader);
} catch (final ClassNotFoundException cnfe) {
if (notFoundException == null) {
notFoundException = cnfe;
}
// Pass-through, anyway we have a return null after this...
}
}
}
}
// If we have to throw an exception, we will do so with the highest-level one just in case it provides
// a useful message. So preferably we will throw the one coming from the thread context class loader,
// if not then the class class loader, etc.
throw notFoundException;
}
/**
* <p>
* Try to obtain a class by name, returning <tt>null</tt> if not found.
* </p>
* <p>
* This method works very similarly to {@link #loadClass(String)} but will just return <tt>null</tt>
* if the class is not found by the sequence of class loaders being tried.
* </p>
*
* @param className the name of the class to be obtained.
* @return the found class, or <tt>null</tt> if it could not be found.
*
* @since 3.0.3
*
*/
public static Class<?> findClass(final String className) {
try {
return loadClass(className);
} catch (final ClassNotFoundException cnfe) {
// ignored, we will just return null in this case
return null;
}
}
/**
* <p>
* Checks whether a class is present at the application's class path.
* </p>
* <p>
* This method works very similarly to {@link #findClass(String)} but will just return <tt>true</tt>
* or <tt>false</tt> depending on whether the class could be found or not.
* </p>
*
* @param className the name of the class to be checked.
* @return <tt>true</tt> if the class was found (by any class loader), <tt>false</tt> if not.
*
* @since 3.0.3
*
*/
public static boolean isClassPresent(final String className) {
return findClass(className) != null;
}
/**
* <p>
* Try to obtain a resource by name, returning <tt>null</tt> if it could not be located.
* </p>
* <p>
* First the <em>context class loader</em> will be used. If this class loader is not
* able to locate the resource, then the <em>class class loader</em>
* (<tt>ClassLoaderUtils.class.getClassLoader()</tt>) will be used if it is different from
* the thread context one. Last, the System class loader will be tried.
* </p>
*
* @param resourceName the name of the resource to be obtained.
* @return the found resource, or <tt>null</tt> if it could not be located.
*
* @since 3.0.3
*
*/
public static URL findResource(final String resourceName) {
// First try the context class loader
final ClassLoader contextClassLoader = getThreadContextClassLoader();
if (contextClassLoader != null) {
final URL url = contextClassLoader.getResource(resourceName);
if (url != null) {
return url;
}
// Pass-through, there might be other ways of obtaining it
// note anyway that this is not really normal: the context class loader should be
// either able to resolve any of our application's resources, or to delegate to a class
// loader that can do that.
}
// The thread context class loader might have already delegated to both the class
// and system class loaders, in which case it makes little sense to query them too.
if (!isKnownLeafClassLoader(contextClassLoader)) {
// The context class loader didn't help, so... maybe the class one?
if (classClassLoader != null && classClassLoader != contextClassLoader) {
final URL url = classClassLoader.getResource(resourceName);
if (url != null) {
return url;
}
// Pass-through, maybe the system class loader can do it? - though it would be *really* weird...
}
if (!systemClassLoaderAccessibleFromClassClassLoader) {
// The only class loader we can rely on for not being null is the system one
if (systemClassLoader != null && systemClassLoader != contextClassLoader && systemClassLoader != classClassLoader) {
final URL url = systemClassLoader.getResource(resourceName);
if (url != null) {
return url;
}
// Pass-through, anyway we have a return null after this...
}
}
}
return null;
}
/**
* <p>
* Checks whether a resource is present at the application's class path.
* </p>
* <p>
* This method works very similarly to {@link #findResource(String)} but will just return <tt>true</tt>
* or <tt>false</tt> depending on whether the resource could be located or not.
* </p>
*
* @param resourceName the name of the resource to be checked.
* @return <tt>true</tt> if the class was located (by any class loader), <tt>false</tt> if not.
*
* @since 3.0.3
*
*/
public static boolean isResourcePresent(final String resourceName) {
return findResource(resourceName) != null;
}
/**
* <p>
* Obtain a resource by name, throwing an exception if it is not present.
* </p>
* <p>
* First the <em>context class loader</em> will be used. If this class loader is not
* able to locate the resource, then the <em>class class loader</em>
* (<tt>ClassLoaderUtils.class.getClassLoader()</tt>) will be used if it is different from
* the thread context one. Last, the System class loader will be tried.
* </p>
* <p>
* This method does never return <tt>null</tt>.
* </p>
*
* @param resourceName the name of the resource to be obtained.
* @return an input stream on the resource (null never returned).
* @throws IOException if the resource could not be located.
*
* @since 3.0.3
*
*/
public static InputStream loadResourceAsStream(final String resourceName) throws IOException {
final InputStream inputStream = findResourceAsStream(resourceName);
if (inputStream != null) {
return inputStream;
}
// No way to obtain that resource, so we must raise an IOException
throw new IOException("Could not locate resource '" + resourceName + "' in the application's class path");
}
/**
* <p>
* Try to obtain a resource by name, returning <tt>null</tt> if it could not be located.
* </p>
* <p>
* This method works very similarly to {@link #loadResourceAsStream(String)} but will just return <tt>null</tt>
* if the resource cannot be located by the sequence of class loaders being tried.
* </p>
*
* @param resourceName the name of the resource to be obtained.
* @return an input stream on the resource, or <tt>null</tt> if it could not be located.
*
* @since 3.0.3
*
*/
public static InputStream findResourceAsStream(final String resourceName) {
// First try the context class loader
final ClassLoader contextClassLoader = getThreadContextClassLoader();
if (contextClassLoader != null) {
final InputStream inputStream = contextClassLoader.getResourceAsStream(resourceName);
if (inputStream != null) {
return inputStream;
}
// Pass-through, there might be other ways of obtaining it
// note anyway that this is not really normal: the context class loader should be
// either able to resolve any of our application's resources, or to delegate to a class
// loader that can do that.
}
// The thread context class loader might have already delegated to both the class
// and system class loaders, in which case it makes little sense to query them too.
if (!isKnownLeafClassLoader(contextClassLoader)) {
// The context class loader didn't help, so... maybe the class one?
if (classClassLoader != null && classClassLoader != contextClassLoader) {
final InputStream inputStream = classClassLoader.getResourceAsStream(resourceName);
if (inputStream != null) {
return inputStream;
}
// Pass-through, maybe the system class loader can do it? - though it would be *really* weird...
}
if (!systemClassLoaderAccessibleFromClassClassLoader) {
// The only class loader we can rely on for not being null is the system one
if (systemClassLoader != null && systemClassLoader != contextClassLoader && systemClassLoader != classClassLoader) {
final InputStream inputStream = systemClassLoader.getResourceAsStream(resourceName);
if (inputStream != null) {
return inputStream;
}
// Pass-through, anyway we have a return null after this...
}
}
}
return null;
}
/*
* This will return the thread context class loader if it is possible to access it
* (depending on security restrictions)
*/
private static ClassLoader getThreadContextClassLoader() {
try {
return Thread.currentThread().getContextClassLoader();
} catch (final SecurityException se) {
// The SecurityManager prevents us from accessing, so just ignore it
return null;
}
}
/*
* This will return the class class loader if it is possible to access it
* (depending on security restrictions)
*/
private static ClassLoader getClassClassLoader(final Class<?> clazz) {
try {
return clazz.getClassLoader();
} catch (final SecurityException se) {
// The SecurityManager prevents us from accessing, so just ignore it
return null;
}
}
/*
* This will return the system class loader if it is possible to access it
* (depending on security restrictions)
*/
private static ClassLoader getSystemClassLoader() {
try {
return ClassLoader.getSystemClassLoader();
} catch (final SecurityException se) {
// The SecurityManager prevents us from accessing, so just ignore it
return null;
}
}
/*
* This method determines whether it is known that a this class loader is a a child of another one, or equal to it.
* The "known" part is because SecurityManager could be preventing us from knowing such information.
*/
private static boolean isKnownClassLoaderAccessibleFrom(final ClassLoader accessibleCL, final ClassLoader fromCL) {
if (fromCL == null) {
return false;
}
ClassLoader parent = fromCL;
try {
while (parent != null && parent != accessibleCL) {
parent = parent.getParent();
}
return (parent != null && parent == accessibleCL);
} catch (final SecurityException se) {
// The SecurityManager prevents us from accessing, so just ignore it
return false;
}
}
/*
* This method determines whether it is known that a this class loader is a "leaf", in the sense that
* going up through its hierarchy we are able to find both the class class loader and the system class
* loader. This is used for determining whether we should be confident on the thread-context class loader
* delegation mechanism or rather try to perform class/resource resolution manually on the other class loaders.
*/
private static boolean isKnownLeafClassLoader(final ClassLoader classLoader) {
if (classLoader == null) {
return false;
}
if (!isKnownClassLoaderAccessibleFrom(classClassLoader, classLoader)) {
// We cannot access the class class loader from the specified class loader, so this is not a leaf
return false;
}
// Now we know there is a way to reach the class class loader from the argument class loader, so we should
// base or results on whether there is a way to reach the system class loader from the class class loader.
return systemClassLoaderAccessibleFromClassClassLoader;
}
private ClassLoaderUtils() {
super();
}
}