Using Aether to resolve dependencies in a Maven plugin

When we upgraded to the latest version of Maven (3.0.4), some of our in-house plugins had issues with dependency resolution.

We already has similar problems when migrating from Maven 2 to Maven 3, due to the new library used by Maven to access the repository system: Aether. These problems only came when using plugins that had to resolve dependencies by themselves.

Running these plugins with Maven 2 require the use of the org.apache.maven.artifact.resolver package (ArtifactResolver and ArtifactCollector classes, a. o.). Although still valid when running the plugin with Maven 3, this can lead to some side effects we experienced, such as:

  • Dependencies explicitly marked as excluded still being resolved
  • Dependencies taken from a Maven 1 repository being unresolvable because coordinates stated as null:null:jar (seems specific to Maven 3.0.4+)
  • Inconsistencies with the dependency mechanism used internally by Maven (see here)

So we had to update our plugins to use the new Aether library to perform dependency resolution, and found a good starting point on the Sonatype blog.

Let’s take one sample method that would need to be updated. For instance, the following method is responsible of resolving all dependencies of a project:

This is quite a simple process, as all needed components (project, resolver, localRepository, remoteRepositories and artifactMetadataSource) can be injected by Plexus, either by an expression or a component “annotation”:

As already said, this code can still be executed with Maven 3, thanks to the great job the Maven guys did in making it globally backward compatible. But this sometimes fails to do the job, and this is not the way we should write new plugins!

The Sonatype blog post is quite nice, but using it as is would mix a lot of the same concepts. Yes, part of the classes you manipulate to handle dependency resolution are both in Aether and Maven API, notably Maven Artifact and Aether Artifact. A plugin will usually work on the Maven Artifact side, so we rather want to use the Maven packages as much as possible, with ideally no explicit reference about the underlying library that Maven uses. That would also minimize the modifications needed inside the plugin when upgrading to Maven 3.

Unfortunately, there’s no way to achieve dependency resolution without at least one import of an Aether class, but still this is valuable as it is limited to the dependency resolution process, not the dependencies or artifact structures, which is still nice to reduce the impact on the business part of the plugin work.

Here is the method modified to be compliant with Maven 3.0.4 and Aether 1.13.1

The only Aether class that we still have to use is RepositorySystemSession, all other are core Maven ones, especially the Artifact one. Also note that you do not have to depend on Aether explicitly, as it will be taken transitively from the org.apache.maven:maven-core:jar:3.0.4 dependency.

As for the previous implementation, the required parameters can be obtained with Plexus injections:

With an isolation of dependency resolution inside specific methods that will not have their return type changed, a plugin will easily be updated to support Maven 3 dependency resolution mechanism.
One drawback is the use of RepositoryUtils.toArtifacts, which is marked by Maven as intended to be used internally.

Similarly, resolving one artifact can be done like this:

This code does not depend directly on any Aether class.

Using the above samples, plugins can easily be migrated to fully comply with the new Maven 3 dependency resolution library.

7 comments to Using Aether to resolve dependencies in a Maven plugin

  • Will

    execuse me, I’m a beginner who’s developing my first Maven plugin. according to your code, you used the ProjectDependenciesResolver which belongs to package org.apache.maven.project, i couldn’t help but noticed, there is another ProjectDependenciesResolver in org.apache.maven. what’s more, it can do transitively resolving too! I tried to use it in my test code, it worked good without any Aether class, and without the drawback about RepositoryUtils.toArtifacts. so, my question is, why not use this org.apache.maven.ProjectDependenciesResolver? what’s internal difference between the two solutions?

  • Thomas

    Will, you’re right about that org.apache.maven.ProjectDependenciesResolver, but that one is still using the old way of resolving dependencies (pre-Aether).
    That will lead to the side effects (mentioned in the beginning of this post) when using your plugin under Maven 3 :
    * Exception thrown about {null:null:null:jar} when dealing with a pure Maven1 dependency in your project tree
    * Dependency exclusions not being correctly used
    * Differences between the dependency tree obtained by your plugin and the one effectively used by Maven 3

  • How do I find out the true dependency resolution for maven-3. I’m running into an issue where “exec:exec” creates different classpath then “dependency:tree”.

    • Thomas

      dependency:tree should be the correct way to get the real project dependencies. But you will have to be sure you use at least the version 2.5 of the plugin.

      exec:exec runs the Maven exec plugin. The classpath used can vary, parameters like classpathScope can modify the classpath that is used by the plugin.

  • Mores

    org.apache.maven:maven-core:jar:3.0.5 depends on org.sonatype.aether
    org.apache.maven:maven-core:jar:3.1.1 depends on org.eclipse.aether

    Have you found an elegant way around this dependency issue so that when building a Maven plugin it will work for both 3.0.X and 3.1.X environments ?

    • Thomas

      Hi Mores,
      We didn’t upgrade to 3.1.x yet, so maybe my answer will not be complete.
      As Maven 3.0.5 do not provide the complete layer above Aether (hence the mention I made about being forced to import 1 Aether Class), it will be extremely difficult to provide a plugin working for both 3.0.x and 3.1.x, unless Maven automatically “translate” the import (which is something they already made for making most of Maven 2 things compatible with Maven 3).

      But if your plugin directly depends on a specific version Aether, instead of using the one provided by Maven, maybe this will not be an issue. You directly use Aether in your plugin after all, and it should be able to pull dependencies, whatever Aether version Maven uses.

  • Ben

    Thanks for this article. It was very useful to resolve all project dependecies in my maven plugin.

Leave a Reply

  

  

  


× one = 3

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">