Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Dependency conflicts: Classes with the same fully-qualified name are included in two different libraries, which leads to unexpected semantic behaviors #809

Closed
HelloCoCooo opened this issue Apr 3, 2019 · 3 comments

Comments

@HelloCoCooo
Copy link
Contributor

HelloCoCooo commented Apr 3, 2019

Hi, in motan-0.2.1, duplicate classes with the same fully-qualified name org.jboss.netty.channel.socket.nio.SelectorUtil are included in two different libraries, i.e., org.jboss.netty:netty:3.2.5.Final and io.netty:netty:3.7.0.Final.

According to "first declaration wins" class loading strategy, only the class in org.jboss.netty:netty:3.2.5.Final can be loaded, and that in io.netty:netty:3.7.0.Final will be shadowed.

In total, there are 15 conflicting API pairs between these two library version.

For instance, your project expects to invoke method org.jboss.netty.channel.socket.nio.namingStrategyInstance(MapperConfig config, Class implClass) in io.netty:netty:3.7.0.Final. As it has been shadowed, so that this method defined in org.jboss.netty:netty:3.2.5.Final is actually forced to be referenced via the following invocation path:

<com.weibo.motan.benchmark.FullName: java.lang.String toString()> D:\testcase\TestProject\motan-0.2.1\motan-benchmark\motan-benchmark-api\target\classes
<com.alibaba.fastjson.JSON: java.lang.String toJSONString(java.lang.Object)> D:\cEnvironment\repository\com\alibaba\fastjson\1.1.30\fastjson-1.1.30.jar
<com.alibaba.fastjson.JSON: java.lang.String toJSONString(java.lang.Object,com.alibaba.fastjson.serializer.SerializerFeature[])> D:\cEnvironment\repository\com\alibaba\fastjson\1.1.30\fastjson-1.1.30.jar
<com.alibaba.fastjson.serializer.JSONSerializer: void write(java.lang.Object)> D:\cEnvironment\repository\com\alibaba\fastjson\1.1.30\fastjson-1.1.30.jar
<com.alibaba.fastjson.serializer.CollectionSerializer: void write(com.alibaba.fastjson.serializer.JSONSerializer,java.lang.Object,java.lang.Object,java.lang.reflect.Type)> D:\cEnvironment\repository\com\alibaba\fastjson\1.1.30\fastjson-1.1.30.jar
<com.google.common.collect.MinMaxPriorityQueue$QueueIterator: java.lang.Object next()> D:\cEnvironment\repository\com\google\guava\guava\18.0\guava-18.0.jar
<org.jboss.netty.channel.socket.nio.AbstractNioChannel$WriteRequestQueue: java.lang.Object poll()> D:\cEnvironment\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar
<org.jboss.netty.channel.socket.nio.AbstractNioChannel$WriteRequestQueue: org.jboss.netty.channel.MessageEvent poll()> D:\cEnvironment\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar
<org.jboss.netty.channel.Channels: void fireChannelInterestChanged(org.jboss.netty.channel.Channel)> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.channel.DefaultChannelPipeline: void sendUpstream(org.jboss.netty.channel.ChannelEvent)> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.channel.DefaultChannelPipeline: void sendUpstream(org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext,org.jboss.netty.channel.ChannelEvent)> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.handler.execution.ExecutionHandler: void handleUpstream(org.jboss.netty.channel.ChannelHandlerContext,org.jboss.netty.channel.ChannelEvent)> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.handler.ssl.ImmediateExecutor: void execute(java.lang.Runnable)> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.channel.socket.nio.NioDatagramWorker: void run()> D:\cEnvironment\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar
<org.jboss.netty.channel.socket.nio.SelectorUtil: namingStrategyInstance(MapperConfig config, Class implClass)>

Although both of these two conflicting classes contain the referenced method (with the same signature), they have completely different implementations. This issue will not lead to runtime crashes, but it can introduce inconsistent semantic hehavior by changing the control flows and data flows.

Solution:

Reverse the declaration order of org.jboss.netty:netty:3.2.5.Final and io.netty:netty:3.7.0.Final in pom.xml.
Then, according to "first declaration wins" class loading strategy, class org.jboss.netty.channel.socket.nio.SelectorUtil in io.netty:netty:3.7.0.Final can be loaded (the version motan 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 14 conflicting API pairs can be found in the following attachment.
15 conflicting API pairs in project Motan.txt

Dependency tree-----

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ motan-benchmark-api ---
[INFO] com.weibo:motan-benchmark-api:jar:0.2.1
....
[INFO] +- com.weibo:motan-transport-netty:jar:0.2.1:compile
[INFO] | +- org.jboss.netty:netty:jar:3.2.5.Final:compile
[INFO] | +- (com.weibo:motan-core:jar:0.2.1:compile - omitted for duplicate)
[INFO] | +- (org.slf4j:slf4j-api:jar:1.5.8:compile - omitted for duplicate)
[INFO] | +- (org.slf4j:slf4j-log4j12:jar:1.5.8:compile - omitted for duplicate)
[INFO] | +- (log4j:log4j:jar:1.2.14:compile - omitted for duplicate)
[INFO] | +- (org.apache.commons:commons-lang3:jar:3.1:compile - omitted for duplicate)
[INFO] | - (commons-codec:commons-codec:jar:1.4:compile - omitted for duplicate)
[INFO] +- com.weibo:motan-registry-zookeeper:jar:0.2.1:compile
[INFO] | +- (com.weibo:motan-core:jar:0.2.1:compile - omitted for duplicate)
[INFO] | +- org.apache.zookeeper:zookeeper:jar:3.4.8:compile
[INFO] | | +- (org.slf4j:slf4j-api:jar:1.6.1:compile - omitted for conflict with 1.5.8)
[INFO] | | +- (org.slf4j:slf4j-log4j12:jar:1.6.1:compile - omitted for conflict with 1.5.8)
[INFO] | | +- (log4j:log4j:jar:1.2.16:compile - omitted for conflict with 1.2.14)
[INFO] | | +- jline:jline:jar:0.9.94:compile
[INFO] | | | - (junit:junit:jar:3.8.1:compile - omitted for conflict with 4.5)
[INFO] | | - io.netty:netty:jar:3.7.0.Final:compile
...

Thanks!
Best regards,
Coco

@HelloCoCooo
Copy link
Contributor Author

HelloCoCooo commented Apr 3, 2019

//Loaded version
public PropertyNamingStrategy namingStrategyInstance(MapperConfig config, Class implClass){
return null;
}
//Shadowed version
public PropertyNamingStrategy namingStrategyInstance(MapperConfig config, Class implClass){
return (PropertyNamingStrategy) this.beanFactory.createBean(implClass);
}
//Code snippet of entry method in host project
ropertyNamingStrategy propertyNamingStrategy = A.namingStrategyInstance(entryClass);
propertyNamingStrategy.toString();

Using the following test case to run on these two versions of methods separately starting from the entry method in your project, then we can get a Java.lang.NullPointerException on the loaded version.
Testcaseforweibocommotan.txt

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

@HelloCoCooo HelloCoCooo changed the title Classes with the same fully-qualified name are included in two different libraries, which leads to unexpected semantic behaviors Dependency conflicts: Classes with the same fully-qualified name are included in two different libraries, which leads to unexpected semantic behaviors Apr 5, 2019
@sunnights
Copy link
Collaborator

Sure, feel free to fix it in motan-benchmark.
Sorry for the late reply due to vacation.

@HelloCoCooo
Copy link
Contributor Author

This issue has been fixed.
Thank you for your support.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants