Maven and JSR-269 Annotation Processors

The maven-compiler-plugin (as of version 2.1) has not yet been enhanced to cope with some of the changes to the javac compiler introduced as part of JSR-269. One particular snafu I encountered recently is described in this bug report.

To repeat what is said in the bug report: With JSR 269, annotation processors are discovered by searching the classpath for a well known file (META-INF/services/javax.annotation.processing.Processor). The contents of this file enumerate available annotation processors (in other words this file is a provider-configuration file).

In a Maven project the logical placement of this file is the src/main/resources folder. With the default maven lifecycle the contents of this folder are copied to the output folder (which forms part of the classpath searched by the compiler for annotation processors) before compilation takes place. This causes the compiler to expect a compiled instance of annotation processors enumerated in the file to be available at compilation time. If the compiler fails to find the processors it will raise an error, causing the build to abort. Of course in the project where the annotation processors are defined this causes a chicken and egg problem. The processors need to be first compiled by the compiler but the compiler expects them to already exist.

The workaround is to modify the maven build lifecycle to compile the project in two passes. The first pass compiles just the annotation processors (with annotation processing disabled), the second compiles the rest of the project (with annotation processing enabled). The first pass makes the compiled annotation processors available for the second pass.

Below is the relevant excerpt from pom.xml:

...
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>2.1</version>
 <configuration>
  <source>1.6</source>
  <target>1.6</target>
 </configuration>
 <executions>
  <execution>
   <id>default-compile</id>
   <configuration>
    <compilerArgument>-proc:none</compilerArgument>
    <includes>
     <include>path/to/annotation/processor/**</include>
     <include>path/to/annotation/processor/dependencies/**</include>
    </includes>
   </configuration>
  </execution>
  <execution>
   <id>compile-everything-else</id>
   <phase>compile</phase>
   <goals>
    <goal>compile</goal>
   </goals>
  </execution>
 </executions>
</plugin>
...

The above configures the maven build as follows:

  • Overrides the default-compile execution to disable annotation processing (-proc:none) and to only compile the source files required to generate the annotation processors.
  • Add another compiler execution to compile all other source files in the project in the default manner (meaning annotation processing will be enabled).
  • Note that it is important to compile just the files needed for the annotation processor in the first pass, since any files compiled in the first pass will never be recompiled in the second pass and therefore will never have the annotation processor invoked on them. This is because the maven-compiler-plugin only tells the Java compiler to compile the files that it believes need to be re-compiled. If we were to allow all files in the project to be compiled in the first pass then the annotation processor would never get invoked in the second pass as the maven-compiler-plugin would determine that all class files were up to date and that there was no work to do.
Advertisements