fsteeg.com | notes | tags
∞ /notes/java-annotation-processing-in-eclipse | 2017-12-30 | eclipse programming
Java annotations provide metadata for Java code. Many developers use annotations provided by standard Java APIs (like @Override
) or by frameworks (like @Test
). Developers can define their own annotations and process them at runtime using reflection. Additionally, Java provides APIs for writing custom annotation processors that can process these annotations at compile time.
Eclipse provides support for hooking these processors into the compilation process. So when you edit code with these annotations, the processor can analyse the source files, do stuff, and report information back to you.
To give you an idea about how that works, I’ll use Contracts for Java as an example, a tool that implements a contract model similar to that of Eiffel in Java, based on annotations. For our setup, we’ll need Eclipse 4.7 (Oxygen, the 2017 release) or later.
To use Contracts for Java, create a Java project, and add the latest release to the build path:
Then add some code that uses contract annotations (copy and paste the code below into the src folder of your project):
import com.google.java.contract.Ensures;
import com.google.java.contract.Requires;
public class Contracts {
public static void main(String[] args) {
System.out.println(new Numbers().add(-10, 5));
}
}
class Numbers {
@Requires({ "c > 0", "b > 0" })
@Ensures({ "result > a", "result > b" })
int add(int a, int b) {
return a - b;
}
}
At this point, the project should not report any issues. Normal compilation of the Java source file works just fine. Our processor is not enabled for our annotations yet. To configure the annotation processing we go to Project > Properties > Java Compiler > Annotation Processing.
Select Enable project specific settings and add the following options that we want to pass to the annotation processor: set com.google.java.contract.classpath
to %classpath%
and com.google.java.contract.classoutput
to %PROJECT.DIR%/.apt_generated
(the location of your project’s generated source directory):
These properties are used by the processor to compile the annotated files, and to create output files. The %classpath%
placeholder is replaced with your Java project’s build path by Eclipse, so that the annotation processor can access all the libraries used in your project when compiling your code. The %PROJECT.DIR%
placeholder is replaced with the path to your Java project.
Finally we add the processor Jar to Project > Properties > Java Compiler > Annotation Processing > Factory Path:
After confirming these changes, and running a full build, compilation issues in the annotations are now reported in the editor. Our code contains a precondition mentioning a variable called c
. But the annotated method has no c
parameter. This is reported as an error in the editor:
After we fix the first precondition to a > 0
, the code compiles, and we can run it (Run > Run as > Java application).
To see the files generated by the processor in the .apt_generated
directory in Eclipse, you should disable filtering of .*resources
in the Package Explorer’s view menu (the little triangle > Filters…):
Also make sure you have set up your workspace to refresh automatically in Preferences > General > Workspace > Refresh using native hooks or polling:
Besides the annotation processing at compile time, Contracts for Java also uses bytecode instrumentation for its runtime checks. To have the contracts checked at runtime, add -javaagent:cofoja.contracts.asm-1.3-20160207.jar
to the VM arguments of your run configuration (go to Run > Run Configurations, activate the Arguments tab):
Now, when running, we are made aware of the violated precondition in our code, since we are passing -10
as a
, which is not larger than 0
:
After we fix the call violating the precondition to new Numbers().add(10, 5);
we now see that our implementation of add
does not fulfill the postcondition, since the result is not larger than a
:
After fixing our implementation to return a + b
, not a - b
, all contracts are fulfilled and the code now runs without errors.
Contracts for Java uses annotations in an interesting way and shows what can be achieved with annotation processing as a tool, and how Java annotation processors can integrate with Eclipse.
If you’re interested in creating your own processors, check out my test projects for bug 3412981 to get a basic setup where an annotation processor compiles the annotated Java source file.
For more information on these tools check out the Contracts for Java and JDT-APT project sites.
1 That bug came up in the comments of a previous post about using Contracts for Java in Eclipse.