tags:

views:

1861

answers:

4

Say I have a Maven dependency defined in a project like below.

    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.3.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mycompany.library</groupId>
        <artifactId>mylibrary</artifactId>
        <version>1.0.1</version>
        <scope>compile</scope>
    </dependency>

Then, within mylibrary, I also have a dependency defined as below.

    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.3.1</version>
        <scope>compile</scope>
    </dependency>

When I package my project, I don't see xstream packaged within it. I think the project's xstream dependency scope, 'test' is overriding the mylibrary's xstream dependency scope, 'compile'.

In this kind of situation, what's the best way to include the xstream for the whole project so the submodule can have access to it when packaged within the project?

I've read Apache Maven website's explanation on Transitive dependencies, but I'm struggling to understand what it means, and also to find out the best practice in this situation.

A: 

If you want it packaged, why are you declaring scope? If it is required at compile and execution time, shouldn't you leave the scope blank? If you did that, then you would only need

<dependency>
    <groupId>mycompany.modules</groupId>
    <artifactId>submodule</artifactId>
    <version>1.0.1</version>
</dependency>

in your pom. Unless there is a reason to descope it during compile but not during packaging?

aperkins
from the Apache Maven website:- compileThis is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.
tim_wonil
Maybe my original question caused confusion because I used 'submodule'. Please see my edited question above.
tim_wonil
Yes, I was more wondering about the test scope.
aperkins
+2  A: 

By declaring your own dependency on xstream, and setting the scope to test, you are overriding the dependencies declared by mylibrary.

This is actually a Maven feature - it allows you to do things such as depend on a later version of a transitive dependency within your own project, and not end up packaging two different versions of the same artifact. For example, you might depend on version 1.2.15 of log4j, but because you also use libraryX which depends on log4j-1.2.14 - you wouldn't want both log4j-1.2.15 and log4j-1.2.14 to be packaged with your project.

If you actually want xstream to be packaged within your project, you should not be declaring the scope as test. In fact if you remove your listed dependency on xstream, things will work out as you like, since mylibrary has a compile dependency on it..

matt b
In that case, if some time in the future, I don't need to use mylibrary or that library changes its dependency on xstream to something else, my project will fail to pass the tests. Then, I'll need to specify xstream for project's test scope again. Is this Maven feature/limitation? I'm willing to accept limitations of Maven, just trying to see what the best practice is.
tim_wonil
I'm not certain this is correct. There is a more intutive mechanism for excluding unwanted transitive dependencies, and that's the <exclusions> block.
Buhb
"if some time in the future, I don't need to use mylibrary or that library changes its dependency on xstream to something else, my project will fail to pass the tests." then at that point simply add the dependency back. As long as you continue to rely on the same version of mylibrary, no unexpected changes should happen. New versions of your project might have different needs - which is totally normal.
matt b
+3  A: 

This feels really odd to me, and if it's "feature", I think it is a really dangerous one. Anyway, it's not a Maven bug and it's in the maven documentation here.

Regarding best practices on this issue, I haven't heard of any, but the safest way to proceed ought to be to entirely remove xstream from your pom, relying on the transitive dependency. Doing this will result in a build failure if the dependency to mylibrary is removed. This will act as a notification to you that you need to fix something. You won't silently loose required dependencies, and you won't silently have dependencies you no longer need.

On a side note, mvn dependency:analyze can be used to check for dependencies that are included but not used.

Buhb
+4  A: 

As mattb's answer says, declaring the dependency as test scope overrides the transitive compile-scoped dependency declaration, and as a result the dependency is not included in your packaged war.

If you only need the dependency in your tests because 'mylibrary' needs it to execute, you shouldn't declare the dependency at all in your project's pom. Let the transitive dependency resolution process handle it.

If your project does use the xstream jar directly, you can still rely on the transitive dependency, as you will need a compatible version for your project and 'mylibrary' to both run against the xstream jar. You should have unit tests that exercise the functionality, and if mylibrary changes version of xstream to an incompatible version, your builds should fail, and you can address the issue at that point.

In general I'd say you should try to avoid declaring dependency versions directly in multi-module projects. I declare the versions in a dependencyManagement section of a parent POM so that the child need only declare the groupId/artifactId. Alternatively, from Maven 2.0.9 onwards there is an additional dependency scope of import:

This scope is only used on a dependency of type pom in the section. It indicates that the specified POM should be replaced with the dependencies in that POM's section. Since they are replaced, dependencies with a scope of import do not actually participate in limiting the transitivity of a dependency.

So using import scope you can define your common dependency versions in a single POM, import the dependencies of that POM into your dependencyManagement section, and just declare the groupId/artifactId of the dependency in your other POMs.

Rich Seller