June 23, 2009

Maven: How Relocated Artifacts Can Ruin Your Day

Wow, this is one of those days I struggle with issues all the time without actually going forward one little bit on my original task... I guess you all know this feeling :o(
Now, one of the things that I wasted quite some time on is excluding a dependency. One of our modules is defining a dependency to Apache DBCP framework like this:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
</dependency>
This dependency is transitively depending on some XML artifacts, which you can see by using the maven-dependency-plugin's tree goal:
...
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.1:compile
[INFO]   +- commons-pool:commons-pool:jar:1.2:compile
[INFO]   +- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO]   \- xerces:xercesImpl:jar:2.0.2:compile
...
Well, the xml-apis and xerces artifacts are a bit outdated and actually we didn't want them at all in our war files, so I decided to exclude them using the dependency exclusion feature:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xercesImpl</artifactId>
    </exclusion>
  </exclusions>
</dependency>
What do you think will be the result? Surprisingly, xml-apis:xml-apis vanished, but xerces:xercesImpl didn't. The new dependency tree is the evidence:
...
[INFO] +- commons-dbcp:commons-dbcp:jar:1.2.1:compile
[INFO]   +- commons-pool:commons-pool:jar:1.2:compile
[INFO]   \- xerces:xercesImpl:jar:2.0.2:compile
...
That's strange, isn't it? It took me an hour or so playing around to find the reason for this behaviour: Xerces has been relocated with respect to its Maven coordinates... The only hint Maven is giving you is this message when executing in debug mode:
[DEBUG] While downloading xerces:xerces:2.0.2
  This artifact has been relocated to xerces:xercesImpl:2.0.2.
What does that mean? Well, you may relocate artifacts in the Maven repository when the group or artifact id should change (for more details, see the Guide to Relocation). Originally, the DBCP artifact specified a dependency to xerces:xerces in its dependencies – which you can see in the artifact's POM file (residing in your local Maven cache, for instance):
<dependency>
  <groupId>xerces</groupId>
  <artifactId>xerces</artifactId>
  <version>2.0.2</version>
</dependency>
However, xerces:xerces has been relocated to xerces:xercesImpl and these new coordinates are what is shown in the dependency tree. But, when excluding a dependency, you must specify the original transitive dependency (as defined by the artifact), not the new coordinates:
<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
  <exclusions>
    <exclusion>
      <groupId>xml-apis</groupId>
      <artifactId>xml-apis</artifactId>
    </exclusion>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xerces</artifactId>
    </exclusion>
  </exclusions>
</dependency>
How intuitive is that???
Maven should really do better, either by indicating the original coordinates when showing the dependency tree/list, or better yet by issuing a warning when trying to exclude a relocated dependency. I guess the latter one is not easy to implement, but current solution is just annoying!

3 comments:

  1. This is really good stuff to know. That's wasnt the exact issue I was researching but it's a good point to keep in mind and thanks for sharing it.

    ReplyDelete
  2. Hello! I think you've been wrong with the exclusions. See this:

    xerces <--- This should be xercesImpl
    xercesImpl <--- This should be xerces


    Greetings!

    ReplyDelete
    Replies
    1. Hi Adrian,
      of course you're right, I've corrected that. Thanks for careful reading ;-)

      Delete