How to get dependency version form parent project in Gradle - spring

I have a Gradle project and I want to create a submodule but I am getting an FAILURE when building the project.
The error message is
Execution failed for task ':child-project:compileJava'
> Could not resolve all files for configuration 'child-project:compileClasspath'.
> Could not find org.springframework.boot:spring-boot-starter:.
Required by:
project :parent-project
This is the parent project build.gradle file:
plugins {
id 'org.springframework.boot' version '2.3.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
allprojects {
apply plugin: 'java'
group = 'com.test'
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
repositories {
//local nexus
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
//other dependencies
}
This is the child project build.gradle:
plugins {
id 'java'
}
group = 'com.test'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_11
dependencies {
compile 'org.springframework.boot:spring-boot-starter'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
Please help, thanks in advance.

In order to be able to specify Spring Boot dependencies without versions, you need to apply the Spring Boot plugin to all modules. Right now you only have it in the parent project, but not the child.
Because applying the plugin will also, by default, disable the normal jar task can create a bootJar instead, you need to change this for libraries:
// Child build file
plugins {
// Note that there are no versions on the plugins in the child project as this is defined by the ones in the parent
id 'org.springframework.boot'
id 'io.spring.dependency-management'
}
bootJar {
enabled = false
}
jar {
enabled = true
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
}
Alternatively, you could also scrap the io.spring.dependency-management plugin (and the org.springframework.boot plugin in the child project), and instead import the Spring Boot BOM as a platform:
// Child build file (alternative)
dependencies {
// Use 'platform' for making the versions in the BOM a recommendation only, and 'enforcedPlatform' for making them a requirement.
// Note that you need the version of the BOM, so I recommend putting it in a property.
implementation enforcedPlatform("org.springframework.boot:spring-boot-dependencies:2.3.0.RELEASE")
// Here you can leave out the version
implementation 'org.springframework.boot:spring-boot-starter'
}
I usually go for the latter alternative as this allows me to use normal Gradle semantics. But it is mostly just preference.
(And just a small note to your build script: the compile configuration is deprecated. It is used in the line compile 'org.springframework.boot:spring-boot-starter'. You probably just copy/pasted it from somewhere, but you should replace it with implementation.)

Related

Could not find org.springframework.boot:spring-boot-dependencies:2.6.1

I follow the instructions here for the latest spring.framework.boot plugin.
My build.gradle has
plugins {
id 'java-library'
id 'eclipse'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'org.springframework.boot' version '2.6.2'
id 'org.springframework.boot.experimental.thin-launcher' version '1.0.23.RELEASE'
}
but gradle tasks have the error
> Could not resolve all dependencies for configuration ':detachedConfiguration1'.
> Could not find org.springframework.boot:spring-boot-dependencies:2.6.2.
UPDATE I neglected to add the final lines of the error description, which were in fact important to answer it.
Searched in the following locations:
- https://repo.spring.io/snapshot/org/springframework/boot/spring-boot-dependencies/2.6.2/spring-boot-dependencies-2.6.2.pom
- https://repo.spring.io/milestone/org/springframework/boot/spring-boot-dependencies/2.6.2/spring-boot-dependencies-2.6.2.pom
Here is a FULL file as an example: (this is the ROOT build.gradle) (If you code a monolith, you'll only have a root build.gradle, if you write multiple "gradle modules", you'll have a root and subproject build.gradle files) (if you have no idea what i'm talking about see link to docs.gradle.org at bottom)
(you can remove the dependencies, but i prefer to give a full working)
plugins {
id 'org.springframework.boot' version '2.6.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-batch'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-ldap'
implementation 'org.springframework.boot:spring-boot-starter-integration'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-web-services'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'com.h2database:h2'
implementation 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'org.springframework.batch:spring-batch-test'
testImplementation 'org.springframework.integration:spring-integration-test'
testImplementation 'org.springframework.security:spring-security-test'
}
test {
useJUnitPlatform()
}
allprojects {
/* custom task to show dependencies. run "gradle printAllDependencies" from commandline. see https://stackoverflow.com/questions/44266687/how-to-print-out-all-dependencies-in-a-gradle-multi-project-build/54436979#54436979 */
task printAllDependencies(type: DependencyReportTask) {}
}
Alternatively, you can try a "gradle FLUSH"
Gradle FLUSH Cache
(Optional, but preferred). Close all instances of IntelliJ or any other Java IDE.
./gradlew --stop
OR
gradle --stop
(now delete the folders)
rm -rf $HOME/.gradle/caches/
rm -rf $HOME/.gradle/build-cache-tmp/
(now resume normal gradlew commands like:)
./gradlew clean build
and alternatively, you can do an intelliJ "FLUSH"
(tested with IntelliJ version 2020.1 or later)
Close IntelliJ.
Delete the ".idea" folder off of the root folder.
Re OPEN the project.
Wait for Gradle imports and indices rebuild to complete
Try the IDE build again.
And the big hammer: "Invalidate IntelliJ caches". see https://www.jetbrains.com/help/rider/Cleaning_System_Cache.html and/or https://www.jetbrains.com/help/idea/invalidate-caches.html
If you're using Eclipse or NetBeans or Other, you'll have to find the "equivalent". The idea is that the IDE gets "confused".
BONUS
Multi Gradle Module :
https://docs.gradle.org/current/samples/sample_building_java_applications_multi_project.html
Thanks to #granadaCoder above, and after reinstalling Gradle to the latest version 7.3.3 the actual problem was that a 2nd repositories setting had crept into my build.gradle
repositories {
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
Which was exactly where the error said the dependencies were not found. I changed this to
repositories {
mavenCentral()
}
Things then started to work.

gradle does not fetch dependencies after upgrade from 5.1.1 to 6.4.1

I have several services that uses gradle 5.1.1 with java 8.
As we want to upgrade to Java 13, we first need to upgrade to gradle 6after doing so, some dependencies are not fetched.
Those dependencies are listed with compile() under a dependency which is our jar library and still built with gradle 5.1.1
our libraries are stored in a S3 bucket and we use shadowjar to generate the end jar.
so, for example:
I have project A which I want to upgrdae.
Project A has project B as a dependency (compile)
Project B has google guava as a dependency (also with compile)
Now, project A, that under gradle 5.1.1 had fetched guava with no problems, alerting me that it is missing guava after upgrading to gradle 6.
I use local computer installed gradle (not wrapper).
Here are the important build.gradle parts:
buildscript {
repositories {
jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
}
ext.ver = [
'springboot': '2.2.0.RELEASE',
'slf4j' : '1.7.12'
]
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${ver.springboot}"
classpath 'io.spring.gradle:dependency-management-plugin:1.0.7.BUILD-SNAPSHOT'
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
classpath 'com.amazonaws:aws-java-sdk-core:1.11.5'
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'maven-publish'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
compileJava {
sourceCompatibility = JavaVersion.VERSION_1_8
}
configurations {
compile.exclude module: 'spring-boot-starter-logging'
testCompile.exclude module: 'spring-boot-starter-logging'
runtime.exclude module: 'spring-boot-starter-logging'
compile.exclude group: 'ch.qos.logback'
}
configurations.all {
resolutionStrategy.cacheDynamicVersionsFor 10, 'seconds'
resolutionStrategy.cacheChangingModulesFor 10, 'seconds'
}
dependencyManagement {
applyMavenExclusions = false
}
repositories {
mavenLocal()
maven {
url "s3://bucket"
credentials(AwsCredentials) {
accessKey = awsCredentials.AWSAccessKeyId
secretKey = awsCredentials.AWSSecretKey
}
metadataSources {
artifact()
}
}
mavenCentral()
}
dependencies {
compile("com.test:projectB:1.0.0")
...
}
import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer
shadowJar {
classifier = ''
baseName = 'project-A'
manifest {
attributes 'Main-Class': 'com.test.projectA.Starter'
}
mergeServiceFiles()
append 'META-INF/spring.handlers'
append 'META-INF/spring.schemas'
append 'META-INF/spring.tooling'
transform(PropertiesFileTransformer) {
paths = ['META-INF/spring.factories']
mergeStrategy = "append"
}
}
Could this be because project B was not built with new gradle?
unfortunately, I cannot create a real reproducer as those libraries are real code of the company I work at.
Thanks and Regards,
Ido
The metadataSources declaration of the s3 bucket Maven repository is most likely the root cause why transitive dependencies of projectB are not resolved. The documentation is quite a bit vague here, but I suspect artifact() looks for the actual jar file only and not for the POM file, hence transitive dependency resolution is not performed. You should be able to see this behavior when running the build with switches --info and --refresh-dependencies.
Thankfully, this is quite easy to fix. Add mavenPom() and Gradle will try to resolve the POM first and with that, dependency resolution should be back to normal.
And while you're at it, you might want to read the upgrading from Gradle 5 guide and get rid of the compile configuration in favor of implementation. You should be able to see a warning similar to this when running the build with --warning-mode all:
The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the implementation or api configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/6.4.1/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations

Can't set variables before plugins{}

I have this build.gradle
plugins {
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'java'
id 'eclipse-wtp'
}
[...]
dependencies {
compileOnly group: 'org.springframework.boot', name: 'spring-boot-dependencies', version: '2.1.6.RELEASE', ext: 'pom'
[...]
I would specify a variable springBootVersion = "2.1.6.RELEASE". Unluckily, this is not possible, since I get this error:
only buildscript {} and other plugins {} script blocks are allowed
before plugins {} blocks
I also tried to remove the version from spring-boot-dependencies but I get this error:
Could not resolve: org.springframework.boot:spring-boot-dependencies
Is there not a way to declare a variable before the plugin{} block, or, alternatively, remove the version from spring-boot-dependencies?
I'm using Gradle 5.4.1
You cannot use variables in the plugins block. Check the "Strict Syntax" section here: https://docs.gradle.org/current/javadoc/org/gradle/plugin/use/PluginDependenciesSpec.html
also:
https://github.com/gradle/gradle/issues/3593
You shouldn't need to define the spring boot version anywhere else than in the plugins block by applying the spring boot plugin(org.springframework.boot) and then applying the (io.spring.dependency-management) after the block. The latter one then will take care of resolving the right versions based on the boot plugin version.
If don't know about it yet, I suggest to use the https://start.spring.io/ to generate your spring boot project, because it can generate you a ready-to-go gradle project.
Perhaps
plugins {
ext.springBootVersion = '2.1.6.RELEASE'
id 'org.springframework.boot' version springBootVersion
...
}
[...]
dependencies {
compileOnly "org.springframework.boot:spring-boot-dependencies:${plugins.springBootVersion}#pom"
Using buildscript you can do this:
buildscript {
ext {
springBootVersion = '2.6.6'
}
}
plugins {
id 'org.springframework.boot' version "${springBootVersion}"
}

Gradle 'Implementation' Dependencies Are Runtime-Scoped When Project Has No Classes Of Its Own

I've got a Gradle multi-project build for a custom Spring Boot Starter. Following Spring Boot Starter convention, my classes are all in an 'autoconfiguration' project, and I have a separate 'starter' project that ONLY brings in the dependencies needed to use the autoconfiguration.
The multi-project build produces 2 non-runnable jars, 1 for the autoconfiguration sub-project, and one for the starter sub-project. My new project that is using this starter pulls in the starter jar, but when I go to use classes that are from transitive dependencies, my project can't find them anywhere on the classpath.
When I dug into the starter jar, I found that all the dependencies it defines are RUNTIME scoped, which would explain the problem. I can 'fix' the problem by setting all the dependencies in my starter as 'compile' instead of 'implementation', but it's my understanding that 'compile' is on its way out, and that 'implementation' dependencies should be compile-scoped anyway. Can someone tell me what additional config may be necessary to define the starter dependencies as 'implementation' without them being scoped as 'runtime' in the resulting jar?
My starter/autoconfigure multi-project root gradle file:
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE' apply false
id 'io.spring.dependency-management' version '1.0.7.RELEASE' apply false
}
wrapper {
gradleVersion = '5.2.1'
}
repositories {
mavenLocal()
// private repo info omitted
mavenCentral()
jcenter()
}
subprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'maven-publish'
apply plugin: 'java-library'
group = 'com.mystarter'
repositories {
mavenLocal()
// private repo info omitted
mavenCentral()
jcenter()
}
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
}
bootJar {
enabled = false
}
jar {
enabled = true
}
javadoc {
failOnError = false
options.addStringOption('Xdoclint:none', '-quiet')
}
task sourcesJar(type: Jar) {
from sourceSets.main.allJava
classifier = 'sources'
}
task javadocJar(type: Jar) {
from javadoc
classifier = 'javadoc'
}
publishing {
publications {
myProjStarterArtifacts(MavenPublication) {
from components.java
artifact sourcesJar
artifact javadocJar
}
}
repositories {
// private repo info omitted
}
}
tasks.build.finalizedBy tasks.publishToMavenLocal
}
My starter sub-project's build file:
dependencies {
compile project(':myproj-spring-boot-autoconfig')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-cas'
implementation 'org.springframework.security:spring-security-ldap'
}
If I change the above 'implementation' lines to be 'compile' lines, that's when the resulting pom file stops making these 4 dependencies 'runtime' scope and instead correctly scopes them as 'compile'. As a side note, the 'compile project' line works just fine, it's just the lines that are 'implementation' that don't seem to work the way I'd expect when the project has no classes of its own.
My new project's dependency for my starter:
dependencies {
implementation('com.myproj:myproj-spring-boot-starter:1.0.0')
// other dependencies
}
implementation dependencies defined in a Gradle project are only made transitive for the runtimeClasspath of the consumers of said project, that is by design.
If you have a project without code but only defining dependencies, consider using the java-platform plugin for it, which allows you to specify constraints and optionally dependencies.
Otherwise, if you want the project to expose its dependencies to consumers at compilation time, you should use the api configuration for declaring them instead of compile which is indeed on its way out.
For more details, see documentation.

How are some gradle dependencies working with no version supplied

As far as I know gradle requires a version number when setting dependencies, but partial wildcards are allowed. For example if I want Guava, I cannot do this as it fails:
compile('com.google.guava:guava')
It has to be (as an example):
compile('com.google.guava:guava:21.0')
However, I'm learning Spring, which has the following:
compile("org.springframework.boot:spring-boot-starter")
compile("org.springframework:spring-web")
compile("com.fasterxml.jackson.core:jackson-databind")
How are these dependencies working with no version supplied?
Is it because of the following, but I thought these lines were required only for my plugin 'org.springframework.boot':
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.3.RELEASE")
}
}
It is worth mentioning that the trick is called BOM (bill of materials) and the actual versions can be checked in the related POM file (in this example, it is for the version 2.7.0) inside spring-boot-dependencies package. This is mentioned in the Spring Boot official documentation here: Build Systems.
Another way that Spring provides this (for non Boot projects) is through Spring Platform BOM where it actually provides version for the following dependencies.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
}
}
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom 'io.spring.platform:platform-bom:Athens-SR2'
}
}
TL;DR - spring boot uses custom dependencies resolver.
A spring boot plugin that is applied with the following piece of code:
apply plugin: 'spring-boot'
handles the dependencies that are listed without version. This logic is implemented in this class which delegates it to here. DependencyManagementPluginFeatures are applied here.
The spring boot gradle plugin documentation states the following:
The version of the spring-boot gradle plugin that you declare
determines the version of the spring-boot-starter-parent bom that is
imported (this ensures that builds are always repeatable). You should
always set the version of the spring-boot gradle plugin to the actual
Spring Boot version that you wish to use.
Spring Boot Dependency Management Plugin is not necessary.
You may use build-in Gradle BOM support instead of Spring Boot Dependency Management Plugin
For example:
plugins {
id 'java'
id 'org.springframework.boot' version '2.1.0.RELEASE'
}
repositories {
jcenter()
}
dependencies {
implementation platform('org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'
}
and for multi-module project:
in root build.gradle :
plugins {
id 'java-library'
id 'org.springframework.boot' version '2.1.0.RELEASE'
}
allprojects {
apply plugin: 'java-library'
repositories {
jcenter()
}
}
dependencies {
implementation project(':core')
implementation 'org.springframework.boot:spring-boot-starter-web'
}
and in core/build.gradle
dependencies {
api platform('org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE')
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Resources