General Framework For Model Inheritance
Maven 3.0 uses a new standalone component that handles inheritance and interpolation of a model in any format. The model needn’t even be XML based. If you can translate your model into a list of property-value pairs, you can use this framework for inheritance.
ModelProperties are the building blocks and consist of a URI and a value. If you want to create a hierarchy (or a node), you would specify something like:
mp("http://myorg.org/model/foo", null) mp("http://myorg.org/model/foo/bar", "fooey")
In XML, this would correspond to:
<foo>
<bar>fooey</bar>
</foo>
If two of the same properties are stacked, say “http://myorg.org/model/foo/bar”, then the top most property value will be used. This allows for a simple linearized inheritance, where the most specialized model overrides values of its parent model. But what happens to collections, like dependencies, that need to be merged as we move up the hierarchy of parent poms?
That’s where the concept of the ModelContainer comes in. The first thing to do is to specify #collection in the URI: http://apache.org/maven/project/dependencies#collection.
This tells the ModelTransformer implementation instance not to override the property, as it is part of a collection. Instead, the framework just to adds the property to the general collection. Obviously, the framework doesn’t know anything about how to handle the specifics of these URI collections, as that is a model specific rule. Thus the framework must delegate the decision, calling all the ModelContainerFactories that is has been initialized with, asking each if they can provide a container to handle the property.
ModelDataSource source = new DefaultModelDataSource(); source.init( props, Arrays.asList( new ArtifactModelContainerFactory(), new IdModelContainerFactory() ) );
A ModelContainer implementation is rather simple. The framework just passes in all like model containers and asks what it should do. A ModelContainer is required to implement the following method:
ModelContainerAction containerAction( ModelContainer modelContainer );
A ModelContainer implementation instance will know its own model properties (say version, groupId and artifactId) and decides what action to take in relation to the specified modelContainer, which is passed to it by the framework. The returned ModelContainerAction is an enum that specifies an action: no operation, delete or join of containers.
For example, if either the groupIds or versions are different, the container would return a NOP, telling maven-shared-model to leave the specified container in the collection. If the artifactIds, versions and groupIds are all the same, then the container would tell the framework to join the containers. That’s where the implementation of ModelDataSource comes in. It has join, delete and query methods for handling the operations on ModelContainers. If the ModelContainer implementation told the framework to join model containers, the framework would then take both model containers and invoke: ModelDataSource.join(ModelContainer a, ModelContainer b)
Out of the box, you can use the ModelTransformer for transforming XML to a list of model properties, but nothing would stop someone from plugging in their own transformer for virtually any other format. Thus you can used maven-shared-model for inheriting any data format according to a general rule set that you can customize.
Clearly the component name, maven-shared-model, is not appropriate, as it can be used independently of Maven and does not dictate the use of the Maven model. After the first alpha release of Maven 3.0, I’ll be creating a separate project, independent of trunk, for maven-shared-model.
MySQL's Zack Urlocker writes about Sonatype's new CEO in InfoWorld
There’s are lots of people who already know that Mark de Visser is the new CEO of Sonatype, but Zack wrote a nice bit about it today in his InfoWorld blog:
http://weblog.infoworld.com/openresource/archives/2008/11/marketing_maven.html
I am really excited to be working with Mark. More good news to come!
To Mercury and Beyond
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.
MASA: Building Android Applications with Maven
When I first downloaded the SDK for Android nearly a year ago, I was greeted by an ant build file. I wanted to start developing Android applications but wasn’t willing to go back to ant to do it; so I decided to throw together a quick set of plugins to build Android with Maven: MASA Plugins. The name MASA comes from PKD’s book We Can Build You. In the book, there is a company MASA Associates (Multiplex Acoustical System of America), responsible for building simulacra (or androids). PKD’s MASA is also a play on the word NASA, and as we all know, Maven is as simple as rocket science.
After starting, I found there was little documentation on how to use the command line to build Android apps, leading me to dissect the build.xml file to learn how the command line options worked.
After that, it was just a matter of creating a custom lifecycle for the packaging type ‘android:apk’. The plugins largely consisted of translating POM parameters to the command line, similar to Apache NMaven, undergoing incubation at the ASF.
A sample POM looks like:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jvending.masa</groupId>
<artifactId>maven-test</artifactId>
<version>1.0.1</version>
<packaging>android:apk</packaging>
<name>maven-test</name>
<dependencies>
<dependency>
<groupId>android</groupId>
<artifactId>android</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.jvending.masa.plugins</groupId>
<artifactId>maven-dx-plugin</artifactId>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>
So by specifying the packaging type as ‘android:apk’ and the sourceDirectory as ‘src’, you can build your android project with Maven. If you type ‘mvn install -Dmasa.debug’ the plugins will also deploy the apk to the G1 device over USB or to the emulator.
For the latest SDK, I included the packaging type: ‘android:apk:platformTest’ that will deploy the apk to the device and run the unit tests on the target device.
At the time, the Android source was not open-source, so MASA provides a build script for importing the android.jar into the local repo. Now that Android is open-source, I’ll be looking to deploy the android.jar into the Maven repo to avoid this additional step and then getting a release of MASA deployed as well.
One area that I still need to work on is the delayed signing of apks, so expect to see future updates.