Search code examples
mavendependency-management

How can I depend on a library with transitive dependencies which are adjusted by Maven dependency management, at the same versions as adjusted?


Consider the following project POM for a Java library:

<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>foo</groupId>
  <artifactId>lib</artifactId>
  <version>1</version>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.10</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.3</version>
    </dependency>
  </dependencies>
</project>

As desired, this project depends on commons-codec version 1.10:

$ mvn dependency:list | grep commons-codec
[INFO]    commons-codec:commons-codec:jar:1.10:compile

However, if this library is used as a dependency in a downstream project, the version of commons-codec inherited transitively will be 1.9, because org.apache.httpcomponents:httpclient:4.5.3 depends on 1.9, not 1.10.

Here is an application POM which illustrates the issue:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>foo</groupId>
  <artifactId>app</artifactId>
  <version>1</version>

  <dependencies>
    <dependency>
      <groupId>foo</groupId>
      <artifactId>lib</artifactId>
      <version>1</version>
    </dependency>
  </dependencies>
</project>

And the resolved dependencies:

mvn dependency:list | grep commons-codec
[INFO]    commons-codec:commons-codec:jar:1.9:compile

Is this a bug in Maven? Or intended behavior?

If intended, is there a way—without hardcoding transitive dependencies in either POM—to inherit the versions as they are resolved during the build of the library component?


Solution

  • One solution is to inherit the dependency management of the library, like so:

    <?xml version="1.0" encoding="UTF-8"?>
    <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/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>foo</groupId>
      <artifactId>app</artifactId>
      <version>1</version>
    
      <dependencyManagement>
        <dependencies>
          <dependency>
            <groupId>foo</groupId>
            <artifactId>lib</artifactId>
            <version>1</version>
            <type>pom</type>
            <scope>import</scope>
          </dependency>
        </dependencies>
      </dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>foo</groupId>
          <artifactId>lib</artifactId>
          <version>1</version>
        </dependency>
      </dependencies>
    </project>
    

    And then it works:

    mvn dependency:list | grep commons-codec
    [INFO]    commons-codec:commons-codec:jar:1.10:compile
    

    But is this the best/only solution? It seems unfortunate/unintended if one must always do this with every dependency to ensure that transitive versions actually match.

    I have also posted this code as a gist.