Friday, December 11, 2009

New Apache.org artifact repositories for Maven you should be aware of

I recently tried creating a Maven core integration test using the archetype that the team kindly created. However when I tried to use it I got Unable to find resource 'org.apache.maven.its:maven-it-sample-archetype:jar:1.0-SNAPSHOT'.

Turns out this artifact does not live in http://people.apache.org/repo/m2-snapshot-repository - instead I've learned that Apache has new Maven repositories set up now that projects there are slowly adopting.

Wondering why I never heard about it before....

Anyways I added a proxy for https://repository.apache.org/content/groups/snapshots-group/ to my local Nexus install. Then I disabled my old proxy for http://people.apache.org/repo/m2-snapshot-repository since the snapshots-group includes the PAO repo. Finally I added the new repo to my 'All Repositories' and 'Public Snapshot Repositories' group in Nexus so that my Maven build will pick it up.




Here is some information I found out about the new repos.

http://www.apache.org/dev/repository-faq.html

https://issues.apache.org/jira/browse/INFRA-1896

The new Apache.org artifact repositories - use em' or else.

Thursday, December 10, 2009

Robustly enforcing a minimum Maven version for your project

Maven user's quickly realize that it is a good idea to force a fixed (or at least minimum) version of Maven to be used to build their Maven project. Initially one may gravitate towards using the following pom snippet:

<prerequisites>
  <maven>2.0.10</maven>
</prerequisites>

Looks good, except there are a couple gotchas
1. Typos. If you make one then you will get no warning or complaints from maven. You might as well have this.

<prerequisites>
  <maven>Maven treats this string as older than any Maven version you might use</maven>
</prerequisites>

A bug is even more likely if you do this:

<prerequisites>
  <maven>${maven.min-version}</maven>
</prerequisites>

Let's say that property can't be resolved for whatever reason - typos? - again Maven will treat that 'version' as older than any valid Maven version you could be executing the POM with. This is actually not a bug but by design since Maven does not force project's ( in this case Maven itself) to a strict version number specification.

2. Another problem is that section is not even used at all by the next major version of Maven 3.x as of today. So even if you did have it right, it is not future proof.

3. Finally maven considers versions like "1.0-alpha-2" to be newer than "1.0-alpha-15" as of Maven 2.x series at least. A bug indeed in the version compare logic - the failing test cases being commented out in the source code. Although Maven 3.x ignores prerequisites in pom, at least the version comparison noted here is fixed.

4. Lastly the <maven> element does not handle version ranges. So when it sees [2.0.6,2.0.8], we are just back to problem 1.

So what to do?

Use the maven-enforcer-plugin instead. The benefits are:

1. Many different rules supported, not just Maven version.
2. Write your own rules.
3. Version ranges are supported.
4. Version comparisons are more robust.
5. Bind the version check to Maven lifecycle phases or profiles.

Below is an example configuration for the plugin to require a minimum Maven version 2.0.9 and above. The unique thing about this example is that there are three executions, one for each pre-defined Maven build lifecycle. Usually you just see the first execution only, which is bound to the validate phase of the default lifecycle. However if one were to do
mvn clean
or
mvn site
then the version validation would not be enforced since those goals do not have a validate phase. Having all three executions covers all the bases.

The other best practice is define a property to hold the Maven versions so that it can be changed in one place only or manipulated on the cmd line.

<properties>
  <maven.min-version>2.0.9</maven.min-version>
</properties>

<plugin>
  <artifactId>maven-enforcer-plugin</artifactId>
  <executions>
    <execution>
      <id>enforce-default</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
    <execution>
      <id>enforce-clean</id>
      <phase>pre-clean</phase>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
    <execution>
      <id>enforce-site</id>
      <phase>pre-site</phase>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>[${maven.min-version},)</version>
          </requireMavenVersion>
        </rules>
      </configuration>
    </execution>
  </executions>
</plugin>