Resources Blog What's in Maven 3.0 for Plugin Authors?

What's in Maven 3.0 for Plugin Authors?

While Maven 3 is a dramatic improvement over Maven 2 from the perspective of performance, extensibility, and architecture, most end-users are motivated by plugins. This has been true about Maven from the beginning, while the framework has value, it is the plugins that make the difference. This blog focuses on the changes that are of interest to plugin authors.

Accessing Project Dependencies

The easiest way for a plugin to get the entire set of transitive dependencies for a project is the mojo annotation @requiresDependencyResolution. This annotation is configured with one of the values: compile, runtime or test, which denote the dependency scope a plugin is interested in. What has been proven troublesome is the subtle fact that the runtime classpath is not a superset of the compile classpath, i.e. dependencies of scope provided and system are not included in the runtime classpath.

Now, what shall plugin authors do that need to inspect the union of the compile and runtime classpaths? So far, the answer was either to request the test classpath or to programmatically do dependency resolution with the proper scope filter. Neither of these two approaches are really attractive for one reason or the other. So we eventually added support for a new annotation value in Maven 3.0, compile+runtime, that ensures project dependencies with the scopes compile, runtime, provided and system get resolved.

Requiring Dependency Collection

While we are at dependency resolution, Maven 3.0 also introduces the new annotation @requiresDependencyCollection. Admittedly, the name of this new annotation differs only slightly from the existing @requiresDependencyResolution annotation but there's a rather important difference regarding the effect.

Dependency resolution consists of two phases. Phase one is determining the coordinates of all the transitive dependencies, phase two is getting the files for these artifacts which potentially involves downloading them. If one is only interested in phase one, we talk about dependency collection. In more detail, if a plugin goal declares @requiresDependencyCollection compile, it can inspect all transitive compile time dependencies of the project via MavenProject.getArtifacts(). But unlike @requiresDependencyResolution, the returned artifact instances might be unresolved, i.e. have no file associated with them.

It might seem odd to have unresolved artifacts but let me try to sketch the use cases that motivate this. While unresolved artifacts can not be used to create classpaths, they still allow a plugin to inspect the set of transitive dependencies, look for unwanted artifacts, check versions etc. If that reminds you of the Maven Release Plugin or the Maven Enforcer Plugin, great. Those plugins and others are usually invoked in very early lifecycle phases of the build, in particular, before any artifacts have been packaged or even any classes have been compiled. If those plugins try to resolve dependencies during a multi-module build with inter-project dependencies, they can't succeed, what hasn't been built yet cannot be resolved.

In Maven 2.x, there's a hack that tolerates resolution errors if all of the missing artifacts are in the reactor but it still leaves your build with an ugly warning and the plugin in question with unsatisfied requirements. @requiresDependencyCollection is the answer to this situation and will help to eventually fix some long-standing issues in the previously-mentioned plugins. So if your plugin doesn't need the actual files but only the coordinates of the project dependencies, consider to use the new annotation when targetting Maven 3.0 as a prerequisite.

Writing Thread-safe Maven Plugins

Related to the new support for parallel building in Maven 3.0, there's also a new mojo annotation @threadSafe. Right now, this annotation is nothing more than an informative suggestion. When Maven 3.0 is asked to perform a parallel build and encounters a plugin that isn't marked as thread-safe, it will print a prominent warning that your build might fail as historically, plugin authors assumed a sequential execution and, as such, didn't code with concurrency and thread safety in mind.

Unlike the previously mentioned extension to the Mojo API, this new annotation can be safely added to a plugin that targets Maven 2.x as runtime. So once you have reviewed your code and possibly adjusted it to support concurrent mojo execution, feel free to add this annotation to disable the build warning from Maven 3.0. In addition to disabling the warning, the generated plugin site will also indicate that the goal supports parallel builds.

Using Generics with Maven Plugins

Given that Maven 2.2.x already requires Java 1.5 to run, more and more plugins are moving to Java 1.5 as well. To enable the benefits of Java 1.5, the Maven API has been updated to use generics, thereby easing plugin coding. Furthermore, Maven 3.0 allows to use enums for plugin configuration parameters.

Fixes to the Classloader Hierarchy

The last change I want to mention is the reworked class loader hierarchy. Build extensions can now contribute additional APIs and components that other plugins can utilize during the build. The APIs and components contributed by extensions are shared across all projects using the same extension which allows plugins to easily exchange domain-specific data structures during the build, e.g. via MavenProject.getContextValue() or via some stateful singleton from the extension. If that sounds interesting to you, have a look at the wiki article Maven 3.x Class Loading which describes the revised class loading in more depth.

Picture of Benjamin Bentmann

Written by Benjamin Bentmann

Benjamin is a Software Developer at Sonatype, based in Germany. His specialties include Java, C++, MFC, and .NET.