What is the best way to declare a Maven dependency as only being used for the test runtime (but not test compilation) class path?
Specifically, I want slf4j-api (a logging facade) as a typical, compile-scope dependency, but I want slf4j-simple (the barebones implementation suitable for unit tests) only on the test runtime class path (it's not needed for test compilation). I've been doing this:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
However, the downside of this is that dependency:analyze reports slf4j-simple as unused, presumably because it's not needed for compilation:
[WARNING] Unused declared dependencies found:
[WARNING] org.slf4j:slf4j-simple:jar:1.7.7:test
I can't use a runtime dependency because I don't want that dependency transitively inherited (e.g. so downstream dependencies can use log4j, etc. instead). I tried runtime with optional=true, but that results in the same warning.
(Note that I could also set ignoreNonCompile for the dependency plugin, but that seems like a very blunt instrument that would hide other potential problems.)
There is no scope that does exactly what you want here; test is the best available option.
A test-runtime scope has been requested before (Re: Need for a test-runtime scope?) and the suggested workaround is exactly the ignoreNonCompile configuration you've already discovered.
dependency:analyze already has some limitations ("some cases are not detected (constants, annotations with source-only retention, links in javadoc)"). You may have to accept that any test-scope dependencies that it warns against are false positives.
(You could split the definition of your tests into a separate module, which would have no slf4j implementation dependencies, then run them in another module. I don't think that would be worth it.)
There is no concept of test-runtime in maven. The only real downside is the dependency analysis identifying these runtime test dependencies as unused. Since they are only test dependencies, however, this is pretty benign and cannot cause issues to other projects transitively dependent on this project.
Since maven-dependency-plugin 2.10 (revision 1649454, Jan 2015), you can also add to the configuration a list of ignoredDependencies, ignoredUnusedDeclaredDependencies and ignoredUsedUndeclaredDependencies.
As a workaround i would suggest to have separate maven project with test cases that depend on main project
Related
I have a large Maven project with many modules and many pom.xml files. The project has changed and I suspect the pom's contain some unnecessary dependencies. Is there is a command which removes any unused dependencies from a pom?
The Maven Dependency Plugin will help, especially the dependency:analyze goal:
dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared.
Another thing that might help to do some cleanup is the Dependency Convergence report from the Maven Project Info Reports Plugin.
You can use dependency:analyze -DignoreNonCompile
This will print a list of used undeclared and unused declared dependencies (while ignoring runtime/provided/test/system scopes for unused dependency analysis.)
## Be careful while using this, some libraries used at runtime are considered unused
For more details refer to this link
As others have said, you can use the dependency:analyze goal to find which dependencies are used and declared, used and undeclared, or unused and declared. You may also find dependency:analyze-dep-mgt useful to look for mismatches in your dependencyManagement section.
You can simply remove unwanted direct dependencies from your POM, but if they are introduced by third-party jars, you can use the <exclusions> tags in a dependency to exclude the third-party jars (see the section titled Dependency Exclusions for details and some discussion). Here is an example excluding commons-logging from the Spring dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.5</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Have you looked at the Maven Dependency Plugin ? That won't remove stuff for you but has tools to allow you to do the analysis yourself. I'm thinking particularly of
mvn dependency:tree
I had similar kind of problem and decided to write a script that removes dependencies for me. Using that I got over half of the dependencies away rather easily.
http://samulisiivonen.blogspot.com/2012/01/cleanin-up-maven-dependencies.html
You can use DepClean https://github.com/castor-software/depclean/
DepClean is a tool to automatically remove dependencies that are included in your Java dependency tree but are not actually used in the project's code.
You can use dependency_cleaner https://github.com/junaidbs/dependency_cleaner
This jar will help to identify and remove unwanted dependency from pom.
It will automate the process of Removing a dependency and run then check whether the dependency needful
If you are using eclipse, right-click on the jar in Maven Dependencies:
Select Maven -> Exclude Maven Artifact...
On various projects I've been working on, I've seen diferent ways of specifying dependencies versions. On some projects, the package version is written on the same dependency declaration:
<dependency>
<groupId>org.apache.myfaces.extensions.validator.validation-modules</groupId>
<artifactId>myfaces-extval-property-validation</artifactId>
<version>2.0.7</version>
<scope>compile</scope>
</dependency>
On others, a property is used, as in:
<dependency>
<groupId>org.apache.myfaces.extensions.validator.validation-modules</groupId>
<artifactId>myfaces-extval-property-validation</artifactId>
<version>${versions.extval}</version>
<scope>compile</scope>
</dependency>
For multimodule projects, I can see a clear benefit in declaring versions on the parent pom to avoid duplication (and the potential confusion and errors that come with it), but on single module applications, would there be a benefit to use such a level of indirection?
What would be a best practice for this and why?
Thanks a lot :)
With a version property you can override it on the command line whereas with a fixed version you cannot.
So you can recompile your project with a newer version just by specifying it on the command line.
mvn -Dversions.extval=2.0.8 clean package
Or something.
apart from that, mostly used on multi-projects, and although there you have the dependency management section as well for versions.
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.
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.
I'm having the two dependencies javaee-api and hibernate-entitymanager in my pom. But they don't work very well together: as soon as I add javaee-api, all my unit tests break due to java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/validation/Validation. Without javaee-api, everything works fine. Why is that?
(this question has been edited in order to fit the problem ;))
Maven Dependencies have no order , but the provide the concept of scopes.
So what you need to do is, use the scopes to build the right set of dependencies for:
compile time
runtime in the server: (use for example provided for dependencies that are needed at compiletime, but will be provided by the server, so your Application does/must not contain them
test time: use the test scope to add dependencies that are only needed for testing (junit for example)
In your special case it looks like that javax.validation interface libary is not aviable in the tests. May they are not incuded in javaee-api. If this is the case, then add:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<scope>test</scope>
</dependency>
But be careful, your explanation, about what is included in the server, and the described behaviour sounds strange to me. - I recommend to double check what the server provides and what javaee-api relay includes. But may I am wrong, and javax.validation is only needed for test explicit
The reason why the problem only occoures when javaee-api is included, could be, that javax validation sometimes is only turned on, when a implementation is aviable in the classpath.
The order of dependency matter in some cases. The "problem" is that if a libary/dependency is referenced in two places (direct and inderect) and the version of both references to the same library differers, Maven has to decide which version to use.
The first and most important criterium is the depth of the reference in the dependency tree. If you reference the library directly in your project POM, then this will be dominate all other. If the library is refered directly by a libary that is refered direcly by you, then this will dominate all other where is one more indirection.
But in case where are two references (to the same library in different versions) in the same depth of the dependency tree, the first one will win. (more details).
First of all,
This is caused by the fact that the java-ee-api.jar contains crippled classes. There are alternative dependencies around which fix this problem. Changing the order in the pom.xml did the trick for me, too.
As far as I know, in Maven2 the order of the dependency is not kept and there is no guarantee on the order of how those dependencies appear in the final class path when you execute your code. In your case, I would "exclude" overlapping libraries in your dependency structure.
Look here for the Maven exclusion docs.
Given the complexity of library version matching I would suggest you start off with one of the Hibernate Maven examples, and morph it into your project.
I hope this helps.