5

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.

Nikolay
  • 1,111
  • 2
  • 12
  • 16

4 Answers4

6

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.

rec
  • 10,340
  • 3
  • 29
  • 43
  • Cool feature :) Still don't understand, why maven allows compilation with indirect dependencies. Why someone will use classes from indirectly resolved libraries, when most of the times you don't know and you don't need to know about this libraries. They are used internally by the module you are referencing and are only necessary when you build the final runnable module. – Nikolay Oct 15 '13 at 14:55
  • It's not a bug, it's a feature. Actually, the transitive dependency feature is one of the main reasons for many people to use Maven (or Ivy, or Groove Grapes, etc.). Btw. did you try using the correct scopes, e.g. the scope "provided" for dependencies that you only need during compilation and later at runtime, but which are not needed otherwise? – rec Oct 15 '13 at 15:45
  • There are a lot of cases in which the compiler needs the transitive dependency even if there is no code referencing it. For example, if in module 1 you have class A and in module 2 you have class B which extends class A, any code that references class B also needs to have class A on the build path to compile (and hence module 1). It would thus be very complicated to predict which transitive dependency is unnecessary. – Didier L Apr 10 '14 at 13:59
2

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.

Ben Thurley
  • 6,943
  • 4
  • 31
  • 54
1

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.

Community
  • 1
  • 1
Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
0

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.

Community
  • 1
  • 1
wemu
  • 7,952
  • 4
  • 30
  • 59
  • Thanks for reply: **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.** - Imagine that module-1 is a core framework module. My module uses it, and module-2 also uses it. – Nikolay Oct 15 '13 at 14:56
  • Then both modules need to define it as a dependency. (This is often forgotten, breaking your module when `module-2` ever not depends on `module-1` anymore; `dependency:analyze` can help with this.) But this is how it is done... enough people chiming in here, hopefully, to make you believe that it is the right way. There's many ways Maven lets itself being configured one way or another, this is not one of them. If you don't want this, Maven may not be the tool for you. Then, if you really insist, `dependency:copy` may get you going, but then you entirely abandon Maven's dependency mechanism. – Sander Verhagen Oct 15 '13 at 16:14