Trying to understand how the ordering of repositor...
# community-support
a
Trying to understand how the ordering of repositories affects a project's build. In my script there are SNAPSHOT and RELEASE repositories declared and a couple of other repos as well. The original ordering is RELEASE followed by SNAPSHOT. With this original ordering if I build my project then the build PASSES, but with below message:
Copy code
Errors occurred while build effective model from D:\POC\Gradle-User-Home\caches\modules-2\files-2.1\log4j\log4j\1.2.15\a09f05bb79a0acabbe979f67ed4fbbbc07a368c1\log4j-1.2.15.pom:
        'build.plugins.plugin[io.spring.gradle.dependencymanagement.org.apache.maven.plugins:maven-antrun-plugin].dependencies.dependency.scope' for junit:junit:jar must be one of [compile, runtime, system] but is 'test'. in log4j:log4j:1.2.15
If I reverse the order of the two repos, i.e. SNAPSHOT followed by RELEASE then my build FAILS with below message:
Copy code
Errors occurred while build effective model from D:\POC\Gradle-User-Home\caches\modules-2\files-2.1\commons-codec\commons-codec\1.15\890916a890daedad2b33b2fe1c670d1ce7a5a8b9\commons-codec-1.15.pom:
Errors occurred while build effective model from D:\POC\Gradle-User-Home\caches\modules-2\files-2.1\log4j\log4j\1.2.15\a09f05bb79a0acabbe979f67ed4fbbbc07a368c1\log4j-1.2.15.pom:
        'build.plugins.plugin[io.spring.gradle.dependencymanagement.org.apache.maven.plugins:maven-antrun-plugin].dependencies.dependency.scope' for junit:junit:jar must be one of [compile, runtime, system] but is 'test'. in log4j:log4j:1.2.15
I have tried to printout the dependency tree, but I do not see any mention of the above dependencies. Searched over the internet too, didn't find much help. How should I go about debugging this issue? Thanks.
v
Just stop using the Spring Dependency Management plugin.
It is an obsolete relict from times when Gradle did not have built-in BOM support. By now it does more harm than good and even the maintainer of it recommends not to use it anymore, but instead the built-in BOM support using
platform(...)
.
šŸ‘ 1
a
I tried to implement the platform approach but couldn't get it working. Getting below error :
Copy code
Execution failed for task ':ProjectABCSpringBootWar:bootWarMainClassName'.
> Could not resolve all files for configuration ':ProjectABCSpringBootWar:providedRuntime'.
   > Could not find org.springframework.boot:spring-boot-starter-tomcat:.
     Required by:
         project :ProjectABCSpringBootWar
Below is what the build.gradle looks like (some details omitted). I tried to add the platform :
Copy code
plugins {
    id 'org.springframework.boot' version "${springBootVersion}"
    //id 'io.spring.dependency-management' version "${ioSpringDependencyManagement}"
    id 'war'
}

apply from: "${rootDir}/gradle/bootWar.gradle"  //------------------- refer next block
apply from: "${rootDir}/ProjectABC/dependencies.gradle"

dependencies {

    implementation platform("org.springframework.boot:spring-boot-dependencies:2.1.4.RELEASE")

    compile deps.external.spring
    compile deps.external.springAOPAlliance
    compile "org.aspectj:aspectjweaver:${aspectjweaverVersion}"
    compile "org.slf4j:slf4j-api:${slf4j_apiVersion}"
    compile "org.yaml:snakeyaml:${snakeYamlVersion}"

    testSpringRuntime "org.aspectj:aspectjweaver:${aspectjweaverVersion}"
    testSpringRuntime "org.slf4j:slf4j-api:${slf4j_apiVersion}"
}
below is what bootWar.gradle looks like (some details omitted)
Copy code
springBootWar.gradle


configurations {
    libs
    libs.transitive = false
    warArchive
	all {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

dependencies {

    implementation "org.springframework.boot:spring-boot-starter-web"
    implementation "org.springframework.boot:spring-boot-legacy:${springBootLegacyVersion}"
	
    implementation "org.aopalliance:com.springsource.org.aopalliance:${springAOPVersion}"

    providedRuntime "org.glassfish:jakarta.el:${jakarta_elVersion}"
    providedRuntime "org.springframework.boot:spring-boot-starter-tomcat"
    providedRuntime "org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatEmbedWebsocketVersion}"
    providedRuntime "org.apache.tomcat.embed:tomcat-embed-core:${tomcatEmbedCoreVersion}"
    providedRuntime "org.apache.tomcat:tomcat-annotations-api:${tomcatAnnotationsApiVersion}"

    testImplementation "org.springframework.boot:spring-boot-starter-test"
    testImplementation "junit:junit:${junitVersion}"
    testImplementation "org.mockito:mockito-core:${mockito_coreVersion}"

}

tasks.named('test') {
    useJUnitPlatform()
}
I am using Gradle 6.8.3 and Java 8 to build the project. What wrong am I doing here? Also what am I lacking in terms of concept here?
I was able to fix it by making below changes to the bootWar.gradle: added :
Copy code
implementation platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
changed the configuration of tomcat starter from providedRuntime to implementation :
Copy code
implementation "org.springframework.boot:spring-boot-starter-tomcat"
However, this feels like a workaround to me as in reality org.springframework.boot:spring-boot-starter-tomcat should be declared with providedRuntime What could have possibly gone wrong in my case?
v
Just also use the platform on
providedRuntime
, or any configuration it extends, or the configuration that actually is resolved. Using the platform on
implementation
only affects
implementation
and all that extend from it.
šŸŽ‰ 1
Btw, the Spring Boot plugin has a constant for the BOM coordinates including appropriate version. You can find it in the Spring Boot docs if you mind.
a
I'll definitely give it a try. Thanks again.
šŸ‘Œ 1
I reverted the earlier "implementation" to get back to providedRuntime and added *platform (assuming this is what you suggested) :*
Copy code
providedRuntime platform("org.springframework.boot:spring-boot-starter-tomcat")
My build now fails with below errors:
Copy code
No matching variant of org.springframework.boot:spring-boot-starter-tomcat:2.5.15 was found. The consumer was configured to find a runtime of a platform compatible with Java 8, packaged as a jar, and its dependencies declared externally but:
          - Variant 'apiElements' capability org.springframework.boot:spring-boot-starter-tomcat:2.5.15 declares a component compatible with Java 8, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares an API of a library and the consumer needed a runtime of a platform
          - Variant 'javadocElements' capability org.springframework.boot:spring-boot-starter-tomcat:2.5.15 declares a runtime of a component, and its dependencies declared externally:
              - Incompatible because this component declares documentation and the consumer needed a platform
              - Other compatible attributes:
                  - Doesn't say anything about its target Java version (required compatibility with Java 8)
                  - Doesn't say anything about its elements (required them packaged as a jar)
          - Variant 'mavenOptionalApiElements' capability org.springframework.boot:spring-boot-starter-tomcat-maven-optional:2.5.15 declares a component compatible with Java 8, packaged as a jar, and its dependencies declared externally:
              - Incompatible because this component declares an API of a library and the consumer needed a runtime of a platform
          - Varian
I think I'm failing at understanding the error message.
v
No,
org.springframework.boot:spring-boot-starter-tomcat
is not a platform / BOM. You should have the dependency like before and additionally use the Spring BOM via
platform(...)
a
I'm sorry but looks like I'm already using the Spring's BOM only. Below syntax, right?
Copy code
implementation platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
v
Again, you are using it on the
implementation
configuration which will affect the
implementation
configuration and all configurations that directly or indirectly extend from it like
runtimeClasspath
,
compileClasspath
, and so on. But
providedRuntime
does not fall into that category so is not affected. So use it there too is what I said.
āœ… 1
Otherwise it is like you are asking "I added dependency X to implementation, why is it not available on providedRuntime" šŸ˜‰
šŸ‘ 1
a
You have been a great teacher. Its completely my fault. I'm a bad student. šŸ™‚ Thank you so much for your guidance. Your suggestion for adding another platform declaration for providedRuntime worked and my build is successful now.
Copy code
implementation platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
providedRuntime platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
Left tomcat-starter as is :
Copy code
providedRuntime "org.springframework.boot:spring-boot-starter-tomcat"
I'm just curious as to why the ordering of repositories was such an issue for the Spring Dependency Management plugin. With the above working setup I have just started another build, this time with original repo order (RELEASE followed by SNAPSHOT) to see whether the repo ordering is an issue in this case. The initial results show positive signs. But will wait for the full build to complete just to be double sure. I'll share the end result. Thank you again. Stay blessed. šŸ™‚
v
Who knows what that dreaded plugin does under the hood. šŸ˜„ I've just seen from your message that you use it and that is always a bad idea, so was worth to try first removing it. šŸ™‚
šŸ˜‚ 1
a
The build PASSED. šŸŽ‰ Order of SNAPSHOT and RELEASE repos no more affect the build. Tested both the order 😃
šŸ‘Œ 1
You mentioned earlier : "...By now it does more harm than good and even the maintainer of it recommends not to use it anymore, but instead the built-in BOM support using
platform(...)
." Wanted to read more on it for the purpose of documentation. I came across a GitHub issue : https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/211 which lists some of the things like exclusion behavior that Gradle is yet to take care of for which the plugin serves still the purpose. What specific cases might not be good candidates for this plugin I see there is not much good consolidated information out there on the internet.
v
That "exclusion behavior" is one of the "more harm than good" points. Why in the name of the good lord should you ever want to port over this broken behavior from Maven to Gradle? That is like writing a plugin that forces you to also execute the
clean
task with every Gradle execution to get reproducible and correct result. Let me rephrase the documentation that one of the last comments in that issue links to for that exclusion thing. • You have library A • You have library B which uses A for some of its functionality • You have library C which uses B • You have project X where you depend on B and C So you have
Copy code
X
-> B
   -> A
-> C
   -> B
      -> A
Now
C
recognizes, that it only uses features of B that do not require
A
. It has the marvellous idea to exclude
A
for that reason. What would you expect as a result? That if
X
is using features of
B
that need
A
it fails because
A
is excluded from
X->B->A
just because
C
said it does not need
A
? The Gradle logic makes much more sense. If noone needs
A
, then it is excluded from the final result. But just because
C
says it does not need
A
does not mean that
X
does not need
A
.
šŸ™ 1