nutzam / nutzboot

NutzBoot,简称NB,是可靠的企业级微服务框架,提供自动配置,嵌入式web服务,分布式会话,服务治理,负载均衡,hystrix,RPC等一篮子解决方案
https://nutz.io
Apache License 2.0
501 stars 138 forks source link

Dependency Conflict: duplicate classes "org.slf4j.LoggerFactory.getILoggerFactory" in different JARs, have different implementations #199

Closed HelloCoCooo closed 4 years ago

HelloCoCooo commented 4 years ago

Hi, in nutzboot-2.3.8.v20191031(/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-freemarker/ module), duplicate classes with the same fully-qualified name org.slf4j.LoggerFactory.getILoggerFactory are included in two different libraries, i.e., org.slf4j:slf4j-api:1.7.28 and org.gandon.tomcat:juli-to-slf4j:1.1.1.

According to "first declaration wins" class loading strategy, only this class in org.gandon.tomcat:juli-to-slf4j:1.1.1 can be loaded, and that in org.slf4j:slf4j-api:1.7.28 will be shadowed.

In total, there are 2 conflicting API pairs with the same signature between these two conflicting library versions.

For instance, your project expects to invoke method org.slf4j.LoggerFactory.getILoggerFactory in org.slf4j:slf4j-api:1.7.28. As it has been shadowed, so that this method defined in org.gandon.tomcat:juli-to-slf4j:1.1.1 are actually forced to be referenced via the following invocation path:

<io.nutz.demo.simple.MainLauncher: index()Ljava/lang/Object;> /root/sensor/unzip/nutzboot-2.3.8.v20191031/nutzboot-demo/nutzboot-demo-simple/nutzboot-demo-simple-freemarker/target/classes
<org.nutz.lang.util.NutMap: <clinit>()V> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.lang.util.Regex: getPattern(Ljava/lang/String;)Ljava/util/regex/Pattern;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.repo.cache.simple.LRUCache: get(Ljava/lang/Object;)Ljava/lang/Object;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.dao.util.RelationObjectMap: get(Ljava/lang/Object;)Ljava/lang/Object;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.dao.impl.entity.field.AbstractEntityField: getValue(Ljava/lang/Object;)Ljava/lang/Object;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.lang.eject.EjectFromMap: eject(Ljava/lang/Object;)Ljava/lang/Object;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.dao.entity.Record: get(Ljava/lang/Object;)Ljava/lang/Object;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.resource.impl.JarResourceLocation$1: toString()Ljava/lang/String;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.resource.impl.JarResourceLocation: <clinit>()V> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.log.Logs: get()Lorg/nutz/log/Log;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.nutz.log.impl.Slf4jLogAdapter: getLogger(Ljava/lang/String;)Lorg/nutz/log/Log;> /root/.m2/repository/org/nutz/nutz/1.r.68.v20191031/nutz-1.r.68.v20191031.jar
<org.slf4j.LoggerFactory: getLogger(Ljava/lang/String;)Lorg/slf4j/Logger;> /root/.m2/repository/org/slf4j/slf4j-api/1.7.28/slf4j-api-1.7.28.jar
<org.slf4j.LoggerFactory: getILoggerFactory()Lorg/slf4j/ILoggerFactory;>

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.slf4j.LoggerFactory in org.slf4j:slf4j-api:1.7.28 can be loaded (the version that nutzboot-2.3.8.v20191031 expects to reference by static analysis). This fix will not affect other libraries or class, except the above duplicate class.

The detailed informantion of the remaining 1 conflicting API pair can be found in the following attachment. 2 conflicting API pairs in Nutzboot.txt

Dependency tree---

[INFO] org.nutz:nutzboot-demo-simple-freemarker:jar:2.3.8.v20191031 [INFO] +- org.nutz:nutzboot-starter-nutz-mvc:jar:2.3.8.v20191031:compile [INFO] | - (org.nutz:nutzboot-core:jar:2.3.8.v20191031:compile - omitted for duplicate) [INFO] +- org.nutz:nutzboot-starter-tomcat:jar:2.3.8.v20191031:compile [INFO] | +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.46:compile [INFO] | | - org.apache.tomcat:tomcat-annotations-api:jar:8.5.46:compile [INFO] | +- org.nutz:nutzboot-servlet3:jar:2.3.8.v20191031:compile [INFO] | | +- org.nutz:nutz-plugins-websocket:jar:1.r.68.v20191031:compile [INFO] | | | - (org.nutz:nutz:jar:1.r.68.v20191031:compile - omitted for duplicate) [INFO] | | +- javax.annotation:javax.annotation-api:jar:1.2:compile [INFO] | | - (org.nutz:nutzboot-core:jar:2.3.8.v20191031:compile - omitted for duplicate) [INFO] | +- org.gandon.tomcat:juli-to-slf4j:jar:1.1.1:compile [INFO] | - (org.nutz:nutzboot-core:jar:2.3.8.v20191031:compile - omitted for duplicate) [INFO] +- org.nutz:nutzboot-starter-freemarker:jar:2.3.8.v20191031:compile [INFO] | +- org.freemarker:freemarker:jar:2.3.28:compile [INFO] | - (org.nutz:nutzboot-core:jar:2.3.8.v20191031:compile - omitted for duplicate) [INFO] +- org.slf4j:slf4j-log4j12:jar:1.7.28:compile [INFO] | +- org.slf4j:slf4j-api:jar:1.7.28:compile [INFO] | - log4j:log4j:jar:1.2.17:compile [INFO] - org.nutz:nutzboot-core:jar:2.3.8.v20191031:compile [INFO] +- org.nutz:nutz:jar:1.r.68.v20191031:compile [INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:compile [INFO] - (org.slf4j:slf4j-api:jar:1.7.28:compile - omitted for duplicate)

Thank you very much. Best, Coco

HelloCoCooo commented 4 years ago

Code snippet of org.slf4j.LoggerFactory.getILoggerFactory in org.slf4j:slf4j-api:1.7.28 (shadowed but expected to invoke method):

public static ILoggerFactory getILoggerFactory() {  //line 407
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            synchronized (LoggerFactory.class) {
                if (INITIALIZATION_STATE == UNINITIALIZED) {
                    INITIALIZATION_STATE = ONGOING_INITIALIZATION;
                    performInitialization();
                }
            }
        }
        switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION:
            return StaticLoggerBinder.getSingleton().getLoggerFactory();
...

Code snippet of org.slf4j.LoggerFactory.getILoggerFactory inorg.gandon.tomcat:juli-to-slf4j:1.1.1 (loaded version):

public static ILoggerFactory getILoggerFactory() {  //line 405
        if (INITIALIZATION_STATE == UNINITIALIZED) {
            INITIALIZATION_STATE = ONGOING_INITIALIZATION;
            performInitialization();
        }
        switch (INITIALIZATION_STATE) {
        case SUCCESSFUL_INITIALIZATION:
            return getLoggerFactory();
...

As a result, these conflicting method included in org.slf4j:slf4j-api:1.7.28 deals with different cases, which changes the control flows and data flows. So being forced to use these methods in org.gandon.tomcat:juli-to-slf4j:1.1.1 may lead to inconsisitent semantic behaviors.

HelloCoCooo commented 4 years ago

Using the following test case to run on these two versions of methods separately starting from the entry method Main(String, int); in your project, then we can find the state of variable NutMap referenced by this entry method is changed.

Tese case of Nutzboot.txt

Please check whether the changes of this variable value will affect your semantic behaviors.

ywjno commented 4 years ago

@HelloCoCooo はい、できます。リクエスト大歓迎です。