Open HelloCoCooo opened 4 years ago
Code snippet of org.apache.commons.logging.LogFactory.getFactory in commons-logging:commons-logging:1.2 (shadowed but expected to invoke method):
public static LogFactory getFactory() throws LogConfigurationException { //line 417
// Identify the class loader we will be using
ClassLoader contextClassLoader = getContextClassLoaderInternal();
if (contextClassLoader == null) {
// This is an odd enough situation to report about. This
// output will be a nuisance on JDK1.1, as the system
// classloader is null in that environment.
if (isDiagnosticsEnabled()) {
logDiagnostic("Context classloader 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 classloader " +
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.
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).
ClassLoader baseClassLoader = contextClassLoader;
if (props != null) {
String useTCCLStr = props.getProperty(TCCL_KEY);
if (useTCCLStr != null) {
// The Boolean.valueOf(useTCCLStr).booleanValue() formulation
// is required for Java 1.2 compatibility.
if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
// Don't use current context classloader when locating any
// LogFactory or Log classes, just use the class that loaded
// this abstract class. When this class is deployed in a shared
// classpath of a container, it means webapps cannot deploy their
// own logging implementations. It also means that it is up to the
// implementation whether to load library-specific config files
// from the TCCL or not.
baseClassLoader = thisClassLoader;
}
}
}
// Determine which concrete LogFactory subclass to use.
// First, try a global system property
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
"] to define the LogFactory subclass to use...");
}
try {
String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
"' as specified by system property " + FACTORY_PROPERTY);
}
factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
}
}
} catch (SecurityException e) {
if (isDiagnosticsEnabled()) {
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 (RuntimeException e) {
// This is not consistent with the behaviour 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.
if (isDiagnosticsEnabled()) {
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 JDK1.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) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
"] to define the LogFactory subclass to use...");
}
try {
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
if( is != null ) {
// This code is needed by EBCDIC and other strange systems.
// It's a fix for bugs reported in xerces
BufferedReader rd;
try {
rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
} catch (java.io.UnsupportedEncodingException e) {
rd = new BufferedReader(new InputStreamReader(is));
}
String factoryClassName = rd.readLine();
rd.close();
if (factoryClassName != null && ! "".equals(factoryClassName)) {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Creating an instance of LogFactory class " +
factoryClassName +
" as specified by file '" + SERVICE_ID +
"' which was present in the path of the context classloader.");
}
factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
}
} else {
// is == null
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
}
}
} catch (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.
if (isDiagnosticsEnabled()) {
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) {
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
"' to define the LogFactory subclass to use...");
}
String factoryClass = props.getProperty(FACTORY_PROPERTY);
if (factoryClass != null) {
if (isDiagnosticsEnabled()) {
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 {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
}
}
} else {
if (isDiagnosticsEnabled()) {
logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");
}
}
}
// Fourth, try the fallback implementation class
if (factory == null) {
if (isDiagnosticsEnabled()) {
logDiagnostic(
"[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
"' via the same classloader that loaded this LogFactory" +
" class (ie not looking in the context classloader).");
}
// 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 classloader because:
// * that can cause problems (see comments in newFactory method)
// * no-one should be customising 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, thisClassLoader, contextClassLoader);
}
if (factory != null) {
/**
* Always cache using context class loader.
*/
cacheFactory(contextClassLoader, factory);
if (props != null) {
Enumeration names = props.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
String value = props.getProperty(name);
factory.setAttribute(name, value);
}
}
}
return factory;
}
Code snippet of org.apache.commons.logging.LogFactory.getFactory in org.slf4j:jcl-over-slf4j:1.7.21 (loaded version):
static LogFactory logFactory = new SLF4JLogFactory(); //line 41
......
public static LogFactory getFactory() throws LogConfigurationException { //line 257
return logFactory;
}
As a result, these conflicting method included in commons-logging:commons-logging:1.2 deals with different cases, which changes the control flows and data flows. So being forced to use these methods in org.slf4j:jcl-over-slf4j:1.7.21 may lead to inconsisitent semantic behaviors.
@nosqlcoco May I pull a request to fix it?
@nosqlcoco May I pull a request to fix it?
Hi @HelloCoCooo. you can pull a request, I will testing in my machine. thanks.
Hi, in springboot-weapp-demo, duplicate classes with the same fully-qualified name org.apache.commons.logging.LogFactory.getFactory are included in two different libraries, i.e., commons-logging:commons-logging:1.2 and org.slf4j:jcl-over-slf4j:1.7.21.
According to "first declaration wins" class loading strategy, only this class in org.slf4j:jcl-over-slf4j:1.7.21 can be loaded, and that in commons-logging:commons-logging:1.2 will be shadowed.
By further analyzing, your project expects to invoke method org.apache.commons.logging.LogFactory.getFactory in commons-logging:commons-logging:1.2. As it has been shadowed, so that this method defined in org.slf4j:jcl-over-slf4j:1.7.21 are actually forced to be referenced via the following invocation path:
Workaround solution: An easy way to workaround the problem is reversing the declaration order of these two libraries (i.e., reverse the declaration order of httpclient and maven-resolver-transport-http) in pom file. Then, according to "first declaration wins" class loading strategy, class org.apache.commons.logging.LogFactory.getFactory in commons-logging:commons-logging:1.2 can be loaded (the version that springboot-weapp-demo expects to reference by static analysis). This fix will not affect other libraries or class, except the above duplicate class.
Dependency tree---
[INFO] com.wxapp:wxapp:jar:1.0.0 [INFO] +- org.springframework.boot:spring-boot-starter:jar:1.4.2.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot:jar:1.4.2.RELEASE:compile [INFO] | | - org.springframework:spring-context:jar:4.3.4.RELEASE:compile [INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:1.4.2.RELEASE:compile [INFO] | +- org.springframework:spring-core:jar:4.3.4.RELEASE:compile [INFO] | - org.yaml:snakeyaml:jar:1.17:runtime [INFO] +- org.springframework.boot:spring-boot-starter-websocket:jar:1.4.2.RELEASE:compile [INFO] | +- org.springframework:spring-messaging:jar:4.3.4.RELEASE:compile [INFO] | | - org.springframework:spring-beans:jar:4.3.4.RELEASE:compile [INFO] | - org.springframework:spring-websocket:jar:4.3.4.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-starter-data-mongodb:jar:1.4.2.RELEASE:compile [INFO] | +- org.mongodb:mongodb-driver:jar:3.2.2:compile [INFO] | | +- org.mongodb:mongodb-driver-core:jar:3.2.2:compile [INFO] | | - org.mongodb:bson:jar:3.2.2:compile [INFO] | - org.springframework.data:spring-data-mongodb:jar:1.9.5.RELEASE:compile [INFO] | +- org.springframework:spring-tx:jar:4.3.4.RELEASE:compile [INFO] | +- org.springframework:spring-expression:jar:4.3.4.RELEASE:compile [INFO] | +- org.springframework.data:spring-data-commons:jar:1.12.5.RELEASE:compile [INFO] | - org.slf4j:jcl-over-slf4j:jar:1.7.21:runtime ...... [INFO] +- com.alibaba:fastjson:jar:1.2.31:compile [INFO] +- org.projectlombok:lombok:jar:1.16.10:compile [INFO] +- com.google.guava:guava:jar:19.0:compile [INFO] +- commons-logging:commons-logging:jar:1.2:compile ......
Thank you very much. Best, Coco