A while ago I wrote some Java code as a plugin for the Go Continuous Delivery server software. It works quite nicely and won a contest - but that's not the topic for today.
Out of curiosity I have considered changing the way that dependencies have been managed. So far the application just has three direct runtime dependencies and relies on Gradle to pull in any transitive dependencies. I want to explicitly declare the dependencies and let Gradle only take care of downloading them, compilation, and assembling the jar.
A quick look at the tree of transitive dependencies shows common libraries but different versions.
+--- cd.go.plugin:go-plugin-api:14.4.0
+--- com.google.code.gson:gson:2.3.1
\--- org.cloudfoundry:cloudfoundry-client-lib:1.1.3
+--- org.springframework:spring-webmvc:4.0.5.RELEASE -> 4.0.8.RELEASE
| +--- org.springframework:spring-beans:4.0.8.RELEASE
| | \--- org.springframework:spring-core:4.0.8.RELEASE
| | \--- commons-logging:commons-logging:1.1.3
| +--- org.springframework:spring-context:4.0.8.RELEASE
| | +--- org.springframework:spring-aop:4.0.8.RELEASE
| | | +--- aopalliance:aopalliance:1.0
| | | +--- org.springframework:spring-beans:4.0.8.RELEASE (*)
| | | \--- org.springframework:spring-core:4.0.8.RELEASE (*)
| | +--- org.springframework:spring-beans:4.0.8.RELEASE (*)
| | +--- org.springframework:spring-core:4.0.8.RELEASE (*)
| | \--- org.springframework:spring-expression:4.0.8.RELEASE
| | \--- org.springframework:spring-core:4.0.8.RELEASE (*)
| +--- org.springframework:spring-core:4.0.8.RELEASE (*)
| +--- org.springframework:spring-expression:4.0.8.RELEASE (*)
| \--- org.springframework:spring-web:4.0.8.RELEASE
| +--- org.springframework:spring-aop:4.0.8.RELEASE (*)
| +--- org.springframework:spring-beans:4.0.8.RELEASE (*)
| +--- org.springframework:spring-context:4.0.8.RELEASE (*)
| \--- org.springframework:spring-core:4.0.8.RELEASE (*)
+--- org.springframework.security.oauth:spring-security-oauth2:2.0.4.RELEASE
| +--- org.springframework:spring-beans:4.0.8.RELEASE (*)
| +--- org.springframework:spring-core:4.0.8.RELEASE (*)
| +--- org.springframework:spring-context:4.0.8.RELEASE (*)
| +--- org.springframework:spring-webmvc:4.0.8.RELEASE (*)
| +--- org.springframework.security:spring-security-core:3.2.5.RELEASE
| | +--- aopalliance:aopalliance:1.0
| | +--- org.springframework:spring-aop:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-beans:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-context:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-core:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | \--- org.springframework:spring-expression:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| +--- org.springframework.security:spring-security-config:3.2.5.RELEASE
| | +--- aopalliance:aopalliance:1.0
| | +--- org.springframework.security:spring-security-core:3.2.5.RELEASE (*)
| | +--- org.springframework:spring-aop:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-beans:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-context:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | \--- org.springframework:spring-core:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| +--- org.springframework.security:spring-security-web:3.2.5.RELEASE
| | +--- aopalliance:aopalliance:1.0
| | +--- org.springframework.security:spring-security-core:3.2.5.RELEASE (*)
| | +--- org.springframework:spring-beans:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-context:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-core:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | +--- org.springframework:spring-expression:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| | \--- org.springframework:spring-web:3.2.8.RELEASE -> 4.0.8.RELEASE (*)
| +--- commons-codec:commons-codec:1.6
| \--- org.codehaus.jackson:jackson-mapper-asl:1.9.13
| \--- org.codehaus.jackson:jackson-core-asl:1.9.13
+--- org.apache.httpcomponents:httpclient:4.3.6
| +--- org.apache.httpcomponents:httpcore:4.3.3
| +--- commons-logging:commons-logging:1.1.3
| \--- commons-codec:commons-codec:1.6
+--- commons-io:commons-io:2.1
+--- com.esotericsoftware.yamlbeans:yamlbeans:1.06
+--- com.fasterxml.jackson.core:jackson-core:2.3.3
+--- com.fasterxml.jackson.core:jackson-databind:2.3.3
| +--- com.fasterxml.jackson.core:jackson-annotations:2.3.0
| \--- com.fasterxml.jackson.core:jackson-core:2.3.3
+--- org.apache.tomcat.embed:tomcat-embed-websocket:8.0.15
| \--- org.apache.tomcat.embed:tomcat-embed-core:8.0.15
+--- org.apache.tomcat:tomcat-juli:8.0.15
\--- com.google.protobuf:protobuf-java:2.6.1
Just look at the number of times a Spring library version 3.2.8 is specified, but overridden to 4.0.8 - that's a jump in major version number, which may have introduced incompatible code changes, such as removing a method from the API.
I wonder if there are any tools out there that could analyse the caller / callee relationships to offer developers some reassurance or warnings when jars get replaced by different versions in a situation like this.