Maven dependency exclusion for War file, but inclusion for tests - java

I have a maven POM file for a web service. For one of the dependencies I have to specify several exclusions for jar files that are already kept at a higher-level in the web-application server (accessible to all web-applications, not just this particular one). One example of such exclusion is the JAR containing my JDBC driver.
Example (with fictional details):
<dependency>
<groupId>mygroup</groupId>
<artifactId>myartifact</artifactId>
<version>1.0.0</version>
<exclusions>
<!--The jdbc driver causes hot-deployment issues-->
<exclusion>
<groupId>db.drivers</groupId>
<artifactId>jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
The problem I am encountering is that I need the JDBC driver for my tests. My tests currently fail since they cannot load the JDBC driver.
How can I configure the POM so that the excluded parts are accessible to my tests, but do not get included into my WAR file?
Update: I cannot make changes to the POM for mygroup.myartifact since this it is being depended on by many other projects, and this exclusion requirement is unique for my project.
Update 2: It seems I did a poor job of phrasing this question. Lars's solution below is perfect for one exclusion (as the example shows), however in my real scenario I have multiple exclusions, and adding additional dependencies for each seems smelly. The solution that seems to work is to set the scope of the shown dependency to compile and then create a second dependency the same artifact (mygroup.myartifact) with no exclusions and the scope set to test. Since Lars both answered my poorly phrased question correctly, as well as led me in the direction of the actual solution, I will mark his reply as the answer.

Use the "scope" tag inside your dependency.
<scope>test</scope>
http://maven.apache.org/pom.html#Dependencies
edit: if I understand your configuration correctly, the scope=test that you need to add should be added in the mygroup.myartifact POM. That way you can test that artifact with jdbc jar included, but always when other POMS want to include mygroup.myartifact, they don't get jdbc included as a transitive dependency.
second edit: Ok, if you don't control the POM you want to include - do an exclusion like you have already done, and then add jdbc as a new dependency, with scope=test.

Related

Force Maven use only first level dependencies

I have a Maven Java project. I don't want my project dependencies to be satisfied by chance through a chain of subdependencies when compiling the project. It is OK for me when building the final war when maven must check all used dependencies and add necessary libs to the war, but when compiling the code I want to be sure that only direct dependencies are used. Why?
Let's say I have two dependencies:
<dependency>
<groupId>com.package</groupId>
<artifactId>module-1</artifactId>
</dependency>
<dependency>
<groupId>com.package</groupId>
<artifactId>module-2</artifactId>
</dependency>
For our project module-1 and module-2 serve completely different purposes, but somewhere in the dependency tree of module-2, module-1 is used. I delete module-1 dependency, but maven continue to build my project without compilation errors, because it resolves module-1 from module-2 sub-dependencies. This change goes unnoticed.
After sometime we decide to remove module-2, because we don't need it. Strange enough but we can not any more compile classes which were using imports from module-1 and which are not connected to module-2 logic.
This is a simple case, but in big project this can make quite a dependency mess.
You can use the Maven dependency plugin goal "dependency:analyze" to give you a report of all used dependencies which are not declared on the current module (included transitively). That way Maven will still use transitive dependencies (no way around that I guess), but you can force yourself via the plugin to make sure these are also declared. It will also warn you of unnecessary dependencies. Mind, the plugin analyzes the compiled classes. At times, you may need to configure the plugin, because occasionally it may not detect that a dependency is required at compile time but not at runtime, e.g. because a constant was inlined.
If you really need to do this then you can setup exclusions in the pom.
e.g. here's an example of an exclusion in one of my poms where I don't want it to automatically get commons-logging because I'm using a different logging provider.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
You could do something like this (untested)
<dependency>
<groupId>com.package</groupId>
<artifactId>module-2</artifactId>
<exclusions>
<exclusion>
<groupId>com.package</groupId>
<artifactId>module-1</artifactId>
</exclusion>
</exclusions>
</dependency>
I wouldn't necessarily recommend this though. It makes sense in the case of my logging exclusion because I'm using slf4j instead of commons logging. I've seen other examples where this is used to exclude spring 2 if the project as a whole is using spring 3.
It's a bit difficult to tell from your example because it's so vague. In general you should keep your dependencies to a minimum. If module-2 depends on module-1 then it implies that your application won't compile or run without module-1. If in fact it can live happily without it then it's not really dependent.
As a side note it's a bit alarming that you don't have a version number against the dependencies. You'll probably find maven warns you about this. It's good practice to always include a version number. If you're dependent on a module which is currently in development then you should use the .SNAPSHOT suffix on the version to get the latest build for that version.
There seems to be no way to tell maven not to resolve dependency transitively: How to exclude all transitive dependencies of a Maven dependency. One of the reason's I think, is that the user can soon run into runtime troubles, when he finds that some of the artifacts are not being resolved at runtime or there are artifact versions problems. However, if you check the link out, you can make each of the deps 'standalone' with a wildcard exclusion pattern.
One other option is to use <optional> dependency for each of your module-X sub-dependencies. This will make sure the project compiles and non of your module-X would be resolved transitively. Like:
<dependency>
<groupId>com.package</groupId>
<artifactId>module-1</artifactId>
<optional>true</optional>
</dependency>
Still, analyzing the dependency tree might be the most safe and predictable choice.
It does sound a bit strange what you plan to do. In a way you sabotage the dependency management you want to use.
If your module-2 depends on module-1 and has a dependency to it, then any module that depends on module-2 only need to define that one.
You may be able to restrict the depth of the resolution using exclusions: Exclude all transitive dependencies of a single dependency
Newer versions of maven allow wildcards in those.
But: you will need to re-add the ones you actually need, this is by repeating the dependencies you have an other modules. This duplicates the work.
If there are artifacts that cause weirdness it may be possible to define a scope: http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html so it is not propagated to dependant modules as well.

How to add Spring libs using Maven

I learned Spring via Spring In Action 3 few month ago. I downloaded Spring libraries from official site (list was like in SIA3(aop, asm, aspects, beans ...)), added them to my project and everything worked fine. Now I want to use Maven, but I am getting a lot of errors and sinking in searching what library to add.
I am newby, dont know all Spring dependencies(within it libs) and the question is not about my errors, but about the way to add all Spring libraries to my project via Maven. How do you usually add Spring libs using Maven?
You don't have to download the libraries themselves anymore. That is what Maven is for. (and quite some more, of course)
set up Maven properly
set up Maven in the IDE tool you have (like this)
edit the pom.xml to include what you need, adding the dependencies in the in the dependencies tag.
Maven takes care of resolving the dependencies of the specified packages. If a package depends on other packages, it will do it for you. You only have to specify the packages you directly need
For example
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
You can easily find the packages using Google, and searching for "maven repository "
Avoiding version clashes
Also, as Bart mentioned, the common way of having Spring in the pom.xml - as it has way too many versions, and clashes can occur - is through a common property specifying the version for all Spring components. (Based on this answer)
Specify the property in the properties tag:
<properties>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
Then use it in the dependencies like this:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
Be careful to use it for ALL components to avoid version clashes. (of course, issues mught still occur, bz having different libraries reference spring too, but that is another story in its own.)
Side note
Keep in mind note that Maven projects use specific directory layout. When I first started using maven for my own projects, first I created a new blank one, and played around with it, before I began migrating my older projects to use maven. Believe me, it pays off.
Add spring artifacts to your pom.xml file. For example
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.4.RELEASE</version>
You can find more artifact info here
http://mvnrepository.com/
HERE you can find the dependencies as per your requirement. Just click on the dependency and inside click on the latest release, scroll down there is your code inside the <dependencies> Your required dependency and version</dependencies>.
Just copy the XML code and paste it in your pom.xml file.

Solving Maven dependency convergence issues

I use the maven-enforcer-plugin to check for dependency convergence issues. A typical output would be:
[WARNING] Rule 1: org.apache.maven.plugins.enforcer.DependencyConvergence failed
with message:
Failed while enforcing releasability the error(s) are [
Dependency convergence error for junit:junit:3.8.1 paths to dependency are:
+-foo:bar:1.0-SNAPSHOT
+-ca.juliusdavies:not-yet-commons-ssl:0.3.9
+-commons-httpclient:commons-httpclient:3.0
+-junit:junit:3.8.1
and
+-foo:bar:1.0-SNAPSHOT
+-junit:junit:4.11
]
Seeing this message, I would normally "solve" it by excluding the transitive dependency, e.g.
<dependency>
<groupId>ca.juliusdavies</groupId>
<artifactId>not-yet-commons-ssl</artifactId>
<version>0.3.9</version>
<exclusions>
<!-- This artifact links to another artifact which stupidly includes
junit in compile scope -->
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
I'd like to understand whether this is truly a fix and the risks involved in excluding libraries in this fashion. As I see it:
The "fix" is normally safe, provided I'm choosing to use the newer version. This relies on the library authors maintaining backwards compatibility.
There is typically no impact on the Maven build (since the nearer definition wins), however by excluding the dependency I'm telling Maven that I know about this problem and thus appeasing the maven-enforcer-plugin.
Are my thoughts correct and is there an alternative way of handling this issue? I'm interested in answers that focus on the general case - I realise the junit example above is a little strange.
We all agree that JUnit should never be set to another scope than test. Generally speaking I don't think either that there is another solution than excluding the unwanted dependency, so we all agree that your are right to do it.
A SIMPLE CASE :
As Andreas Krueger says, there may be a risk with versions (I actually encountered it). Let say that the project's dependencies are the following:
+-foo:bar:1.0-SNAPSHOT
+-group1:projectA:2.0
+-group2:projectB:3.8.1
+-group2:projectB:4.11
Note that it is only a mere simplification of your case. Seeing this dependency tree, you would exclude the dependency projectB given by projectA :
<dependency>
<groupId>group1</groupId>
<artifactId>projectA</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>group2</groupId>
<artifactId>projectB</artifactId>
</exclusion>
</exclusions>
</dependency>
After packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 3.8.1. Everything would be fine and the project would run without encountering any problem at all.
Then, a while after, let say that you decide to upgrade to the next version of project A, version 3.0 which adds new great features :
<dependency>
<groupId>group1</groupId>
<artifactId>projectA</artifactId>
<version>3.0</version>
<exclusions>
<exclusion>
<groupId>group2</groupId>
<artifactId>projectB</artifactId>
</exclusion>
</exclusions>
</dependency>
The problem is that you are not aware yet that projectA version 3.0 also have upgraded its dependency projectB to version 5.0 :
+-foo:bar:1.0-SNAPSHOT
+-group1:projectA:3.0
+-group2:projectB:5.0
+-group2:projectB:4.11
In that case, the exclusion you would have made a while ago excludes projectB version 5.0.
However, projectA version 3.0 needs the improvements from project B version 5.0. Because of the exclusion, after packaging the project with maven, the remaining dependency would be group2-someProjectB-4.11.jar, version 4.11 and not 5.0. At the moment you use any of projectA's new features, the program wouldn't run correctly.
WHAT WAS THE SOLUTION ?
I encountered this problem in a Java-EE project.
A team developped database services. They packaged it as projectA. Each time they updated the services, they also updated a file listing all their current dependencies and the current versions.
ProjectA was a dependency for the Java-EE project I was working on. Each time the service-team updated ProjectA, I also checked the versions' updates.
In fact, there is no harm in excluding a dependency. But each time you update a dependency where an exclusion has been set, You have to check :
if this exclusion still makes sense.
if you need to upgrade the version of the excluded dependency in your own project.
I guess maven exclusions are like kitchen knifes. It's sharp, cuts vegetables with no effort, but requires care when handling it...
If JUnit as an artifact is coming through as a dependency in compile scope, it is a bug of one of your libraries, here: ca.juliusdavies.
JUnit should always be included in test scope. Thus, it is not packed into the produced .jar, .war or .ear file, on successful build.
Generally speaking, there is no harm in excluding already included dependencies, as when library 1 and library 2 share one common dependency.
The only problem, of course, that can occur, is when library 1 and library 2 include different versions of the same dependent artifact. This can cause run-time errors, when the features of the library have changed.
Fortunately, this is not often the case, unless the difference in the version numbers is great. In general, it is advisable to include the latest dependency version and exlude the older one. This is most of the time viable.
If not, check wheter there are updates to the first-level dependencies of your project.

How to handle sub projects dependencies in Maven

My project is made of 5 sub projects. One is a War, and the other 4 are jars. Basically the war project needs all 4 jar projects, and their dependencies.
I can strip down the dependencies to have something like war->A->B->C->D. Every sub project add their share of external dependencies (spring, struts, hibernate) so that in the end the war gets everything needed to run.
This looks pretty well organised and square, but then I ask myself if this is very practical to make changes.
Imagine I have to change one line of code in project D, without changing anything to its Maven dependencies. I would have to re-release project D obviously, but then I have to re-release projects C, B, A, and the war just to reflect this change in their pom files. This can be long and annoying, especially if you have to quickly release a new version to fix something in production.
I could make the war depend on all 4 projects, so then I just have to change project D version number in the war pom file. But then I have project A depending indirectly on project D 1.0 and the war specifying project D 1.1. I think that the war direct dependency would win in that case wouldn't it ?
This would make the new war release quicker, but it would also mess my sub projects dependencies, as they would be outdated.
What would be an acceptable way to handle this situation ?
There is no simple answer to your problem.
If you indeed do have a chain of transitive dependencies (A->B->C->D), then releasing each modules up the chain independently is not a bad option. Although it is tedious, there is a good chance your nested dependencies are simple lib jars and will not see changes too often. Hopefully you will not be forced to go through that process frequently. Pretend it would be the same situation as if log4j was updated and all of your modules needed to be updated as well.
Another thing to consider is your WAR's dependencies. Yes, Maven will pull dependencies in automatically for you but it is often a good practice to declare your known dependencies explicitly so you can specify a version number yourself for each module. This would mean A depends on D and the others directly. Unfortunately, if you have conflicting version numbers, as you've described, then you are looking for trouble on your classpath. If you really need to do this though, maven does allow you exclude transitive dependencies explicitly:
<project>
...
<dependencies>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-B</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-C</artifactId>
</exclusion>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-C</artifactId>
<version>1.0</version>
<exclusions>
<exclusion>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>my.project</groupId>
<artifactId>module-D</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
...
</project>
Here is the documentation describing these optional dependencies and exclusions.
Do you actually need to release B, C, and D independently? If not, consider using an Aggregator pom.xml file at the root of your modules. This will allow you to use SNAPSHOT versions throughout your modules and then release the bunch at once. This is the way our team manages our multi-module project. Using SNAPSHOT dependencies ensures you use the version that was JUST built when those artifacts are needed.
Do you actually release any of projects A to D independently, without the WAR? If not, I don't see any problems with your current setup. You should absolutely use the same version of any module throughout the project. Otherwise you open the door to classloader hell - believe me, you don't want to get there :-(
To make releases easier, the maven-release-plugin may help you.
The best answer these days is now use gradle which is best of ant and maven. I never really liked maven but gradle took alot of the common concepts but made it more like ant in that it is flexible so that there is no an easy answer to your question in gradle ;).

dependency management with maven

I have lately become a big fan of Maven for controlling the build cycle for my application. However I've encountered some rough edges with Maven's dependency management. I'm wondering if these are limitations of the tool and paradigm, necessary evils of dependancy management, or if I"m just using the tool incorrectly.
First is the matter of transitive dependencies. As I understand it, if you provide a dependency, Maven will in turn find any dependencies of that dependency. That is great, but for many of my dependencies, this has not worked. For example, including Hibernate in my project:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.3.2.GA</version>
</dependency>
Results in a missing dependency of slf4j. I need to manually add this dependency which I assumed would be Maven's job. The same goes for Spring. If I add Spring-MVC as a dependency, shouldn't all of the basic servlet dependencies be added for me (because Spring-MVC would need this stuff)? I'm referring to the servlet, jsp, jstl libraries.
Second is the management of repositories. Maven comes shipped with a default main repository, but I've found that in many cases this repository is not up to date. For example, ifyou want spring3, you have to manually add the springsource repository, and if you want hibernate 3.5+ you have to add the jboss repository. It seems to defeat the point of automatic dependency management when you have to hunt down the correct repositories yourself. This hunting soon gets complicated. For example to add Spring3, you may want the spring release repo, the spring externals repo and the spring milestone repo.
Closely related to number 2 is ensuring you have the correct version of an artifact. I have been burned several times by including the wrong versions of dependent artifacts for a given artifact. For example the wrong version of the servlet/jsp/jstl apis for spring3, or the wrong version of persistence / annotation apis for hibernate. The repositories are filled with many versions, some with confusing names like productx-3.ga, productx-3-rc1, productx-3-SNAPSHOT, productx-3-cr, product-3-beta, etc. Some of these are obvious (rc= release candidate), but it can be confusing trying to determine the order of these versions.
Finally, the issue of the type a dependency. I probably just don't understand this well enough, but many repo artifacts are of type "pom" not "jar". Several times i have added a dependency jar to my project only to find out at build time that the repo jar does not actually exist (example is org.hibernate ejb3-persistence in the jboss repo).
With some experimenting, I can usually get a build to work, but is dependency management in general this complicated? I still prefer this approach to manually adding jar files to my project, but I would be interested to learn how to improve my maven dependency management skills.
Can't answer all parts of the question, but about some of them:
Some transitive dependecies are marked optional, so people who don't need these features wouldn't download them, but people, who need, have to set them explicitly in their poms.
Maven Central repository contains only releases. Therefore, it doesn't contain Hibernate 3.5 (which is beta) as well as it hadn't contained Spring 3 until it was released (by the way, you don't need to specify special Spring repository for Spring 3 any more - release is already in Maven Central)
slf4j is a very special kind of dependency - its runtime behavior depends on which implementation of slf4j you use. Therefore, to control its behavior you have to specify slf4j implementation explicitly
About management skills: to get useful information for maintaining your pom.xml you can use mvn dependency:tree (especially with -Dverbose=true) and mvn dependency:analyze. It can be also useful to check pom file of your dependency to look for optional dependencies.
First is the matter of transitive dependencies. As I understand it, if you provide a dependency, Maven will in turn find any dependencies of that dependency. That is great, but for many of my dependencies, this has not worked. (...)
As already pointed out, some dependencies may be marked as optional (and are not pulled transitively). The idea is that some dependencies are only used for certain features and will not be needed if that feature isn't used. If a user wants to use functionality related to an optional dependency, they will have to redeclare that optional dependency in their own project. So this just works as designed :)
Second is the management of repositories. Maven comes shipped with a default main repository, but I've found that in many cases this repository is not up to date. (...)
Even if the idea behind the concept of a central repo is noble, you can't objectively expect it to contain all jars in the world. One of the most obvious reason is that uploading artifacts to the Central Repository just takes time and resources are not infinite. And because companies like RedHat JBoss or SpringSource or Sun or even me need flexibility, reactivity (in one word, control), it's not surprising that they use their own repository. And, actually, I'm pretty happy that they expose them. But indeed, projects need to document where to find their artifacts if they are not available in central. Just in case, you may find this How to find dependencies on public Maven repositories? helpful. In a corporate environment, the best way to handle this would be to setup a centralized (corporate) proxying repository. See this page for such solutions.
Closely related to number 2 is ensuring you have the correct version of an artifact. (...)
Sorry but you need a bit to know what you're are doing. A project can't guess for you what JSTL version you are going to use. Then, regarding the various versions of artifacts, the naming convention used by projects has nothing to do with maven, this is a project/vendor choice (except for SNAPSHOT that maven handles specially). FWIW, common used schemes include: M1 = Milestone 1, RC1 = Release Candidate 1, GA = General Availability (final release), CR = Customer Release (often a bug-fix release). You may also see alpha, beta. This really depends on the project lifecycle and convention (nothing really unusual here though).
Finally, the issue of the type a dependency. I probably just don't understand this well enough, but many repo artifacts are of type "pom" not "jar". (...)
I think that you're indeed lacking of experience. You seem to be fighting with dependencies while things just go smoothly for me :) Maybe using a repository search engine will help.
You can also exclude some transitive dependencies, like the following example:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
</dependency>
I don't remember the details, but apparently some other dependencies were being discovered when we wanted to use log4j, and we had no interest (or need) in them in our case, so my cow orker just said "no thanks to these" and it works.
Transitive dependencies are only there if they have been encoded explicitly in your direct dependencies' POM's. Sometimes you actually do not want them, either because there are alternative implementations or you're using only part of a library and you don't want to drag in dependencies for things you're actually not using.
You are using a repository manager such as Nexus, aren't you? I advise you to setup one even if you code alone.
I find that Maven actually helps with this problem. I would hate having to perform this trial-and-error from ftp sites and download pages.
Yes, I hate that too :-)
Read the book! I actually learned most of what I know from this other book, but given it comes from the same people, I expect them to be rather interchangeable.

Categories