How can I "un-inherit" a Maven dependency? - maven

I have a large multi-module project. 99% of the modules have a dependency on a certain third-party library (let's call it A), so the dependency on A is recorded in the project parent POM & thus inherited by all of the modules. In these 99% of cases A is provided as part of the runtime environment & thus the dependency is accordingly marked as provided.
I've run across a situation today where one of the child modules (actually a grandchild, if that matters) must not have a dependency on A. How can I remove A from the dependency list of this module?
I've tried combine.self="override" but that construct apparently isn't allowed in the <dependencies> element.

Use dependencyManagement and add dependency exclusion of the artifact that you don't need.
This works as a charm on excluding commons-logging from my spring dependencies:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-framework.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>

Related

Maven how to exclude dependecy which appears multiple times

I'd like to know if there is a way to exclude sdk-s3 just one time. I want to do it because I don't use it and also maven for some reason, starts downloading all the sdk-s3 versions and takes a long time to finish.
Is there a way to exlude this dependecy globally? Thanks
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sqs</artifactId>
<version>1.11.591</version>
<exclusions>
<exclusion>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-sts</artifactId>
<version>1.11.591</version>
<exclusions>
<exclusion>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>1.11.591</version>
<exclusions>
<exclusion>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</exclusion>
</exclusions>
</dependency>
There is no real way to exclude a dependency globally. You can set the scope of the dependency to provided in <dependencyManagement>. This makes sure that the dependency will not be included in the resulting war or ear. It will still be on the compile classpath, though. You could also use the scope test for that.
This scope based approach is of course not what the developers of Maven intended.
Note furthermore, that Maven downloads dependencies only once and caches them in the local repository afterwards. If you want to avoid to have multiple versions, you can fix one version in the <dependencyManagement>.
As #JF Meier said, there is no real way to exclude it from all the transitive dependencies.
If the problem is that it's causing a conflict with another version of the same dependency, you override this, but explicitly defining the dependency with the version that you're interested in. This way, as it will be higher in the hierarchy, you can override all the transitive dependencies from where it's coming.

How to remove spring boot dependency using dependencyManagement?

In my pom file i have the following:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I use this because the project already have a parent pom.
I want to remove some of its dependencies such as:
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.4.3</version>
</dependency>
How do I do this?
spring-boot-dependencies does not add any dependency. It mostly consists out of a giant <dependencyManagement> block containing managed versions for several libraries. This allows you to use versions of libraries that are known to work properly with the given version of Spring boot.
That means that you no longer have to add the <version> to each dependency you define.
It also means that if you have a dependency upon elasticsearch, it certainly doesn't come from spring-boot-dependencies.
If your goal is to override one of the versions, you can, by manually adding <version> to your dependency.
Otherwise, you can usually exclude a dependency by using <exclusions>:
<dependency>
<groupId>com.xyz</groupId>
<artifactId>artifact-abc</artifactId>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>

excluding spring jars from spring-data maven dependency

I'm including spring-data in my pom.xml like so:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
However it includes a bunch of older Spring 3.2.8 jar files that I don't want bundled. Do I need to put exclusions for the 7 or so spring jars to not be included?
Thanks!
Yes, set exclusions. You can either set all of them individually or if you want to exclude all transitive Spring dependencies that come with Spring Data you may use a wildcard.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.5.1.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
Maven will log a warning because that feature will only be supported in upcoming Maven versions even though it's been there for years.

How to make Maven warn about an arbitrary version choice?

In a Maven project we used a third-party artifact (specifically, spring-data-jpa 1.1.0.RELEASE) which depends on another artifact (spring-core) allowing any version in a range (to be precise: [3.0.7.RELEASE,4.0.0.RELEASE), see its pom-file). We had no direct dependency on spring-core.
So one day our build chose 3.1.2.RELEASE, but when 3.2.0.RC1 was released then our build suddenly picked up that version.
However, we would like to have repeatable builds: when we deliver a patch in a year's time, we don't want to pull in an updated version of spring-core, or any other indirect dependency, without at least knowing about it.
(I know that we can guide Maven to choose one specific version for spring-core, e.g., using <dependencyManagement>, but my point here is that there may be arbitrary choices hidden in indirect dependencies, and I'd like Maven to tell us about those, without having to manually check this regularly.)
Question: How can we make Maven warn us if it makes an arbitrary version choice for any indirect dependency?
As you have discovered, version ranges are evil.
The real issue is that version ranges are a siren that seduces people into thinking they are a good idea.
A version range should really be seen as a hint to the developer to allow the developer to choose the version they want from a set of versions.
The mistake in Maven was in allowing version ranges to be defined within the pom.xml in the first place as that allows for people to publish their artifacts with version ranges in them.
Once you have a dependency on an artifact which has transitive dependencies that use version ranges, there are really only two ways to solve the problem for your build (and one is just a more fancy version of the second)
Add your own dependency on the transitive dependency but with a pinned version in place of a range... e.g.
<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-orm</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
You don't need to list <optional>true</optional> dependencies as they are not transitive and similarly you don't need to list <scope>provided</scope> dependencies either for the same reason.
As for the above, but being safer by adding exclusions to the dependency in the first place, e.g.
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Of the two of these, I prefer the latter as it at least gives people a hint as to why those dependencies are being explicitly mentioned.
So to get back to your original question, the point is that you need to set up this dependency tree when you add or update dependencies in your pom.xml.
If spring-data-jpa:1.1.1.RELEASE had a completely different transitive dependency tree with different coordinates, it is when you are editing the pom.xml to update the version that you should also fix the transitives.
There is not, to my knowledge, currently any enforcer rules to support validating what you require.
I would recommend writing an enforcer rule which I would call something like: ensureTransitiveVersionRangesArePinned
That rule should do the following:
Scan the list of project dependencies
Compute the transitive dependencies provided by each project dependency
If any of those transitive dependencies are version ranges then
validate that there is an exclusion for that transitive dependency
validate that there is a pinned version of the transitive dependency as a direct project dependency (may not be a failure if there is no pinned version, as you may be adding an equivalent artifact that is at a different GAV, or you may not need the dependency)... in any case if the dependency is not added back in, most likely the unit tests should catch that by triggering a CNFE so this check is probably not strictly required, but it should perhaps print a warning.
I cannot recall if there is tooling to check that the <exclusions> are actually excluding any transitive dependencies, so you may need to investigate that.

How to exclude maven dependencies?

I have a question about exclusion of maven dependencies. Consider the following
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.0.RELEASE</version>
</dependency>
<dependency>
I am trying to achieve a transition from Spring 3.0.6 to 3.1.0 . Spring security 3.1.0 had a dependency on spring-security-web version 3.0.6 which in turn had a dependency on spring-web 3.0.6. I need to bring it all to 3.1.0. So I exclude spring-security-web from Spring security, have a separate dependency for spring-security-web 3.1.0 which in turn excludes the spring-web 3.0.6 version and I provide a separate spring-web 3.1.0 version. This work but I feel there would be a much easier approach. I tried putting an exclusion for spring web under Spring security but it did not work.
You can utilize the dependency management mechanism.
If you create entries in the <dependencyManagement> section of your pom for spring-security-web and spring-web with the desired 3.1.0 version set the managed version of the artifact will override those specified in the transitive dependency tree.
I'm not sure if that really saves you any code, but it is a cleaner solution IMO.
Global exclusions look like they're being worked on, but until then...
From the Sonatype maven reference (bottom of the page):
Dependency management in a top-level POM is different from just
defining a dependency on a widely shared parent POM. For starters, all
dependencies are inherited. If mysql-connector-java were listed as a
dependency of the top-level parent project, every single project in
the hierarchy would have a reference to this dependency. Instead of
adding in unnecessary dependencies, using dependencyManagement allows
you to consolidate and centralize the management of dependency
versions without adding dependencies which are inherited by all
children. In other words, the dependencyManagement element is
equivalent to an environment variable which allows you to declare a
dependency anywhere below a project without specifying a version
number.
As an example:
<dependencies>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
It doesn't make the code less verbose overall, but it does make it less verbose where it counts. If you still want it less verbose you can follow these tips also from the Sonatype reference.

Resources