I have implemented a maven-java project ( say name : projA) and deployed it as jar.
Now, I have multiple Gradle-java projects ( say projB and projC) and both are using projA as a dependency. And both projB and projC need to use same version of projA as dependency.
Problem is :
projA is changing it's version frequently (being under development). So, now it's developer's overhead to update the version everywhere ( like projB and projC and more) it's been used in as a dependency.
Question:
Was wondering if there is a way to declare a version as latest at the projA ( in pom.xml) and can be pulled in (in gradle.build in projB and projC) as :
implement '<group-id>:projB:latest'
So that developers do not have to track "how many places", "what version" etc to be synchronised.
You can use a snapshot version in the dependencies, that's the definition of an "under development" module that keeps changing.The "-SNAPSHOT" suffix has to be added in the end of the original version and this is effectively working the same way as a hypothetical "latest" version.
dependencies {
compile group: "groupId", name: "artifactId", version: "1.0-SNAPSHOT"
}
Related
if you have a build.gradle file with the line implementation 'org.springframework.boot:spring-boot-starter-web', how do you choose the version of the jar it downloads so that you get the latest one? I've seen a project where it is a 2.2.4 release, but in another project I've seen the same line with a 2.2.5 release.
Since you dropped the name Spring Boot, I assume the project has been generated Spring Initializr. A project generated with the Initializr has two plugins applied:
org.springframework.boot (Reference Documentation)
io.spring.dependency-management (Reference Documentation)
io.spring.dependency-management is Spring's opinionated way to provide Maven-like dependency management to Gradle builds. It allows to declare dependency versions once and then omit the version when declaring the actual dependency.
The org.springframework.boot plugin does the following:
When you apply the io.spring.dependency-management plugin, Spring Boot’s plugin will automatically import the spring-boot-dependencies bom from the version of Spring Boot that you are using. This provides a similar dependency management experience to the one that’s enjoyed by Maven users. For example, it allows you to omit version numbers when declaring dependencies that are managed in the bom. To make use of this functionality, simply declare dependencies in the usual way but omit the version number.
(From: Managing Dependencies)
What does that mean in practice?
When you generate a project for Spring Boot 2.1.14, your build.gradle will look similar to this:
plugins {
id 'org.springframework.boot' version '2.1.14.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
The org.springframework.boot plugin instructs the io.spring.dependency-management to apply the bill of materials (BOM) of Spring Boot 2.1.14. The BOM declares the following version for spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.14.RELEASE</version>
</dependency>
(From: Maven Central)
And this combination allows to declare the dependency to spring-boot-starter-web in the build.gradle without providing an actual version:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
If you would change the version of the org.springframework.boot Gradle plugin, then a different version that matches the Spring Boot versions would be applied.
You may ask, why this tremendous effort?
We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss.
That's why.
One possible solution is to use lockfiles and a version of +, or a combination of major.minor.+ or major.+
implementation 'org.springframework.boot:spring-boot-starter-web:+'
For more information on dependency locking: https://docs.gradle.org/current/userguide/dependency_locking.html
Another approach, and one I'm quite pleased with where available, is using a bill of materials, which specifies versions for a lot of dependencies, by introducing constraints. So where a dependency is used, with no version specified, as in your example, it will get the version the BOM brings in. So for the dependency below, if it is present in the BOM, it will match
implementation 'org.springframework.boot:spring-boot-starter-web
You're also free to override versions manually, by still specifying the version, should you choose to. And a BOM is like any other dependency, so you can use a mixture of lockfiles and BOMs.
Here's gradle documentation on bill of materials: https://docs.gradle.org/current/userguide/platforms.html
I have a very simple springboot application for testing purposes.
Here my build.gradle:
plugins {
id 'org.springframework.boot' version '2.1.0.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
id 'java'
}
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mandas:docker-client:2.0.2'
}
org.mandas:docker-client:2.0.2 has a transitive dependency on org.glassfish.jersey.core:jersey-client:2.28. However gradle then pulls version 2.27 instead of 2.28.
If I run ./gradlew dependencyInsight --dependency org.glassfish.jersey.core:jersey-client I get the following output:
org.glassfish.jersey.core:jersey-client:2.27 (selected by rule)
...
org.glassfish.jersey.core:jersey-client:2.27
\--- org.glassfish.jersey.connectors:jersey-apache-connector:2.27
\--- org.mandas:docker-client:2.0.2 (requested org.glassfish.jersey.connectors:jersey-apache-connector:2.28)
\--- compileClasspath
org.glassfish.jersey.core:jersey-client:2.28 -> 2.27
\--- org.mandas:docker-client:2.0.2
\--- compileClasspath
It seems that spring-boot-starter-web somehow has a dependency on org.glassfish.jersey.core:jersey-client:2.27. However, if I print all my dependencies with ./gradlew dependencies I do not see a dependency on org.glassfish.jersey.core:jersey-client:2.27 from spring-boot-starter-web.
However, searching a bit around the web, i found another way in tracking down a dependency:
grep -r "2.27" ~/.gradle/caches/modules-2/files-2.1/*
Like this i was able to track down where version 2.27 was introduced. It seems to be declared in the following poms:
spring-boot-dependencies-2.1.0.RELEASE.pom
spring-boot-autoconfigure-2.1.0.RELEASE.pom
My question now is manifold:
First of all, why does spring-boot-starter-web depend on jersey? I was always under the impression that if we want to explicitly use jersey over the spring implementation we would include spring-boot-starter-jersey.
Why can't I see that spring-boot-starter-web depends on org.glassfish.jersey.core:jersey-client:2.27 when running ./gradlew dependencies. Obviously there must be a dependeny on it somewhere as it downgrades the version.
Why is version 2.28 downgraded to version 2.27? How can I know which policy is applied by spring boot in order to make a choice for a specific version.
The application is running perfectly fine, but now as i got a version conflict, how should i best handle this? Is it a viable option to just use v2.28 instead of v2.27. I think this also refers to my first answer on why spring-boot is actually using jersey.
I know these are multiple questions, however I think it is better to ask them in one question instead of spreading them over multiple ones, as they are all related to the same context.
BTW: This is not only happening with org.glassfish.jersey.core:jersey-client. Exactly the same thing applies to org.apache.httpcomponents:httpclient.
Thanks for your help!
First of all, why does spring-boot-starter-web depend on jersey? I was always under the impression that if we want to explicitly use jersey over the spring implementation we would include spring-boot-starter-jersey.
It doesn't. Rather, it depends on Tomcat. You are correct that you would need the jersey starter for auto-configuring that.
Why can't I see that spring-boot-starter-web depends on org.glassfish.jersey.core:jersey-client:2.27 when running ./gradlew dependencies. Obviously there must be a dependeny on it somewhere as it downgrades the version.
Because it doesn't. More on that below.
Why is version 2.28 downgraded to version 2.27? How can I know which policy is applied by spring boot in order to make a choice for a specific version.
This is the underlying problem. I will explain it below.
The application is running perfectly fine, but now as i got a version conflict, how should i best handle this? Is it a viable option to just use v2.28 instead of v2.27. I think this also refers to my first answer on why spring-boot is actually using jersey.
It depends. In my experience, your dependencies could break both by upgrading and downgrading a transitive dependency compared to that they have been built and tested against, even if it is just a minor version (I am looking at you, SnakeYAML!) So you really just have to give it a shot. Usually it is safer to upgrade than to downgrade, but sometimes it will still cause problems.
Here's the deal with the Jersey downgrade.
The Spring Dependency Management plugin is used to control the versions of your dependencies, both the direct and the transitive ones.
When you apply both the dependency management plugin and the Spring Boot plugin, the latter will apply its default versions, which comes from the Spring Boot BOM. You can check which dependencies are managed and in what versions by running gradle dependencyManagement.
The idea with all this is that you get a set of dependencies that are known to work well with each other. If you like a different version of one of the managed dependencies, you will have to configure it using the dependencyManagement extension (as documented here).
This is why your Jersey dependency gets downgraded.
I personally don't use the Spring dependency management plugin as I like the way you work with dependencies in plain Gradle. So I usually just do something like this:
plugins {
id 'org.springframework.boot' version '2.1.0.RELEASE'
id 'java'
}
dependencies {
implementation platform("org.springframework.boot:spring-boot-dependencies:2.1.0.RELEASE")
}
In this case, it will by default use the dependencies from the Spring Boot BOM, but not downgrade them if anyone needs a newer version. But it will upgrade them if needed, and you also don't have to specify a version yourself if you don't want to, in which case it will use the one from the BOM.
On Gradle (more specifically with Android) how can I have conditional dependency so that they get loaded either from the parent project like this:
compile project(':lib')
or from a repository is not present in the parent project:
compile 'com.sample:lib:+'
one possible solution I thought of is to do this:
compile allprojects.find({ it.name.equal('lib')}) != null ? project(':lib') : 'com.sample:lib:+'
but the allProjects property is not available in the dependencies task.
Update:
The reason I'm searching for this because we have two setups, one with our sub-projects imported from different git repositories at the development machines and the second one importing the dependencies from our dependencies server for CI.
An alternative I've tried is to set this in the parent project:
ext.projectNames = allprojects.collect{ it.name }
and in the subproject I'm doing this:
compile projectNames.contains('lib') ? project(':lib') : 'com.sample:lib:+'
This works, but however since in the current setup we run just one of the subproject in the CI server this property won't exist and will fail so may have to extract this behaviour to a plugin and maybe play with getParent()
I had a need for the same feature. Getting it to work with gradle was a cinch. The hard part was figuring out how Android Studio syncs the gradle files. Without a successful sync, the IDE will complain it can't find any dependencies. At any rate, I figured it out, here is my solution to make it work with gradle and Android Studio.
https://gist.github.com/vangorra/c1383c355ce8fe56adf8
It essentially boils down to defining the project in settings.gradle:
include 'library'
project(':library').projectDir = file('../Library/library')
Then you have to use a one-liner with options closure for your dependency:
compile ( project(':library').projectDir.exists() ? project(':library'): 'Library:library:unspecified#aar') {
transitive = true
}
Dynamically resolving to either project dependency or external dependency isn't yet a first-class Gradle feature, and it takes some effort (and may incur some limitations) to implement it in a build. You can find a proof-of-concept here: https://github.com/pniederw/elastic-deps
PS: It's allprojects, not allProjects.
(Edited for clarification)
My (non-OSGi) application build is in Gradle, and I am trying to upgrade from very old version of Jersey (1.1.4.1) to something much newer (1.12?). I do not pretend to know anything about using OSGi. But when I point my Gradle dependencies (with $JERSEY_VERSION set to "1.12") to:
[group: 'com.sun.jersey', name: 'jersey-server', version: "$JERSEY_VERSION"]
it downloads the jersey-server-1.12.jar into my Gradle dependencies cache under a "bundles" directory instead of the normal "jars" directory, and then Gradle seems to not include this jar in its classpath like it would if it were under a "jars" subdirectory instead.
I discovered it went under "bundles" because the POM has it labeled as an OSGi enabled jar. I do not think we are going to want to OSGi-ify our project. Am I stuck with older versions of Jersey, or is there anything else I can do to get Gradle to see the Jersey jar? I would prefer to not manually copy the file to a local repo if possible, but rather somehow depend on the dependency management capabilities of Gradle if it is up to the task.
OSGi bundles are normal jars with extra manifest entries. You should be able to use them in a non OSGi project as you would any other dependency. Is it a problem that they end up in the cache's bundles directory?
'Twas a silly oversight: moving from 1.1.4.1 to 1.12, the POM dependencies changed, so that jersey-core.jar was no longer being brought in implicitly. I had to add jersey-core.jar explicitly. I had assumed the problem was the fact that jersey-server.jar was being imported as a bundle, but I was really just getting a ClassNotFoundException for a class that was in jersey-core.jar.
In a project that consists of Java-only sources with Scala tests and which is built with SBT 0.11.2, I have followed the instructions to have the artifacts uploaded to Maven Central. This worked fine, except for the fact that scala-library is listed as a compile-time dependency in the pom, which I don't want, because it would cause the users of my library to pull scala as a transitive dependency. Is there a way to prevent this dependency from being added?
The exact build.sbt I use can be seen on github.
In your build.sbt set autoScalaLibrary := false this removes the dependency to the scala-library.