Limiting a transitive dependency to runtime scope in Maven - 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

Related

Specifying version in Maven optional dependency

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).

Transitive dependencies in 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...

JBoss Maven BOMs and transitive dependencies

I'm trying to use a number of the BOMs in the org.jboss.bom group to bring in the API stacks that are compatible with EAP 6.3.0. My understanding is, that's what they're for. But when I reference them (using 'provided' or even 'compile' scope), the dependencies don't become transitively available. Given that "compile" scope is used on the items inside the BOMs, Maven's documentation of the dependency mechanism seems to indicate that those items should be added to the classpath of my project. Yet I'm getting undefined symbols for the classes that should be brought in.
For example, in project P, I'm including org.jboss.bom.wfk:jboss-javaee-6.0-with-spring:2.4.0-redhat-2 with 'provided' scope, yet org.springframework.context.ApplicationContext is undefined in P.
This is all happening in JBoss Dev Studio 8.1.0.GA, if that makes a difference.
I figured out the solution myself by reading the Maven Dependency documentation in more detail.
Here's my take-away: you cannot depend on the Eclipse m2e plugin to see you through the Maven dependency weeds. Know when/how to use the <dependencyManagement> section of the POM (and when not to use it). Know in particular the specific invocation Maven needs when you want to use a BOM: import the BOM in a <dependencyManagement> section with <type> of pom and <scope> of import, and then in the "regular" <dependencies> section (not <dependencyManagement>) specifically call out the sub-artifacts you need from the POM, but omit the version. (It's all spelled out here.)
The intent of the BOM is not to allow you to mass-import dependencies by referencing only the BOM artifact; rather, it's to make sure the versions of dependencies are the right ones, as defined by the BOM.
Do not assume that Maven allows you to express things in logically/mathematically reduced terms. Find out how to please the beast, and do not rely on a wizard to figure this out for you. Read the Maven docs in detail, find out the recipes and follow them exactly.

Is it recommended to add all dependencies in the pom.xml? [duplicate]

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.

How do you import multiple build types of a native library with Android Maven Plugin

How do you import multiple build types of a the same native library with Maven?
A little context, I'm using the Android Maven Plugin, and I'm trying to import a native library that was build for several different CPU architectures. I read the post here that shows how to declare the dependency, but I still have to install the .so files locally in my repository. I'm not confused on how to install third party libs into my local repository, I'm confused about how to name these things. For example, say we have the dependency:
<!-- Declare the dependency on a native library, already deployed in the Maven repository -->
<dependency>
<groupId>com.acme.android</groupId>
<artifactId>libsample_jni</artifactId>
<version>0.1</version>
<scope>runtime</scope>
<type>so</type>
</dependency>
The code that loads the library would be:
System.loadLibrary("sample_jni");
But let's say we have multiple builds of libsample_jni that target different CPU architectures. What naming convention would I use so that the libs are put in the right folders in my APK? Is this even a naming convention issue, since the plugin is responsible for this? Is there some tag I need to include in the dependency declaration to do this?
In this case the different binary artifacts for different CPU architectures should be differentiated by specifying distinct classifier values in their maven naming co-ordinates.
See http://maven.apache.org/pom.html#Maven_Coordinates

Resources