Github Maven Plugins

Anyone hosting projects built with maven on Github will probably be interested in the Github Maven plugins

At the moment there are two plugins, one for uploading artifacts to the download section of your Github project, and another for uploading your maven site to the gh-pages branch of your repository.

You need to put something like this, or a bit cleverer, in your maven settings.xml:

<profiles>
    <profile>
      <id>github-properties</id>
      <properties>
        <github.global.userName>myusername</github.global.userName>
        <github.global.password>mypassword</github.global.password>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>github-properties</activeProfile>
  </activeProfiles>

site plugin

Then configure the plugins in your project. For the site plugin you need something like this, which will upload to github each tiem you run the site goal in maven.

<build>
   <plugins>
      <plugin>
         <groupId>com.github.github</groupId>
         <artifactId>site-maven-plugin</artifactId>
         <version>0.5</version>
         <executions>
            <execution>
               <goals>
                  <goal>site</goal>
               </goals>
               <phase>site</phase>
               <configuration>
                  <message>Creating site for ${project.version}</message>
               </configuration>
            </execution>
         </executions>
      </plugin>

multi-module projects

I’m not clear on how the multi-module support is supposed to work. The way I have got it to work is by using `path` and `merge` in the modules:

<configuration>
    <path>modulename</path>
    <merge>true</merge>
    ...
</configuration>

my theory is that if you build `site` in your parent project, then it will be uploaded first, clearing the whole site. Then each of the sub modules will be built merging each site into the parent site in the correct subdirectory. This seems to work, but the top level project run with `merge=false` wipes out you site for a while until the child modules complete. So if your project takes a while to build (or fails!) visitors might get disappointed. The only alternative to this seems to be to have `merge=true` on all the projects, but that will leave anything that doesn’t get overwritten unaffected. YMMV.

download plugin

You can have each of your releases uploaded to github for other people to download. I use a profile to only sign and upload my release builds and not every snapshot.

<profile>
    <id>release-sign-artifacts</id>
    <activation>
        <property>
            <name>performRelease</name>
            <value>true</value>
        </property>
    </activation>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <id>sign-artifacts</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>sign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
               <groupId>com.github.github</groupId>
               <artifactId>downloads-maven-plugin</artifactId>
               <version>0.4</version>
               <executions>
                  <execution>
                     <goals>
                        <goal>upload</goal>
                     </goals>
                     <phase>install</phase>
                     <configuration>
                        <description>${project.version} release of ${project.name}</       description>
                        <override>true</override>
                        <includeAttached>true</includeAttached>
                     </configuration>
                  </execution>
               </executions>
           </plugin>
        </plugins>
    </build>
</profile>

The more I use maven and github for Open Source development the better it gets, and this github integration is super-smooth. I have pretty much single click global publishing of my whole project source, documentation and artifacts. Combined with the super easy and free nexus and maven central access provided by sonatype OSS and the jenkins CI server instance provied by cloudbees this is as close to as good a dev environment as you can get – and all for no money. Oh yeah, and I forgot about the eclipse maven integration. We’ve come along way.

Embedding and Extending Apache Ant – testing properties

If you want to test a property from a custom task in Apache Ant 1.8, you can use the testUnlessCondition and testIfCondition in org.apache.tools.ant.PropertyHelper:

@Override
public void execute() throws BuildException
{
   final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper(getProject());
   if(propertyHelper.testUnlessCondition(requiredPropertyName))
   {
      // property is missing
   }
}

Convert URL to URI using lambdaj

import ch.lambdaj.function.convert.Converter;
 
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
 
public class ConvertUrlToUri implements Converter<URL, URI>
{
   public static ConvertUrlToUri convertUrlToUri()
   {
      return new ConvertUrlToUri();
   }
 
   @Override
   public URI convert(final URL from)
   {
      try
      {
         return from.toURI();
      }
      catch (final URISyntaxException e)
      {
         throw new RuntimeException("unable to convert " + from + " to URI", e);
      }
   }
}

Convert URL to filename using lambdaj

import ch.lambdaj.function.convert.Converter;
 
import java.net.URL;
 
public class ConvertUrlToFileName implements Converter<URL, String>
{
   public static ConvertUrlToFileName convertUrlToFileName()
   {
      return new ConvertUrlToFileName();
   }
 
   @Override
   public String convert(final URL from)
   {
      return from.getFile();
   }
}

Unzip with Apache VFS 2.0 (Java)

Apache VFS 2.0 is strange but super powerful. Uncompress a file at a given URI to a given output location:

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-vfs2</artifactId>
   <version>2.0</version>
</dependency>
import java.io.File;
import java.io.IOException;
import java.net.URI;
 
import org.apache.commons.vfs2.AllFileSelector;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
 
public class ZipfileUnpacker
{
   private final FileSystemManager fileSystemManager;
   private final URI packLocation;
 
   public ZipfileUnpacker(final URI packLocation) throws FileSystemException
   {
      this.fileSystemManager = VFS.getManager();
      this.packLocation = packLocation;
   }
 
   @Override
   public void unpack(final File outputDir) throws IOException
   {
      outputDir.mkdirs();
 
      final FileObject packFileObject = fileSystemManager.resolveFile(packLocation.toString());
      try
      {
         final FileObject zipFileSystem = fileSystemManager.createFileSystem(packFileObject);
         try
         {
            fileSystemManager.toFileObject(outputDir).copyFrom(zipFileSystem, new AllFileSelector());
         }
         finally
         {
            zipFileSystem.close();
         }
      }
      finally
      {
         packFileObject.close();
      }
   }
}

Hamcrest regexp matcher

Matches a string against a regular expression, inspired by Piotr Gabryanczyk with a couple of issues fixed.

import java.util.regex.Pattern;
 
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
 
/**
 * Matchers that use regular expressions
 *
 * @author t.wood
 */
public class RegexMatcher
{
   private static abstract class AbstractRegexpMatcher extends TypeSafeMatcher<String>
   {
      protected final String regex;
      protected final Pattern compiledRegex;
 
      private AbstractRegexpMatcher(final String regex)
      {
         this.regex = regex;
         compiledRegex = Pattern.compile(regex);
      }
   }
 
   private static class MatchesRegexpMatcher extends AbstractRegexpMatcher
   {
      private MatchesRegexpMatcher(final String regex)
      {
         super(regex);
      }
 
      @Override
      public boolean matchesSafely(final String item)
      {
         return compiledRegex.matcher(item).matches();
      }
 
      @Override
      public void describeTo(final Description description)
      {
         description.appendText("matches regex ").appendValue(regex);
      }
   }
 
   private static class ContainsMatchRegexpMatcher extends AbstractRegexpMatcher
   {
      private ContainsMatchRegexpMatcher(final String regex)
      {
         super(regex);
      }
 
      @Override
      public boolean matchesSafely(final String item)
      {
         return compiledRegex.matcher(item).find();
      }
 
      @Override
      public void describeTo(final Description description)
      {
         description.appendText("contains match for regex ").appendValue(regex);
      }
   }
 
   /**
    * Match the regexp against the whole input string
    *
    * @param regex the regular expression to match
    *
    * @return a matcher which matches the whole input string
    */
   public static Matcher<String> matches(final String regex)
   {
      return new MatchesRegexpMatcher(regex);
   }
 
   /**
    * Match the regexp against any substring of the input string
    *
    * @param regex the regular expression to match
    *
    * @return a matcher which matches anywhere in the input string
    */
   public static Matcher<String> containsMatch(final String regex)
   {
      return new ContainsMatchRegexpMatcher(regex);
   }
}

maven-enunciate-plugin java 7

It is possible to get the maven-enuniciate-plugin to work with Java 7. You might see an error like this:

[ERROR] Failed to execute goal org.codehaus.enunciate:maven-enunciate-plugin:1.24:docs (default) on project Pluto: Execution default of goal org.codehaus.enunciate:maven-enunciate-plugin:1.24:docs failed: A required class was missing while executing org.codehaus.enunciate:maven-enunciate-plugin:1.24:docs: com/sun/mirror/apt/AnnotationProcessorFactory

Looks like the tools.jar is not available in Java 7 by default. You need to add it as a dependency of the enunciate plugin. If you are using some other (non sun/oracle) JDK then the solution will be different, you may need to use maven profiles for each JDK you want to use.

You can add tools.jar as a dependency like this:

<plugin>
   <groupId>org.codehaus.enunciate</groupId>
   <artifactId>maven-enunciate-plugin</artifactId>
   <configuration>
      <configFile>src/main/doc/enunciate.xml</configFile>
   </configuration>
   <executions>
      <execution>
         <goals>
            <goal>docs</goal>
         </goals>
         <configuration>
            ...
         </configuration>
      </execution>
   </executions>
   <dependencies>
      <dependency>
         <groupId>com.sun</groupId>
         <artifactId>tools</artifactId>
         <version>${java.version}</version>
         <scope>system</scope>
         <systemPath>${java.home}/../lib/tools.jar</systemPath>
      </dependency>
   </dependencies>
</plugin>

You will now get a warning, which hopefully enunciate will fix sometime before java 8!

warning: The apt tool and its associated API are planned to be
removed in the next major JDK release.  These features have been
superseded by javac and the standardized annotation processing API,
javax.annotation.processing and javax.lang.model.  Users are
recommended to migrate to the annotation processing features of
javac; see the javac man page for more information.