To Mercury and Beyond


November 10, 2008 By shane

Last month, I went down to Sonatype’s offices to meet up with the guys down there. I soon found myself being cornered by a serious looking Oleg, who wanted Maven integration with Mercury ASAP, and who I believed I promised Maven integration two weeks prior. We setup our laptops outside to work on Mercury, in what later proved to be a mistake as the burning California sun beat down on us.

Oleg needed a version of project builder that didn’t require a bunch of dependencies from the Maven trunk, as he designed Mercury to be a standalone module, resolving of any type of artifact, not just Maven specific ones. The bulk of the project builder work was already standalone and isolated into maven-shared-model, a component that handles inheritance and interpolation of generic models (including XML ones). So I stripped out some of the interfaces and a few classes from the maven-project component, which had unneeded tie-ins with the rest of Maven core.

Next came the implementation. Mercury has an interface called DependencyProcessor, with the following method that I needed to implement (MavenDependencyProcessor):

public List getDependencies(ArtifactBasicMetadata bmd, MetadataReader mdReader)

The point of this method is to build a full project model, including inheritance, interpolation and profiles, returning the list of ArtifactBasicMetadata dependencies of the processed model to Mercury.

The ArtifactBasicMetadata is a simple value class, giving the MavenDependencyProcessor instance information like version, groupId, artifactId of the artifact for which to process the returned dependency list. The MetadataReader allows the MavenDependencyProcessor instance a way to call back into Mercury to get the parent of the project.

Since the project builder uses basic property lists to store all Maven properties, converting from dependencies to ArtifactBasicMetadata couldn’t be simpler: The core of the conversion is:

List modelProperties = container.getProperties();
ArtifactBasicMetadata metadata = new ArtifactBasicMetadata();
for ( ModelProperty mp : modelProperties )
{
  if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.groupId)) {
       metadata.setGroupId(mp.getValue());
  } else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.artifactId)) {
       metadata.setArtifactId(mp.getValue());
  }  else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.version)) {
       metadata.setVersion(mp.getValue());
  } else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.classifier)) {
       metadata.setClassifier(mp.getValue());
  } else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.scope)) {
       metadata.setScope(mp.getValue());
  } else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.type)) {
       metadata.setType(mp.getValue());
  } else if(mp.getUri().equals(ProjectUri.Dependencies.Dependency.optional)) {
       metadata.setOptional(mp.getValue());
  }
}

One problem we did encounter was how to handle passing in system and user properties for interpolation and profile activation to the MavenDependencyProcessor, as these properties are currently passed by the MavenEmbedder to the project builder. This requires that Maven core explicitly initialize the MavenDependencyProcessor, passing in all properties through the MavenDependencyProcessor constructor.

Mercury integration is pretty exciting because it will allow us to hemorrhage a lot of resolving code which we have to use in the core, as well as providing a deterministic resolver. Full Mercury integration is not slated for the Maven-3.0-alpha1 release and will likely be a core focus of the alpha-2 release. Initial integration looks promising, as Mercury is already able to process classpaths and dependencies from POMs.