Transitive dependencies in maven - maven

I have read about transitive dependency in maven but it makes me little confused. can anyone please explain about transitive dependency in maven and what are the advantages and disadvantages.Thanks in advance.

Start very simple. Assume you are using a library like the Apache Tika lib. So you express this by defining a dependency in your pom file by something like this:
<project...>
<dependencies>
<dependency>
<groupId>..</groupId>
<artifactId>tika-core</artifactId>
<version>1.19</version>
</dependency>
</dependencies>
</project>
The Tika lib also has dependencies on its own which mean the dependencies of Tika are expressed in their pom file and those dependencies are called Transitive Dependencies.
The simple advantage is that you don't need to think about the transitive dependencies. This means you only need to think: I would like to use Tika lib and don't need to bother about their dependencies...
So If you use a class of Tika core in your code you have the dependency available. One tip about best practice is: If you are using a class of a transitive dependency, then make it a direct dependency (add it to your pom file).

Maven documentation assumes that you already know what a transitive dependency is. This may not be the case! So let's dive in...
First things first
A transitive dependency is
A depends on B
B does not depend on A
B depends on C
==> Therefore A depends on C
It's as simple as that.
The good news is
Maven is great at managing dependencies! From Maven documentation:
Maven avoids the need to discover and specify the libraries that your own dependencies require by including transitive dependencies automatically.
So most of the time you won't have to bother about it.
And beyond
However, Maven understands you may need more advanced features, therefore, it proposes several dependency related mechanisms, such as dependency management, mediation, scope, exclusion and optional.
Dependency management, for example, is a very popular feature, because it enforces the versions of dependencies used by a project or a set of project.
Exclusion alows you to exclude transitive dependencies from a project. This feature is useful to manage dependencies incompatibility.
And so on...

Related

What are the disadvantages of using undeclared maven dependencies (through transitive dependencies)?

Maven includes transitive dependencies in a project class path to eliminate the need to redefine these dependencies over and over again each time a module depends on a third party module.
The version of transitive dependencies used can be controlled from a dependency management section in the root pom. So it is controllable.
So why using transitive dependencies in a project without explicitly declaring it in its pom file is considered a bad practice ?
Usually, you update your dependencies after some time.
The update can lead to lost transitive dependencies which will then directly affect your program.
If, on the other hand, you write down all dependencies you use explicitly, it is much easier to make updates and see what you really use.

How two know when a maven project has the same transitive dependenciy twice of different versions?

I have to add a new dependency to a maven project. This dependency has four transitive dependencies(according to http://mvnrepository.com/) and between them, there is spring-data-jpa jar.
The maven project I am working in has many dependencies configured in the pom so I understand there could be a big possibility that there is already a spring-data-jpa dependency in the project(transitive or not).
When you work in a large project with many dependencies and you have to add a new one, how to check if there is already the same transitive dependency of a different version? I have to check manually the transitive dependencies for each direct dependency configured? Has maven a warning for this situation?
How maven works in this situation? I mean, there could be two spring-data-jpa jars of different versions(this would be a problem) or maven resolves this in another way?
The simple answer is that the dependency plugin can tell you. The longer answer is that there are a number of different situations to consider about transitive dependency management, and how the plugin helps and what to do about it differs for each one.
Maven automatically chooses which dependency to include if two dependencies have the same coordinates (groupId, artifactId) with different versions. Broadly speaking, it picks the version that's highest in the tree - effectively overriding dependencies defined in downstream transitive dependency poms. So, if you have two different versions of exactly the same dependency then you will still only find one version of the dependency on the relevant classpath.
The dependency plugin can help you identify this situation by highlighting points where its made a decision, but you probably want to use the dependencyManagement section of your top-level pom to ensure that the dependencies which you bring in are the ones you expect.
Separate difficulties can arise when a dependency changes its groupId or artifactId. Then you can get two dependencies on the classpath - one with an old version on the old coordinates and one with the new version on the new coordinates. As examples, Spring, Hibernate and Apache commons have all found themselves doing this at some point or another. In this case all you can do is use to the dependency plugin to identify duplicated dependencies and then use exclusions tags to explicitly exclude them as transitive dependencies from the dependencies which are pull them in.
It's important to note that all of this dependency management can cause unintended breakage. If the thing that your application depends on really does depend on some specific version of a package as a transitive dependency then you can break it by overriding that version. So testing the features that you use is essential.
Have you tried the Maven Dependency Plugin? There's some useful goals you can run, such as mvn dependency:tree etc.

Limiting a transitive dependency to runtime scope in Maven

I've got 2 projects using Maven. The first one is a library containing utility classes and methods. The second project is an actual application that has the library as a dependency. My library uses internally a third-party library.
So these are the dependencies:
My library: depends on the third-party library
My application: depends on my library
However, I don't want the third-party library classes to be available at compile time in my application. This is because the application is supported by a large team and I want to prevent people from accidentally using methods from the third-party library in the application given that some class names and some method names are similar between the two. Of course the third-par ty library will have to be available in my application at runtime.
If the scope for all my dependencies was compile, it wouldn't achieve my goal. Is there a way to achieve this in Maven 3?
Very good question and unfortunately you can't do this using Maven 3, or 2, or any other version, because of its fundamental design. What you're asking about is actually a desired and ideal behaviour since in fact any artifact's compile dependencies should be transitive with runtime scope. However, design like this leads to some problems. As you can read at Maven's Introduction to the Dependency Mechanism about compile scope:
It is intended that [transitive dependencies of a compile dependency which are themselves compile dependencies should be considered] runtime scope instead, so that all
compile dependencies must be explicitly listed - however, there is the
case where the library you depend on extends a class from another
library, forcing you to have available at compile time. For this
reason, compile time dependencies remain as compile scope even when
they are transitive.
So, as you see, what you require is actually the proper design of this behaviour which is unfortunately impossible to implement.
Nothing has changed during the last three years, so Michal's answer is still correct: There is no way to limit transitive visibility in Maven.
However, you should consider redesigning your library to split it in an api-artifact that is necessary as compile time dependency and which itself does not depend on the third party library and an implementation artifact which is only needed as runtime-dependency and which depends on the third party library.
In your application, you can declare an explicit dependency on the third-party library using "runtime" scope.
This prevents the third-party library from being seen at compile time and thus no direct usages can sneak in. However, it will still be present at run time (since it is needed by your library).
This works, but is awkward and deserves an explanatory XML comment in the pom.
The other answers are correct. Besides working around around a missing crucial feature in maven by splitting out an artificial API-only module, you also have these alternatives:
exclude the transitive dependencies, then depend on them directly (you have to manage the version numbers yourself)
Use checkstyle import control, and CI. That way team members may use the transitives, but then maven verify will fail
Use gradle. This is a solution to many limitations of Maven
What seems to work is use <dependencyManagement> section in the pom.
You will want to check for any side effects, since it works project wide. And you have to specify each library specifically.
Following code sample allowed me to force guava (which was smurfed in the project by google guice as a compile time transitive dependency) to a runtime dependency everywhere.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
You can analyze dependencies with: mvn dependency:analyze or have the dependencies analyzed as part of the verify lifecycle phase:
https://maven.apache.org/plugins/maven-dependency-plugin/examples/failing-the-build-on-dependency-analysis-warnings.html
You can try like that:
#My application pom.xml
<dependencies>
<dependency>
<groupId>My groupId</groupId>
<artifactId>My library</artifactId>
<version>${version}</version>
<exclusions>
<exclusion>
<groupId>third-party library</groupId>
<artifactId> third-party library</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>third-party library</groupId>
<artifactId> third-party library</artifactId>
<version>${version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
If it's possible for you to migrate from Maven to Gradle you can have this issue solved, as pointed out by #tkruse. I'll explain why:
With Gradle, library authors have 2 different "compile" level scopes available: api and implementation.
The api scope behaves like Maven's compile scope and is meant for dependencies of your library that will be part of its API and are thus required to be also "compile" dependencies of the consumers of your lib.
The implementation scope however aims to solve the very problem you presented. Its meant for dependencies of your library that are not part of its API and thus only required to be "runtime" dependencies of the consumers of your lib.
This is done in a very clever way: implementation dependencies are used during compilation phase normally, but when Gradle generates the pom.xml metadata of the library (or any other type of metadata) to be published, it sets this dependency as a "runtime" dependency.
Source: https://gradle.org/maven-vs-gradle

Force transitive dependency version in Gradle

I'm playing around with gradle trying to port my maven project and here's my problem:
In maven we have the <dependencyManagement> which provides a common (default) version for certain dependencies (which is used only when in a sub-pom this artifact is used without a version number). It also, from what I understand, forces a certain version for all transitive dependencies. So if I understand correctly even if artifact B which we have as a dependency has a dependency on artifact C version 1.0 then we will still use a version of artifact C defined in the <dependencyManagement> (so it might be 2.0). Is that correct?
If so then is there a way to do something similar in Gradle? I know that a common way of replacing the <dependencyManagement> is to simply create a Groovy map in one of the build scripts. But how can I force the transitive dependency version? If I use Gradle's "force" won't it affect all (not only transitive) dependencies (which is obviously not what I want)?
In Gradle, forcing a version (e.g. with Configuration.resolutionStrategy.force) will force it for all dependencies of the configuration, direct and transitive. There is no first-class feature that forces a version only for transitive dependencies. Do you have a valid use case for this? At the end of the day, both Gradle and Maven will select a single version for a dependency anyway, no matter where and how often it appears in the dependency tree.
There is a ResolutionStrategy feature that allows forcing artifacts versions including transitive dependencies: https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html
It is also possible to configure dependencies constraints: https://docs.gradle.org/current/userguide/dependency_constraints.html

Is there a way to disallow dependencies on certain artifacts in Maven?

I have a problem with the maven artifact for junit. It comes packaged with hamcrest, but I like to use a later version of hamcrest, so my projects need to use junit-dep and exclude the hamcrest dependencies.
I have a parent pom.xml with a dependencyManagement section to handle the version of junit-dep that I want and ensure that hamcrest is excluded. However, I'm constantly running into an issue where some other test library includes a transitive dependency to junit.
Note that it's not the version number that's a problem. It is the particular artifact. I don't want junit, I want junit-dep. Is there any way to say in my dependencyManagement (or anywhere else) that I never want to include junit of any version at all?
Unfortunately, there is no global exclude option in dependency management.
On the other hand, if you don't want JUnit jar do be included ever, you may play the same trick as commons-logging-99.0-does-not-exist.jar.
Here is the link that describes the trick -> http://day-to-day-stuff.blogspot.com/2007/10/announcement-version-99-does-not-exist.html
Personally, I've found out that it's easier to do the job by using <exclusions> tag.

Resources