djpanda
djpanda

Reputation: 895

How to consume a jar that has spring as a packaged dependency in another spring project with higher spring version?

There are numerous questions on 'How to' add one spring project as a dependency into another spring project. But what I am looking for is solving a specific problem once we have successfully answered the 'How to' part.

Say we have two spring based multi-module maven projects. Project-A and Project-B. SpringFramework version used in Project-A is 4.2.3. Some modules from Project-A are combined and built into a uber-jar using the shade-plugin with NO special configuration. What this means is, if I open the uber-jar, I can find all the dependencies (I think even the transitive dependencies) are packaged into the uber-jar. (Sure, this might be ugly, but I cannot change this uber-jar. Refactoring and breaking it up into smaller consumable dependencies is out of question for x,y,z reasons). Access to underlying resources is through the API's that one of the modules in uber-jar exposes. So I am stuck with this.

Now, I want to use this uber-jar from project-A as a dependency in project-B. I used the install plugin and attached installing this uber-jar during validate phase.

However, in Project-B I wish to use spring-data-jdbc. Now, spring-data-jdbc is a fairly new project and as described here in the reference doc, the minimum spring-framework version required is 5.2.6.RELEASE.

So when I added the uber-jar as the dependency, although I could do a successful maven clean install, I ran into some NoSuchMethodException like this: java.lang.nosuchmethoderror:org.springframework.util.reflectionutils.accessibleconstructor(ljava/lang/class;[ljava/lang/class;) when the artifact was being deployed into tomcat 8.5

Which is similar to the problem described in this question. So using maven dependency plugin I looked at the dependency tree of Project-B, but I could not see spring-version 4.2.3 being listed. No transitive dependencies were being listed under the uber-jar.

I even looked at the build classpath under different goals and still could only find spring-version 5.2.6.RELEASE from Project-B being listed. However, it is pretty clear that during deployment, I am only consuming spring-farmework 4.2.3 from the uber-jar. Because the methid which cannot be found is available only from spring 5.0 onwards as described in the java-doc here

Upgrading the spring-version of Project-A is not possible (again for x,y,z reasons). I tried to exclude all dependencies from the uber-jar when adding the uber-jar as a dependency, but that did not help.

Questions:

  1. Can we tell project-B to use spring-framework dependency (5.2.6.RELEASE) of its project and not use the one from the uber-jar while letting the classes from project-A (in the uber jar) use spring-framework packaged within the uber-jar?
  2. Its unsettling to even ask above question because I think I know that both versions are being added onto the class path. And during deployment the container is picking up the first one it sees causing this problem. Is this correct?
  3. In the model I have described, (like building the uber-jar) how does dependency management work? Meaning, since I can see all the dependencies packaged into the uber-jar, when I use this uber-jar as a dependency, do all the packaged dependencies (jars) also get added to the consuming projects classpath?
  4. I am (maven) installing the uber-jar while builindg project-b during validate phase, and then compiling project-b, is this whats causing the problem?
  5. If this is a messed up situation, can I get a recommendation for an alternative to spring-data-jdbc? so that I can totally ditch the idea of using higher spring version in project-b. I don't need all the features of Java Persistence API like lazy loading, caching and all that. I just want to use the repository pattern to easily perform some CRUD operations with simple transaction management which is why I chose spring-data-jdbc.

I have spent an entire day reading, learning, researching everything from how maven dependency resolution happens, how the shade plugin works and so much more. Feels like one big puzzle that I cannot put it all together. Thanks for any guidance.

Update: So the dependency tree would look like below:

org.railomaya.uberJarModule:uberJar:jar:2.0
[INFO] +- org.railomaya:OtherModule_1:jar:2.0
[INFO] \- org.railomaya:OtherModule_2:jar:1.0:compile
[INFO] +- org.railomaya:otherModule_3:jar:1.0-SNAPSHOT:compile
[INFO] |  +- org.springframework:spring-jms:jar:4.2.3.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-messaging:jar:4.2.3.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-tx:jar:4.2.3.RELEASE:compile
[INFO] |  \- org.apache.activemq:activemq-spring:jar:5.13.2:compile
[INFO] +- org.springframework:spring-context-support:jar:4.2.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:4.2.3.RELEASE:compile
[INFO] |  +- org.springframework:spring-context:jar:4.2.3.RELEASE:compile
[INFO] |  |  +- org.springframework:spring-aop:jar:4.2.3.RELEASE:compile
[INFO] |  |  |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  |  \- org.springframework:spring-expression:jar:4.2.3.RELEASE:compile
[INFO] |  \- org.springframework:spring-core:jar:4.2.3.RELEASE:compile
[INFO] |     \- commons-logging:commons-logging:jar:1.2:compile
[INFO] +- org.springframework.boot:spring-boot-starter:jar:1.5.1.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot:jar:1.5.1.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.1.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.1.RELEASE:compile
[INFO] |  |  +- ch.qos.logback:logback-classic:jar:1.1.9:compile
[INFO] |  |  |  \- ch.qos.logback:logback-core:jar:1.1.9:compile
[INFO] |  |  +- org.slf4j:jcl-over-slf4j:jar:1.7.22:compile
[INFO] |  |  +- org.slf4j:jul-to-slf4j:jar:1.7.22:compile
[INFO] |  |  \- org.slf4j:log4j-over-slf4j:jar:1.7.22:compile
[INFO] |  \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] +- org.springframework:spring-test:jar:4.2.3.RELEASE:test
[INFO] +- javax.servlet:servlet-api:jar:2.5:compile
[INFO] +- org.apache.commons:commons-lang3:jar:3.3.1:compile
[INFO] +- commons-io:commons-io:jar:2.4:compile

Project-B dependency tree where the above uber-jar is used as dependency:

org.railomaya:project-B:war:2.0-SNAPSHOT
[INFO] +- org.railomaya.uberJarModule:uberJar:jar:2.0:compile
[INFO] +- org.apache.commons:commons-dbcp2:jar:2.0.1:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.3:compile
[INFO] +- org.apache.commons:commons-pool2:jar:2.2:compile
[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.1:compile
[INFO] +- org.apache.logging.log4j:log4j-core:jar:2.1:compile
[INFO] +- org.apache.logging.log4j:log4j-web:jar:2.1:compile
[INFO] +- javax.mail:mail:jar:1.4.5:compile
[INFO] |  \- javax.activation:activation:jar:1.1:compile
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
[INFO] +- mysql:mysql-connector-java:jar:5.1.26:compile
[INFO] +- org.quartz-scheduler:quartz:jar:2.2.1:compile
[INFO] |  +- c3p0:c3p0:jar:0.9.1.1:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.6.6:compile
[INFO] +- org.springframework:spring-beans:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:5.2.6.RELEASE:compile
[INFO] |  \- org.springframework:spring-aop:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-context-support:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-core:jar:5.2.6.RELEASE:compile
[INFO] |  \- org.springframework:spring-jcl:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-expression:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-jdbc:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-test:jar:5.2.6.RELEASE:test
[INFO] +- org.springframework:spring-tx:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-web:jar:5.2.6.RELEASE:compile
[INFO] \- org.springframework:spring-webmvc:jar:5.2.6.RELEASE:compile

Upvotes: 0

Views: 530

Answers (1)

Jens Schauder
Jens Schauder

Reputation: 82008

Can we tell project-B to use spring-framework dependency (5.2.6.RELEASE) of its project and not use the one from the uber-jar while letting the classes from project-A (in the uber jar) use spring-framework packaged within the uber-jar?

Not easily. See below.

I think I know that both versions are being added onto the class path. And during deployment the container is picking up the first one it sees causing this problem. Is this correct?

Yes.

when I use this uber-jar as a dependency, do all the packaged dependencies (jars) also get added to the consuming projects classpath?

Yes.

I am (maven) installing the uber-jar while builindg project-b during validate phase, and then compiling project-b, is this whats causing the problem?

Yes.

If this is a messed up situation, can I get a recommendation for an alternative to spring-data-jdbc?

You can always use Springs JdbcTemplate and implement your repositories yourself using it. You might even consider modelling aggregates as Spring Data JDBC does.

Back to the first question:

First lets make clear that there is no guarantee that Project A will work with a newer Spring version, but lets assume it does.

What you need to to is to take apart the ueber-jar i.e. remove everything that isn't Project A itself, and install that in you maven repository, then depend on that and also depend on the right version (TM) of Spring and all other dependencies.

Upvotes: 1

Related Questions