Archive | August 2013

Developing your own maven plugin to verify the bytecode of your artifact

In this article the goal is to develop your own maven plugin that accesses at build time the artifact of your project and verifies the class files. I used this concept for my library jb5n to verify that for each MessageResource interface an appropriate key/value pair is available in the underlying resource bundle.
The first step is of course to create a new maven module or project using the mojo archetype:

mvn archetype:generate \
  -DgroupId=sample.plugin \
  -DartifactId=hello-maven-plugin \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-plugin

This will create a ready-to-run maven project of type maven-plugin for you:

  <groupId>sample.plugin</groupId>
  <artifactId>hello-maven-plugin</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>maven-plugin</packaging>

All necessary dependencies to develop your own maven plugin are already available. The archetype also creates a simple Mojo class:

/**
 * @goal verify
 * @phase verify
 */
public class MyMojo extends AbstractMojo {

    /**
     * @parameter default-value="${project}"
     */
    private MavenProject mavenProject;

The maven goal as well as the default phase of the lifecycle the plugin runs in is given by the javadoc elements @goal and @phase. Newer versions of maven let you define these values with annotations. To access the outcome of the current build process we let our plugin run in the verify phase. We can access the file of our artifact by accessing the member variable mavenProject, which is injected by maven into our plugin with the above definition:

File artifactFile = mavenProject.getArtifact().getFile();

The File object from the above snippet will point to the jar file (in case we have chosen jar packaging for our artifact). Therefore we can open the jar file using Java’s SDK classes JarFile and JarEntry:

JarFile jarFile = new JarFile(artifactFile);
Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) {
	JarEntry jarEntry = entries.nextElement();
	String jarEntryName = jarEntry.getName();
	if(jarEntryName != null && jarEntryName.endsWith(".class")) {
		getLog().debug(String.format("Processing jar file entry '%s'.", jarEntryName));
		...
	}
}

Now we can use a library like javassist or Java’s reflection API to analyze each class file within the artifact.

Compiler aware access to properties files in Java

The standard way in the Java world to provide internationalization to your application is the usage of properties files. You create a simple text file where you store your key/value pairs. Access to these files is normally done through the JDK class ResourceBundle:

ResourceBundle myResources = ResourceBundle.getBundle("MyResources", currentLocale);
myResources.getString("OkKey");

So far, so good. But what if your project grows? How do you keep track of the link between the Java code, i.e. means all the lines that access a particular key, and the properties files? You have to find a way to cope with this, because otherwise it becomes difficult to answer questions like: “Can I remove this key/value pair from my properties file or is it still referenced by some Java code?”. Or what if you want to rename a key? You’ll have to find all occurrences of this string in the Java code. Hopefully you will find all, otherwise ResourceBundle will throw a MissingResourceException.
A simple concept to overcome this problem is to route all access to your properties files to Java interfaces. Let a proxy implementation of this interface fetch the required key from the properties file. This way your IDE can help you to find all lines in your Java code where a certain property is accessed:

MyMessageResource myMessageResource = JB5n.createInstance(MyMessageResource.class);
String ok = myMessageResource.ok();

The Google Web Toolkit (GWT) has introduced this mechanism as Messages. As I needed this kind of functionality quite often, I have implemented a library that implements this idea. In contrast to other implementations I wanted to be backward compatible, so that you can upgrade an existing application step by step. For this I have added an annotation to the interface methods that lets you define the key to use to access the properties file. Normally the name of the method is used as key.

@Message(key = "no.default.key")
String noDefaultKey();

Beyond that the library should also stay extensible. If your requirements change and your customer wants to be able to change the translations without the need of recompilation, you could easily implement your own InvocationHandler that loads the messages e.g. from a database or some other kind of storage:

@MessageResource(invocationHandler=MyDatabaseMessageResource.class)
private interface MyInvocationHandler {
	String ok();
}

Like Google’s GWT my implementation can of course also handle arguments to the message, using Java’s MessageFormat:

public interface MyMessageResource {
	String youHaveNREtries(int numberOfRetries); // "You have {0} retries."
}

And last but not least the jb5n library also allows you to use inheritance to group the messages/translations and distribute the methods over different interfaces and therewith the messages over different files:

public interface MyMessageResource {
	String ok();
}
public interface MySpecificMessageResource extends MyMessageResource {
	String specificMessage();
}

But the library is not yet finished. A maven plugin as well as an ant task are on my todo list. This way you can check that interface and properties file are in sync during the build process.
The source code can be found on github: https://github.com/siom79/jb5n.