I have a library project where i'm considering adding some dependencies as optional, since they will be required only for projects compiling and running on JDK9+.
However, the documentation about optional dependencies does not clarify whether it is possible to optionally depend on specific version, i.e., if i can put in my library pom file something like
<dependency>
<groupId>com.foo</groupId>
<artifactId>dependency-a</artifactId>
<version>2.0</version>
<optional>true</optional>
</dependency>
And somehow make sure that projects that depend on my library and also want to include dependency-a will depend on version 2.0.
As far as I understand, setting dependency-a as optional mean that projects that depend on my library will not transitively depend on it, but they may explicitly add it in their own dependencies if they need some additional (optional) features in my library.
Is there a way to make sure that if they want to add dependency-a they depend on a specific version?
What would happen if a project depending on my library had
<dependency>
<groupId>com.foo</groupId>
<artifactId>dependency-a</artifactId>
<version>1.0</version>
</dependency>
And version 1.0 was not compatible with version 2.0?
And if it isn't possible to enforce the version on optional dependencies, what's the point in setting the version for an optional dependency?
I have also been looking at the maven enforcer plugin as it seems to be able to handle cases like this, but I was wondering if there is a better solution.
I think that it would be more useful to add an exclusion of the library that is optional. Example:
Project A has an optional dependency O
Project B includes project A in its pom file. In case B wants to use O, nothing is changed. Otherwise, an inclusions tag is added in B's pom file inside A dependency to exclude O:
<dependency>
<groupId>project.a.group</groupId>
<artifactId>project.a.artifact</artifactId>
<version>1.4</version>
<exclusions>
<exclusion>
<groupId>o.group</groupId>
<artifactId>o.artifact</artifactId>
</exclusion>
</exclusions>
</dependency>
I still dont understand the benefit of using the optional tag, in case we will be obliged to specify the version vs removing the optional dependency from the first project and simply adding it when needed to the second project.
My understanding of optional is the following:
If you declare a dependency as optional, it stays on your compile classpath (so you need a version), but is not transitively visible for users of your library. So users of your library would need to add the dependency themselves (with a sensible version) to their POM.
I also do not see how you want to use the enforcer plugins because users of your library will not "see" the plugins you have in your POM.
For ease of use, I would recommend to have two different jars for the different Java versions, either separated by classifier or by version (like 1.2.3-JDK8, 1.2.3-JDK9).
Related
Our project has migrated from log4j to log4j2. However, other projects, which our project depends on, are still using log4j.
If I want to exclude log4j with exclusions, I need to add more than 10 exclusions on a single pom.xml and it is not practical.
Question: is there any way to say, it does not matter, from where it comes, exclude log4j from my project. It is like the exact opposite of adding dependency.
This not possible at POM level, as stated by official documentation
Why exclusions are made on a per-dependency basis, rather than at the POM level
This is mainly done to be sure the dependency graph is predictable, and to keep inheritance effects from excluding a dependency that should not be excluded. If you get to the method of last resort and have to put in an exclusion, you should be absolutely certain which of your dependencies is bringing in that unwanted transitive dependency.
If you have control over the other projects you depend on, then the concerned dependency should be declared as optional.
<dependency>
<groupId>com.sample</groupId>
<artifactId>project</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
Optional doesn't affect the project itself (it will keep on having this dependency) but it will not be considered as transitive dependency by dependent projects (hence, you will have the choice to ignore it or to re-declare it, if needed).
As from official documentation
Optional dependencies - If project Y depends on project Z, the owner of project Y can mark project Z as an optional dependency, using the "optional" element. When project X depends on project Y, X will depend only on Y and not on Y's optional dependency Z. The owner of project X may then explicitly add a dependency on Z, at her option. (It may be helpful to think of optional dependencies as "excluded by default.").
Afterwards, if you really want to make sure that the concerned dependency is not brought in by any dependency transitively, you could configure your build to ban it (the build would fail whenever the concerned dependency appears) using the Maven Enforcer Plugin and its bannedDependencies rule.
This question already has answers here:
Maven : Should I keep or remove declared dependencies that are also transitives dependencies?
(2 answers)
Closed 7 years ago.
I'm a newbie in maven and I have a project that has directly dependencies with several libraries, but if I declare only one dependency in my pom.xml the project compiles and runs perfectly. It is becasuse this library have other dependencies which are automatically imported and contain my directly dependencies.
Is it recommended to add all dependencies in the pom.xml despite transitive dependencies?
What version of a dependency should I use? The highest possible version?
No, when there are transitive dependencies which are resolved properly you don't need to specify them explicitly in the pom.xml. Thus your pom is kept small and tidy.
You should use the highest stable version of dependencies in your new projects.
However there are cases when you need a different version (in most cases higher) of a transitive dependency to be used. In that case you specify the transitive dependency with the higher version in a <dependencyManagement> tag. For example if we have:
<dependencies>
<dependency> <!-- has transitive dependency of com.artifact2 v.1.0 -->
<groupId>com.group1</groupId>
<artifactId>com.artifact1</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
If we want to specify explicitly that we need the new version of com.artifact2 which is 2.0 then we add to the pom these lines:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.group1</groupId>
<artifactId>com.artifact2</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
</dependencyManagement>
It is almost always better to use you exactly those dependencies you need. Bundled dependencies often contain more then what you need. You can however, by adding exclusions in your pom.xml, make the dependencies contain only the libraries that you actually need. Often more then one 3th party library uses same common libraries (for example logging dependencies are found in a lot of dependencies). Those libraries will then cause a conflict which can be problematic at some web containers. If you are using eclipse, open your pom in Dependency Hierarchy and see how it goes from there...
Versions depend of several factors. First important thing is always to pick a RELEASE versions (unless for example when one is explicitly required to pick some newest beta, containing the newest features you have been waiting on for 3 months). Second is to figure out which newest versions of different libraries and frameworks you use can successfully work together. Newer versions are usually preferred cause they are developed last, meaning: more developer support, more probable to work with other state-of-the-art frameworks.
We have a reactor pom whose child modules are versioned independently, ie. child module declares it's own version that is independent of the version of the main pom. There is however a dependency between two child modules. How should be this dependency configured to always use the version that is declared by the actual module of the reactor pom?
I would expect that I can set up dependency management in the root pom and use some implicit properties to determine the version of child modules, but the best such option I can find :
${session.projectDependencyGraph.sortedProjects[0].version}
or simply
${reactorProjects[0].version}
so that the dependency management in the root pom would look like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mygroup</groupId>
<artifactId>mySubmodule</artifactid>
<version>${reactorProjects[0].version}</version>
</dependency>
</dependencies>
</dependencyManagement>
looks unreliable because the reactor order may change when new modules or dependencies are added.
Maybe this usage scenario is discouraged by design and then I would like to know why.
Edit: As is suggested in comments, declaring a global properties with child versions in parent POM may be an option for newer versions of Maven. I would however like to see some analysis with respect to wider consequences like workflow, releasing, directory and repository settings, some plugins usage (e.g. version plugin) etc. For example I think that direct consequence of this approach is that I would have to release parent module whenever any of child modules are released. Although this is doable, it would impact the original idea of having an independent versioning.
I would suggest using dependencyManagement in the parent pom. You can define all de versions of all dependencies there.
You can then omit the version of the dependencies in the different modules
So in the parent:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mygroup</groupId>
<artifactId>myModule</artifactid>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
In the module you then use
<dependencies>
<dependency>
<groupId>mygroup</groupId>
<artifactId>myModule</artifactid>
</dependency>
</dependencies>
The version as defined in dependencyManagement will be used.
I would recommend defining the version of all dependencies like this in the parent pom.
I started a thread with this topic on Maven mailing list, I'm posting TL;DR answer here:
My expected solution with implicit properties is generaly not possible because it would get evaluated not only during build but also when using the built artifact as a dependency in other projects and it's evaluation can have unpredictable results then, because the context of implicit properties is completely different. (I'm stupid, I had the same issue already some time ago with ${project.version} property)
Generaly speaking, the question considers only one scenario in one project from one developer point of view. In the mailing list thread there are other points of view that leads to hardcoding the dependency version numbers either in dependency management or in explicit properties even if something else would be possible.
We encountered a situation where Maven chose an inconsistent version of an indirect dependency, and I'd like to understand why, and how to prevent this in the future.
Our pom.xml file had the following dependencies:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
but without a direct dependency on spring-core.
Both depend on spring-core: spring-web 3.1.2.RELEASE depends on spring-core 3.1.2.RELEASE (see its pom-file), and spring-data-jpa 1.1.0.RELEASE depends on an arbitrary 3.x version of spring-core (to be precise [3.0.7.RELEASE,4.0.0.RELEASE), see its pom-file).
Combining both, I would expect Maven to choose spring-core version 3.1.2.RELEASE. However, it does not. Instead, it chooses the highest from the range [3.0.7.RELEASE,4.0.0.RELEASE), which currently is 3.2.0.RELEASE.
(Reproduction scenario: put the above pom.xml file (gist) in its own directory, and run mvn dependency:tree -Dverbose=true: for me the result is this tree (gist). I get the same result for both Maven 2.2.1 on Linux and Maven 3.0.4 on Windows.)
This seems wrong, because it is inconsistent: a version of spring-core which spring-web's pom-file does not allow is used.
(This occurred for us when spring-core 3.2.0.RC1 became available: on the next update it was suddenly selected, and we were lucky that we got a build error because of an incompatible change between spring-core 3.1 and 3.2. But next time we may not be so lucky, and have runtime errors which are very difficult to track down.)
Urghh: I just notice that the order of <dependency> declarations matters: if I put spring-web first, then spring-core 3.1.2.RELEASE is selected. What gives?
Question: How can we make Maven choose consistent versions of indirect dependencies, or at least warn if it makes a choice which goes against a version specified in a pom-file?
Update: I am asking for a general solution here. For this specific case, I know that I can get the correct behavior by adding a dependency in <dependencyManagement>, specifying that I always want spring-core 3.1.2.RELEASE. However, I would like Maven to do the Right Thing (TM) without such specific declarations.
What you expect seems logic but Maven has no chance to do this. It just resolves dependencies without the knowledge that spring-data-jpa might have somethig to do with spring-core.
The dependency resolution works as describe here and is the way you already described:
Dependency mediation - this determines what version of a dependency
will be used when multiple versions of an artifact are encountered.
Currently, Maven 2.0 only supports using the "nearest definition"
which means that it will use the version of the closest dependency to
your project in the tree of dependencies. You can always guarantee a
version by declaring it explicitly in your project's POM. Note that if
two dependency versions are at the same depth in the dependency tree,
until Maven 2.0.8 it was not defined which one would win, but since
Maven 2.0.9 it's the order in the declaration that counts: the first
declaration wins.
So I think the only way to prevent this situation is to be aware of it and to explecitly set the version you need in your own pom.
By the way, why do you think "This seems wrong, because it is inconsistent: a version of spring-core is used which spring-web's pom-file does not allow."?
A version specification of <version>x.y</version> is only a recommendation (see 'Note' here) to use this version. If you intend to force to use this version you have to set <version>[x.y]</version>.
In your dependency declaration, you can add the part to define the indirect dependencies to not import for this dependency.
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