in src/main/java/org/apache/commons/logging/LogFactory.java [724:910]
public static LogFactory getFactory() throws LogConfigurationException {
// Identify the class loader we will be using
final ClassLoader contextClassLoader = getContextClassLoaderInternal();
// This is an odd enough situation to report about. This
// output will be a nuisance on JDK1.1, as the system
// class loader is null in that environment.
if (contextClassLoader == null) {
logDiagnostic("Context class loader is null.");
}
// Return any previously registered factory for this class loader
LogFactory factory = getCachedFactory(contextClassLoader);
if (factory != null) {
return factory;
}
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] LogFactory implementation requested for the first time for context class loader " +
objectId(contextClassLoader));
logHierarchy("[LOOKUP] ", contextClassLoader);
}
// Load properties file.
//
// If the properties file exists, then its contents are used as
// "attributes" on the LogFactory implementation class. One particular
// property may also control which LogFactory concrete subclass is
// used, but only if other discovery mechanisms fail.
//
// As the properties file (if it exists) will be used one way or
// another in the end we may as well look for it first.
final Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
// Determine whether we will be using the thread context class loader to
// load logging classes or not by checking the loaded properties file (if any).
boolean useTccl = contextClassLoader != null;
if (props != null) {
final String useTCCLStr = props.getProperty(TCCL_KEY);
useTccl &= useTCCLStr == null || Boolean.parseBoolean(useTCCLStr);
}
// If TCCL is still enabled at this point, we check if it resolves this class
if (useTccl) {
try {
if (!LogFactory.class.equals(Class.forName(LogFactory.class.getName(), false, contextClassLoader))) {
logDiagnostic(() -> "The class " + LogFactory.class.getName() + " loaded by the context class loader " + objectId(contextClassLoader)
+ " and this class differ. Disabling the usage of the context class loader."
+ "Background can be found in https://commons.apache.org/logging/tech.html. ");
logHierarchy("[BAD CL TREE] ", contextClassLoader);
useTccl = false;
}
} catch (final ClassNotFoundException ignored) {
logDiagnostic(() -> "The class " + LogFactory.class.getName() + " is not present in the the context class loader "
+ objectId(contextClassLoader) + ". Disabling the usage of the context class loader."
+ "Background can be found in https://commons.apache.org/logging/tech.html. ");
logHierarchy("[BAD CL TREE] ", contextClassLoader);
useTccl = false;
}
}
final ClassLoader baseClassLoader = useTccl ? contextClassLoader : thisClassLoaderRef.get();
// Determine which concrete LogFactory subclass to use.
// First, try a global system property
logDiagnostic(() -> "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
"] to define the LogFactory subclass to use...");
try {
final String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
if (factoryClass != null) {
logDiagnostic(() -> "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
"' as specified by system property " + FACTORY_PROPERTY);
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} else {
logDiagnostic(() -> "[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
}
} catch (final SecurityException e) {
logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class" + ": ["
+ trim(e.getMessage()) + "]. Trying alternative implementations...");
// ignore
} catch (final RuntimeException e) {
// This is not consistent with the behavior when a bad LogFactory class is
// specified in a services file.
//
// One possible exception that can occur here is a ClassCastException when
// the specified class wasn't castable to this LogFactory type.
logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to create an instance of the custom factory class: [" + trim(e.getMessage())
+ "] as specified by a system property.");
throw e;
}
//
// Second, try to find a service by using the JDK 1.3 class
// discovery mechanism, which involves putting a file with the name
// of an interface class in the META-INF/services directory, where the
// contents of the file is a single line specifying a concrete class
// that implements the desired interface.
if (factory == null) {
logDiagnostic("[LOOKUP] Using ServiceLoader to define the LogFactory subclass to use...");
try {
final ServiceLoader<LogFactory> serviceLoader = ServiceLoader.load(LogFactory.class, baseClassLoader);
final Iterator<LogFactory> iterator = serviceLoader.iterator();
int i = MAX_BROKEN_SERVICES;
while (factory == null && i-- > 0) {
try {
if (iterator.hasNext()) {
factory = iterator.next();
}
} catch (final ServiceConfigurationError | LinkageError ex) {
logDiagnostic(() -> "[LOOKUP] An exception occurred while trying to find an instance of LogFactory: [" + trim(ex.getMessage())
+ "]. Trying alternative implementations...");
}
}
} catch (final Exception ex) {
// note: if the specified LogFactory class wasn't compatible with LogFactory
// for some reason, a ClassCastException will be caught here, and attempts will
// continue to find a compatible class.
logDiagnostic(() -> "[LOOKUP] A security exception occurred while trying to create an instance of the custom factory class: ["
+ trim(ex.getMessage()) + "]. Trying alternative implementations...");
// ignore
}
}
//
// Third try looking into the properties file read earlier (if found)
if (factory == null) {
if (props != null) {
logDiagnostic(() ->
"[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
"' to define the LogFactory subclass to use...");
final String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) {
logDiagnostic(() ->
"[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
// TODO: think about whether we need to handle exceptions from newFactory
} else {
logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
}
} else {
logDiagnostic("[LOOKUP] No properties file available to determine LogFactory subclass from..");
}
}
//
// Fourth, try one of the three provided factories first from the specified classloader
// and then from the current one.
if (factory == null) {
factory = newStandardFactory(baseClassLoader);
}
if (factory == null && baseClassLoader != thisClassLoaderRef.get()) {
factory = newStandardFactory(thisClassLoaderRef.get());
}
if (factory != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("Created object " + objectId(factory) + " to manage class loader " + objectId(contextClassLoader));
}
} else {
logDiagnostic(() ->
"[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
"' via the same class loader that loaded this LogFactory class (ie not looking in the context class loader).");
// Note: unlike the above code which can try to load custom LogFactory
// implementations via the TCCL, we don't try to load the default LogFactory
// implementation via the context class loader because:
// * that can cause problems (see comments in newFactory method)
// * no-one should be customizing the code of the default class
// Yes, we do give up the ability for the child to ship a newer
// version of the LogFactoryImpl class and have it used dynamically
// by an old LogFactory class in the parent, but that isn't
// necessarily a good idea anyway.
factory = newFactory(FACTORY_DEFAULT, thisClassLoaderRef.get(), contextClassLoader);
}
if (factory != null) {
/**
* Always cache using context class loader.
*/
cacheFactory(contextClassLoader, factory);
if (props != null) {
final Enumeration<?> names = props.propertyNames();
while (names.hasMoreElements()) {
final String name = Objects.toString(names.nextElement(), null);
final String value = props.getProperty(name);
factory.setAttribute(name, value);
}
}
}
return factory;
}