Exclude dependency in a profile - maven-2

I have a maven module which has some dependencies. In a certain profile, I want to exclude some of those dependencies (to be exact, all dependencies with a certain group id). They however need to be present in all other profiles. Is there a way to specify exclusions from the dependencies for a profile?

To my knowledge, no, you can't deactivate dependencies (you can exclude transitive dependencies but this is not what you are asking for) and yes, what you are currently doing with the POM (manually editing it) is wrong.
So, instead of removing dependencies, you should put them in a profile and either:
Option #1: use the profile when required or
Option #2: mark the profile as activated by default or put it in the list of active profiles and deactivate it when required.
A third option would be (not profile based):
Option #3: separate things in two separated modules (as you have separated concerns) and use inheritance.

Instead of excluding dependencies in a profile, you can set them as provided in it. This doesn't require any overly complex configuration and will exclude the dependencies you don't want from the final build.
In the desired profile, add a dependencies section, copy the declaration of the ones you want to exclude and scope them as provided.
For example, let say you want to exclude slf4j-log4j12:
<profiles>
<!-- Other profiles -->
<profile>
<id>no-slf4j-log4j12</id>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
<!-- Other profiles -->
</profiles>

One way that occurs to me is to have the dependencies in a separate pom. You can then add an <exclusions> section via the profile.
<dependencies>
<dependency>
<groupId>my.company.dependencies</groupId>
<artifactId>my-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
</dependency>
</dependencies>
<profile>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>exclude-deps</name>
</property>
</activation>
<dependencies>
<dependency>
<groupId>my.company.dependencies</groupId>
<artifactId>my-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>my.company</groupId>
<artifactId>bad-dep-1</artifactId>
</exclusion>
<exclusion>
<groupId>my.company</groupId>
<artifactId>bad-dep-2</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</profile>

I don't think it is possible to exclude direct dependencies either (at least, nothing is mentioned here).
The best thing you can do is to enclose the desired dependencies for each case into different profiles (as suggested already), but, you'll need to create two "mutually exclusive" profiles with the one of them "active by default". The most reliable way to achieve this is by using a parameter for your profile activation e.g.
<profiles>
<profile>
<id>default-profile</id>
<activation>
<property><name>!exclude</name></property>
</activation>
<dependencies>
dependency-A
dependency-B
...
</dependencies>
</profile>
<profile>
<id>exclude-profile</id>
<activation>
<property><name>exclude</name></property>
</activation>
<!-- exclude/replace dependencies here -->
</profile>
</profiles>
Then using "mvn [goal]" will use profile "default-profile", but "mvn [goal] -Dexclude" will use profile "exclude-profile".
Note that using 'activeByDefault' instead of a parameter for your "default" profile might work in some cases but it also might lead to unexpected behavior. The problem is that 'activeByDefault' makes a profile active as long as there is no other active profile in any other module of a multi-module build.

maven is a tool, we can hack it.
maven runs fine if you have the same artifact + version defined as dependency twice.
define a profile that eliminates an artifact + version by changing it to another package we already have.
For example, in the pom.xml:
... other pom stuff ...
<properties>
<artifact1>artifact1</artifact1>
<artifact2>artifact2</artifact2>
<artifact1.version>0.4</artifact1.version>
<artifact2.version>0.5</artifact2.version>
</properties>
<profile>
<id>remove-artifact2</id>
<properties>
<artifact1>artifact1</artifact1>
<artifact2>artifact1</artifact2>
<artifact1.version>0.4</artifact1.version>
<artifact2.version>0.4</artifact2.version>
</properties>
</profile>
Now if you install this pom.xml without the profile, artifact1:0.4 and artifact2:0.5 will be the dependency.
But if you install this pom.xml with the profile mvn -P remove-artifact2
The result pom.xml contains only artifact1:0.4
This comes quite handy during api migration where artifact are renamed and versions are not compatible.

Bit dirty but lightweight solution is to use <scope>import</scope>.
Unlike the other scopes you could use this:
will disable compile-time and runtime dependecies; unlike provided or runtime which disables only one at a time
won't mess up your test scope
you don't need to specify path to some dummy jar as would system scope require
Nothing gets imported as long as you use this hack outside dependencyManagement.

Related

Add jar (dependency with scope system) in an Ear build

I work with Maven and I want to do a build with packaging ear, i want to add a dependency with scope system and also with specifing the systemPath of the jar like follow:
<dependency>
<groupId>group1</groupId>
<artifactId>group1</artifactId>
<version>1</version>
<scope>system</scope>
<systemPath>D:\Buildear\Jars\file.jar</systemPath>
</dependency>
But I don't found the jar in my generater ear!!!
Help please.
I work with Maven and I want to do a build with packaging ear, I want to add a dependency with scope system (...). But I don't found the jar in my generater ear!!!
Yes, that's just what you get when (ab)using a system scoped dependency which is supposed to be always available by definition. I wrote many times about this, for example in this previous answer that I'm quoting below:
I already wrote many, many,
really many times about this
here on SO and in 99% of the cases,
system scoped dependencies should be
avoided. And I'll repeat what the
Dependency Scopes mini guide says
one more time:
system: This dependency is required in some phase of your
project's lifecycle, but is
system-specific. Use of this scope
is discouraged: This is considered an
"advanced" kind of feature and should
only be used when you truly understand
all the ramifications of its use,
which can be extremely hard if not
actually impossible to quantify.
This scope by definition renders your
build non-portable. It may be
necessary in certain edge cases. The
system scope includes the
<systemPath> element which points to
the physical location of this
dependency on the local machine. It is
thus used to refer to some artifact
expected to be present on the given
local machine an not in a repository;
and whose path may vary
machine-to-machine. The systemPath
element can refer to environment
variables in its path: ${JAVA_HOME}
for instance.
So, instead of using the system
scope, either:
Add your libraries to your local repository via install:install-file.
This is a quick and dirty way to get
things working, it might be an option
if you're alone but it makes your
build non portable.
Install and run an "enterprise repository" like Nexus, Archiva, or
Artifactory and add your libraries via
deploy:deploy-file. This is the
ideal scenario.
Setup a file based repository as described in this previous answer
and put your libraries in there. This
is the best compromise if you
don't have a corporate repository but
need to work as a team and don't want
to sacrifice portability.
Please, stop using the system scope.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>aaa</artifactId>
<groupId>aaa</groupId>
<version>1.0</version>
</parent>
<groupId>aaa</groupId>
<artifactId>aaa</artifactId>
<version></version>
<packaging>ear</packaging>
<name>aaa - Ear</name>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-ejb</artifactId>
<version>${project.version}</version>
<type>ejb</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-webapp</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jboss</groupId>
<artifactId>jbosssx</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${aaa.name}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<generateApplicationXml>false</generateApplicationXml>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<modules>
<ejbModule>
<groupId>${project.groupId}</groupId>
<artifactId>aaa-ejb</artifactId>
</ejbModule>
<jarModule>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<excluded>true</excluded>
</jarModule>
</modules>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<aaa.name>aaa-batch</aaa.name>
</properties>
This creates an ear and copies the libraries into the lib folder in the ear.

How to activate a Maven profile in a dependent module?

Suppose I have a module A:jar, whose runtime and compilation set of dependencies depends on the JDK version. In my example, I have a pre-jdk6-profile for JAXB API: prior to JDK 1.6.0 I need to include jaxb-api-nnn.jar as a compile dependency. This profile is placed to A.pom.
I also have module B:war, which depends on A:jar. I want to be able to activate this profile on a build server to build the JDK 1.5.x deliverable. When I execute Maven with a given profile activated, I get the message:
mvn -Ppre-jdk6-profile -o install
[WARNING]
Profile with id: 'pre-jdk6-profile' has not been activated.
and jaxb-api-nnn.jar is missing in resulting B.war. However if I activate this profile when building from the parent pom.xml, everything is OK. That means the profiles are not inherited from dependencies, and the parent multi-module pom.xml was able to build everything correctly because it seems like all profiles are merged in reactor.
Shifting the profile to parent pom makes things worse, as the dependencies are applied to all other projects (e.g. to C:ear). Are there nice solutions for this task, namely, if any module A depends on module B, then all compile and runtime dependencies which are activated by a profile, are correctly handled?
The profile in project A:jar follows:
<project ...>
<artifactId>A</artifactId>
<packaging>jar</packaging>
...
<parent>
<artifactId>P</artifactId>
...
</parent>
<profiles>
<profile>
<id>pre-jdk6-profile</id>
<activation>
<jdk>(,1.6.0)</jdk>
</activation>
<dependencies>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
...
</project>
a) In a multi-module build, you should always build from the top pom, never from an individual module. If you want to build only one module, use advanced reactor options (see mvn --help) like this:
mvn -pl mymodule
b) Define and activate the profile in the parent pom, but add the configuration in the child pom.
parent pom.xml
<profiles>
<profile>
<id>pre-jdk-6</id>
<activation>
<jdk>(,1.6.0)</jdk>
</activation>
</profile>
</profiles>
child pom.xml
<profiles>
<profile>
<id>pre-jdk-6</id>
<dependencies>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
Several notes way after the fact:
When you use -P profName, it activates a profile named 'profName'
After that, it disables all profiles that have an <activation> tag for them. It doesn't matter whether they are activated by the java version, as in the example, or by default or env value or anything.
That means the -P causes any otherwise activated profile to become deactivated.
Solution: Either use <activation><jdk>...</jdk></activation> or use -P but do not use both.

exclude a dependency from war

how to exclude a dependency from war but using it till testing or development
As the others suggested, scope=provided or scope=test is the way to go.
<scope>provided</scope> implies that the library will be present in the target system and doesn't need to be deployed. (Or in some cases like log4j must not be deployed, because otherwise classloader issues will result)
<scope>test</scope> suggests that the dependency is only needed for test code (and hence will not be needed or provided on the target system)
Here is the relevant documentation:
Introduction to the Dependency Mechanism
On a related note: A different use case is that where you use different databases on different servers. You can use profiles to deploy the correct drivers:
<profiles>
<profile>
<id>testserver</id>
<dependencies>
<dependency>
... (database driver a)
</dependency>
</dependencies>
</profile>
<profile>
<id>productionserver</id>
<dependencies>
<dependency>
... (database driver b)
</dependency>
</dependencies>
</profile>
<profile>
<id>localdevelopment</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
... (database driver c)
</dependency>
</dependencies>
</profile>
</profiles>
That way, if you just call mvn install, driver c will be deployed, whereas mvn install -Ptestserver and mvn install -Pproductionserver will include drivers a or b, respectively.
There is an option to specify scope with in the dependency tag. You can specify scope as test and it won't be included into your war but will only be used for tests.
You do it with <scope>provided</scope> tag.
<dependency>
<groupId>org.livetribe</groupId>
<artifactId>livetribe-jsr223</artifactId>
<version>2.0.6</version>
<scope>provided</scope>
</dependency>

maven conditional dependencies

We would like to bundle library dependencies from (Alfresco or Jackrabbit or ...) based on the customer choice. The number of dependencies actually varies based on the chosen vendor. How do we provide hooks at the maven level, so that the final product just includes the dependent jars as per customer selection.
You could achieve this by putting the needed dependencies into vendor-specific profiles in your pom:
<profiles>
<profile>
<id>Alfresco</id>
<dependencies>
...
</dependencies>
</profile>
<profile>
<id>Jackrabbit</id>
<dependencies>
...
</dependencies>
</profile>
</profiles>
Then you can activate the desired profile for your build like:
mvn -PJackrabbit install

Exclude all transitive dependencies of a single dependency

In Maven2, to exclude a single transitive dependency, I have to do something like this:
<dependency>
<groupId>sample.group</groupId>
<artifactId>sample-artifactB</artifactId>
<version>1</version>
<exclusions>
<exclusion>
<groupId>sample.group</groupId>
<artifactId>sample-artifactAB</artifactId>
</exclusion>
</exclusions>
</dependency>
The problem with this approach is that I have to do this for every transitive dependency contributed by sample-artifactB.
Is there a way to use some sort of wildcard to exclude all transitive dependencies at once instead of one-by-one?
What has worked for me (may be a newer feature of Maven) is merely doing wildcards in the exclusion element.
I have a multi-module project that contains an "app" module that is referenced in two WAR-packaged modules. One of those WAR-packaged modules really only needs the domain classes (and I haven't separated them out of the app module yet). I found this to work:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>app</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
The wildcard on both groupId and artifactId exclude all dependencies that normally would propagate through to the module using this dependency.
For maven2 there isn't a way to do what you describe. For maven 3, there is. If you are using maven 3 please see another answer for this question
For maven 2 I'd recommend creating your own custom pom for the dependency that has your <exclusions>. For projects that need to use that dependency, set the dependency to your custom pom instead of the typical artifact. While that does not necessarily allow you exclude all transitive dependencies with a single <exclusion>, it does allow you only have to write your dependency once and all of your projects don't need to maintain unnecessary and long exclusion lists.
One thing I have found useful:
If you put the dependency with the exclusions in the dependencyManagement section of either the parent POM for your project, or in an importable dependency management POM, then you don't need to repeat the exclusion (or the version).
For example, if your parent POM has:
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
....
</dependencies>
</dependencyManagement>
Then the modules in your project can simply declare the dependency as:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
The in the parent POM will specify both the version and the exclusions. I use this technique for nearly all of our projects and it eliminates a lot of repetition.
Three years ago I recommended using Version 99 Does Not Exist, but now I've figured out a better way, especially since Version 99 is offline:
In your project's parent POM, use maven-enforcer-plugin to fail the build if the unwanted dependency creeps into the build. This can be done using the plugin's banned dependencies rule:
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<id>only-junit-dep-is-used</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>junit:junit</exclude>
</excludes>
</bannedDependencies>
</rules>
</configuration>
</execution>
</executions>
</plugin>
Then when that alerts you about an unwanted dependency, exclude it in the parent POM's <dependencyManagement> section:
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<version>2.1.8.RELEASE</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
This way the unwanted dependency won't show up accidentally (unlike just an <exclusion> which is easy to forget), it won't be available even during compile time (unlike provided scope), there are no bogus dependencies (unlike Version 99) and it'll work without a custom repository (unlike Version 99). This approach will even work based on the artifact's version, classifiers, scope or a whole groupId - see the documentation for details.
I use the following workaround : instead of trying to exclude the artifact in all appropriate dependencies, I draw the dependency as "provided" at top level.
For example, to avoid shipping xml-apis "whatever version" :
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>[1.0,]</version>
<scope>provided</scope>
</dependency>
Currently, there's no way to exclude more than one transitive dependency at a time, but there is a feature request for this on the Maven JIRA site:
https://issues.apache.org/jira/browse/MNG-2315
if you need to exclude all transitive dependencies from a dependency artifact that you are going to include in an assembly, you can specify this in the descriptor for the assembly-plugin:
<assembly>
<id>myApp</id>
<formats>
<format>zip</format>
</formats>
<dependencySets>
<dependencySet>
<useTransitiveDependencies>false</useTransitiveDependencies>
<includes><include>*:struts2-spring-plugin:jar:2.1.6</include></includes>
</dependencySet>
</dependencySets>
</assembly>
There is a workaround for this, if you set the scope of a dependency to runtime, transitive dependencies will be excluded. Though be aware this means you need to add in additional processing if you want to package the runtime dependency.
To include the runtime dependency in any packaging, you can use the maven-dependency-plugin's copy goal for a specific artifact.
If you develop under Eclipse, you can in the POM Editor (advanced tabs enabled) dependency graph look for the dependency you want to exclude of your project and then:
right click on it -> "Exclude Maven Artifact ..." and Eclipse will make the exclusion for you without the need to find out on which dependency the lib is linked.
What is your reason for excluding all transitive dependencies?
If there is a particular artifact (such as commons-logging) which you need to exclude from every dependency, the Version 99 Does Not Exist approach might help.
Update 2012: Don't use this approach. Use maven-enforcer-plugin and exclusions. Version 99 produces bogus dependencies and the Version 99 repository is offline (there are similar mirrors but you can't rely on them to stay online forever either; it's best to use only Maven Central).
In a simular issue I had the desired dependency declared with scope provided.
With this approach the transitive dependencies are fetched but are NOT included in the package phase, which is what you want.
I also like this solution in terms of maintenance, because there is no pom, or custom pom as in whaley's solution, needed to maintain; you only need to provide the specific dependency in the container and be done
Use the latest maven in your classpath.. It will remove the duplicate artifacts and keep the latest maven artifact..