Gradle Dependency of Dependency Substitution - spring

I have a spring-boot gradle project. It has a dependency of a dependency of a dependency that I cannot use for internal reasons. I've forked that module and published it to my repo with its own version number that I want to be used in my project.
Part of the dependency tree looks like:
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web:2.7.2
| +--- org.springframework.boot:spring-boot-starter-tomcat:2.7.2
| | +--- jakarta.annotation:jakarta.annotation-api:1.3.5
| | +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.65
| | +--- org.apache.tomcat.embed:tomcat-embed-el:9.0.65
| | \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65
I need to force my project to use a custom version of those bottom two modules, let's call the new versions org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM and org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM.
I've never attempted this. I'm trying all different setups in my project's build.gradle file like
configurations { main }
configurations.main.resolutionStrategy.dependencySubstitution {
substitute module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM')
substitute module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM')
}
But the 9.0.65 versions keep getting injected. So my question(s) are:
Am I using dependencySubstitution incorrectly, and where in the build.gradle should it be?
Does dependencySubstitution work for dependencies of dependencies (ad nauseam), or only for direct dependencies?
Short of forking all these dependencies to declare their dependencies, is there any way I can force my Gradle to never use version 9.0.65 and only ever use 9.0.65.CUSTOM no matter its location in the dependency tree?

Assuming your configurations are setup properly and the coordinates for your custom modules are correct, you should be able to do
allprojects {
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute(module("org.apache.tomcat.embed:tomcat-embed-el")).with(module("org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM"))
substitute(module("org.apache.tomcat.embed:tomcat-embed-websocket")).with(module("org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM"))
}
}
}
}
I would double check you have your repositories, configurations and that you are doing this dependency substitution in the module that needs to substitute the given modules for custom ones. Otherwise you may run into errors.

Related

Enforce the highest version from among several conflicting transitive dependencies in gradle

I work on a large project with multiple services and libraries, mostly in grails, with gradle builder. I'm trying to update a library (say logback) for security reasons.
I already updated it in one of our libraries (say our-logger), like so:
#our-logger/build.gradle
...
dependencies {
...
compile 'ch.qos.logback:logback-classic:1.2.3'
...
}
when I update a service (say our-service) to use the new version of our-logger i get logback included from other libraries, and gradle chooses the lower version coming through cobertura and some other dependencies, instead of the higher version coming through our-logger.
#our-service/build.gradle
...
apply plugin: 'cobertura'
...
dependencies {
...
compile 'our-logger:9.99' # safe now with logback-1.2.3
...
}
~/our-service $ ./gradlew dependencies
...
cobertura
\--- net.sourceforge.cobertura:cobertura:2.1.1
+--- ch.qos.logback:logback-classic:1.0.13 -> 1.1.11
| \--- ch.qos.logback:logback-core:1.1.11
...
compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
+--- org.grails:grails-dependencies:3.3.8
| +--- org.springframework.boot:spring-boot-starter-logging:1.4.2.RELEASE -> 1.5.15.RELEASE
| | +--- ch.qos.logback:logback-classic:1.1.11
| | | +--- ch.qos.logback:logback-core:1.1.11
...
+--- our-logger:9.99
| +--- ch.qos.logback:logback-classic:1.2.3 -> 1.1.11 (*)
How do I enforce logback-1.2.3 without explicitly declaring it in all services?
The gradle docs file this under Advanced Dependency Management. You should be able to satisfy your goal using excludes. There are other ways too Gradle Docs
compile(“some:other:dependency”) {
exclude group: 'ch.qos.logback', module: 'logback-classic'
}

Gradle is selecting wrong dependency version

I am seeing some weirdness in my Gradle build. I have a Spring Boot app (which uses Gradle for its build) and am trying to pull in both the Hibernate Validator as well as Hibernate Core. Here's the dependencies declaration in my build.gradle file:
dependencies {
compile('org.springframework.boot:spring-boot-starter-web') {
exclude module: 'spring-boot-starter-tomcat'
}
compile(
'org.codehaus.groovy:groovy-all:2.4.12'
,'com.google.inject:guice:4.1.0'
,'ch.qos.logback:logback-classic:1.2.3'
,'org.slf4j:jul-to-slf4j:1.7.25'
,'org.apache.logging.log4j:log4j-to-slf4j:2.9.1'
,'commons-cli:commons-cli:1.4'
,'org.apache.commons:commons-lang3:3.7'
,'io.dropwizard.metrics:metrics-core:3.2.5'
,'io.dropwizard.metrics:metrics-json:3.2.5'
,'org.springframework.security:spring-security-jwt:1.0.9.RELEASE'
,'org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE'
,'io.jsonwebtoken:jjwt:0.9.0'
,'org.hibernate:hibernate-validator:6.0.7.Final'
,'mysql:mysql-connector-java:6.0.6'
,'org.hibernate:hibernate-core:5.2.12.Final'
,'com.h2database:h2:1.4.196'
,'org.springframework.boot:spring-boot-starter-jetty'
,'org.springframework.boot:spring-boot-starter-actuator'
,'org.springframework.boot:spring-boot-starter-security'
,'org.springframework.boot:spring-boot-starter-data-rest'
,'org.springframework.boot:spring-boot-starter-data-jpa'
)
dev('org.springframework.boot:spring-boot-devtools')
testCompile(
'org.spockframework:spock-core:1.0-groovy-2.4'
,'junit:junit:4.12'
)
}
When I run ./gradlew dependencies I get a huge output, but from the compile dependencies tree I see the following:
| +--- org.springframework.boot:spring-boot-starter:1.5.8.RELEASE
| +--- org.hibernate:hibernate-validator:5.3.5.Final -> 6.0.7.Final
| | \--- org.hibernate.validator:hibernate-validator:6.0.7.Final
| | +--- javax.validation:validation-api:2.0.1.Final -> 1.1.0.Final
| | +--- org.jboss.logging:jboss-logging:3.3.0.Final -> 3.3.1.Final
| | \--- com.fasterxml:classmate:1.3.1 -> 1.3.4
So to me it looks like spring-boot-starter:1.5.8.RELEASE is pulling in validation-api:2.0.1.Final but for some reason Gradle is selecting validation-api:1.1.0.Final for me...am I reading that correctly? In my IDE compile classpath I only see validation-api:1.1.0.Final, not 2.0.1.Final.
Why is Gradle selecting 1.1.0.Final instead of 2.0.1.Final? I ask because Hibernate Validator 5.x is not compatible with Validation API 1.x and when my app runs I get all sorts of Hibernate Validation-related errors.
Update
Some more output:
gradle -q dependencyInsight --configuration compile --dependency validation-api
javax.validation:validation-api:1.1.0.Final (selected by rule)
javax.validation:validation-api:2.0.1.Final -> 1.1.0.Final
\--- org.hibernate.validator:hibernate-validator:6.0.7.Final
\--- org.hibernate:hibernate-validator:6.0.7.Final
+--- compile
\--- org.springframework.boot:spring-boot-starter-web:1.5.8.RELEASE
+--- compile
\--- org.springframework.boot:spring-boot-starter-data-rest:1.5.8.RELEASE
\--- compile
The full compile configuration output can be found here.
The version is enforced by Spring Boot.
See the POM for the Spring Boot dependencies: http://search.maven.org/remotecontent?filepath=org/springframework/boot/spring-boot-dependencies/1.5.8.RELEASE/spring-boot-dependencies-1.5.8.RELEASE.pom and look for "javax-validation.version".
See https://docs.spring.io/platform/docs/Brussels-SR4/reference/html/getting-started-overriding-versions.html for more information on how to override Spring Boot versions.
I would recommend overriding directly "javax-validation.version" and "hibernate-validator.version" instead of redefining the dependencies.
There is some conflict with another dependency that is pulling the older 1.1.0 in the compile classpath.
This means that some other library which has higher priority in gradle build order is dependent the older 1.1.0 version.
You can see here more info on how to specify the gradle build order.
I met similar problems, then I found it caused by using the Dependency management in gradle:
plugins {
...
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Hoxton.SR8"
}
}
this dependency management impact the transitive dependency version solution. after comment it out. all the version is correct.

Determine source of dependency version override in Gradle

I have declared a dependency for org.spockframework:spock-spring:1.1.d91bf785-groovy-2.4, but in runtime scope it is overridden to 1.0-groovy-2.4. Is there a way to figure out which dependency is overriding it?
1.0-groovy-2.4 is not present in any gradle file locally, and it is not visible when I run gradle dependencies in the shared-config project.
Here's gradle dependencies from the main-web project:
testCompile
[...]
+--- project :shared-config
| [...]
| +--- org.spockframework:spock-spring:1.1.d91bf785-groovy-2.4
[...]
runtime
[...]
+--- project :shared-config
| [...]
| +--- org.spockframework:spock-spring:1.1.d91bf785-groovy-2.4 -> 1.0-groovy-2.4
| | +--- org.spockframework:spock-core:1.0-groovy-2.4 -> 1.1.d91bf785-groovy-2.4
| | \--- org.codehaus.groovy:groovy-all:2.4.1 -> 2.4.12
dependencyInsight gives some insight:
$ gradle dependencyInsight --dependency org.spockframework:spock-spring:1.0-groovy-2.4 --configuration runtime
> Task :main-web:dependencyInsight
org.spockframework:spock-spring:1.0-groovy-2.4 (selected by rule)
org.spockframework:spock-spring:1.1.d91bf785-groovy-2.4 -> 1.0-groovy-2.4
\--- project :shared-config
\--- runtime
But the same command in the shared-config project doesn't yield any results:
No dependencies matching given input were found in configuration ':shared-config:runtime'
I have tried overriding the version without success:
configurations.all {
resolutionStrategy {
force "org.spockframework:spock-spring:1.1.d91bf785-groovy-2.4"
}
}
Seems like you are using spring boot as a parent or spring boot dependencies as a bom (dependency manager)
by default in latest spring boot versions 1.5 + spock.version is set to 1.0-groovy-2.4 that's why spock-core still have old version.
In order to fix this in gradle you need to override property spock.version in your gradle app. By adding spock.version = 1.1-groovy-2.4 to gradle.properties file.
Visit Spring doc for overriding dep properties to find more.

How do I debug why Gradle is using a different version of the library I specified in a dependent sub-project?

I have a Gradle build that splits up my JOOQ generated code into a separate jooq subproject that my api-svc project then depends on.
When I upgrade the jooq subproject to 3.10.1, for some reason Gradle decides to use 3.9.5 to build the api-svc instead. I have no idea why, and I have to override by adding an explicit dependency in my api-svc project to work around it.
How can I debug what's going on to see why Gradle is overriding the version?
My JOOQ subproject's definition:
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
...
classpath 'org.jooq:jooq-codegen:3.10.1'
...
}
}
...
dependencies {
compile 'org.jooq:jooq:3.10.1'
}
...
Here's the full version of the jooq subproject: https://bitbucket.org/snippets/shorn/64RnL5
And the inclusion in the api-svc project:
dependencies {
compile project(":idl")
compile project(":api-svc:jooq")
...
various other compile dependencies, spring-boot, etc.
When I do ./gradlew :api-svc:jooq:dependencies, it says:
------------------------------------------------------------
Project :api-svc:jooq
------------------------------------------------------------
...
compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
\--- org.jooq:jooq:3.10.1
...
But, when I do ./gradlew :api-svc:dependencies, it shows:
------------------------------------------------------------
Project :api-svc
------------------------------------------------------------
...
compile - Dependencies for source set 'main' (deprecated, use 'implementation ' instead).
+--- project :idl
| +--- org.apache.commons:commons-lang3:3.4
| \--- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.8.8
| +--- com.fasterxml.jackson.core:jackson-core:2.8.8 -> 2.8.10
| \--- com.fasterxml.jackson.core:jackson-databind:2.8.8 -> 2.8.10
| +--- com.fasterxml.jackson.core:jackson-annotations:2.8.0
| \--- com.fasterxml.jackson.core:jackson-core:2.8.10
+--- project :api-svc:jooq
| \--- org.jooq:jooq:3.10.1 -> 3.9.5
...
Full output here, if it helps: https://bitbucket.org/snippets/shorn/4x8eaG
So, you can see Gradle is choosing to use the 3.9.5 version of JOOQ instead of what I specified.
How do I debug why Gradle is doing that?
I can workaround this by adding jooq:3.10.1 as a direct compile dependency of the api-svc project - but that's redundant and as far as I understand, I shouldn't have to do that.
EDIT: Lukas Eder has pointed out in the comments that the root cause of the problem is the Spring has a dependency on JOOQ 3.9.5 and Gradle is using that. But the question is - how do I figure that out for myself without trawling through every dependency in my project, just in case they happen to have the reference that's messing up my build?
I made a post over on the Gradle forms to try and get an answer to this: https://discuss.gradle.org/t/how-do-i-debug-why-a-dependency-was-overridden/24572
There was some other discussion in private messages, but my conclusion is that Gradle has no way to debug these kind of dependency overrides.
If you found this question while trying to figure out a problem with your own build - the only advice seems to be that you need to dig through all your dependencies looking for the source of the conflict.
Alternatively, try a StackOverflow question or posting over on the Gradle forums.

How do I force a gradle dependency for a specific sub-project?

This is what it is doing:
org.apache.santuario:xmlsec:1.5.7 -> 2.0.2
Just for this sub-project, I want to force 1.5.7. BTW this thing is buried pretty deep. There has to be a better way than peeling the onion to get at the one dependency for an exclude. I mean if I have a dependency X why can't I programatically cascade down the tree doing the exclude? This is Gradle, right?
This almost worked:
configurations {
compile.resolutionStrategy {
force 'org.apache.santuario:xmlsec:1.5.7'
}
...
}
The above shows up correctly in the tree but 2.0.2 still winds up in the War.
Regardless anyway that is effective and doesn't require digging it out manualy, I'll take.
Instead of using force you might consider using substitute.
dependencies {
compile 'org.apache.santuario:xmlsec:2.0.1'
}
configurations.all {
resolutionStrategy {
// add dependency substitution rules
dependencySubstitution {
substitute module('org.apache.santuario:xmlsec') with module('org.apache.santuario:xmlsec:1.5.7')
}
}
}
Then I get an output of:
$ ./gradlew -q dependencies --configuration compile
------------------------------------------------------------
Root project
------------------------------------------------------------
compile - Dependencies for source set 'main'.
\--- org.apache.santuario:xmlsec:2.0.1 -> 1.5.7
\--- commons-logging:commons-logging:1.1.1

Resources