Update: This article introduces “apt”, the new tool provided along with JDK1.5 beta2 to process annotations, the new addition to Java5. It walks you through a simple example on declaring an annotation, using it and processing the same. This will help you get started on using annotations in your applications.
Introduction
Sun has recently released Annotation Processing Tool (apt) along with beta2 of JDK1.5. This tool greatly helps in using the annotations feature that is introduced in JDK1.5. In this article, I will walk you through the steps required to create, use and process annotations, with the help of an example. This is not an article on annotations feature in JDK1.5, but an article on how to process annotations using apt. One can refer to the article “Declarative Programming in Java” at OnJava to get a very good overview of annotations and what they have to offer. “Apt” is a command line utility that helps in processing annotations on the source code. Instead of delving into the details on what “apt” offers, I will straight away dive into an example, which can help one appreciate the “power” of apt.
Setting the Ground
During software development, it is very common for developers to use markers such as “TODO”, “FIXME” to mark the work that is not yet complete in all aspects. Usually these will be present inside the comments. Usually the developer searches the source code regularly to figure out if there are still any more incomplete tasks. Let us use annotations in this scenario, for the purpose of understanding the annotations feature and understanding how to the processing them using the apt. The objective is to process the annotated source code and get list of places where the code has been marked as “ToDo”. Before proceeding further, ensure that you have JDK1.5 beta 2 with you. You can download it from here. Also, as you are aware, JDK1.5 comes with host of other additions to the language, such as Generics, enhanced for loop, static imports. I will refrain from using the new features in the listings below to the best possible extent, so that the focus of the reader is not diverted to these new features.
Step 1: Declare the ToDo Annotation
Declaring an annotation is very simple. Following is the listing of the ToDo annotation. This annotation does not have any members.
Listing 1: ToDo.javapackage com.rajs.jdk1_5;
public @interface ToDo
{
} |
Notice the special “@” tag before the interface declaration. This indicates that this is not a regular interface, but an annotation.
Step 2: Write Code that uses the “ToDo” Annotation.
Annotations can be at package level, class level, field level, method level and even at local variable level. Let us create two classes one which uses the annotation at a class level and another at method level for the sake of the example.
Listing 2: IncompleteClass.java
package com.rajs.jdk1_5;
@ToDopublic class IncompleteClass
{
} |
Listing 3: PartiallyCompleteClass.java
package com.rajs.jdk1_5;
public class PartiallyCompleteClass
{
private int age = 20;
@ToDo
private String name;
public PartiallyCompleteClass()
{
}
public int getAge()
{
return age;
}
@ ToDo
public void incompleteMethod()
{
}
} |
Notice the way the class, method and attribute are annotated by simply putting the “@<AnnotationName> before declaring them. Now we have code which can compile in JDK1.5. However there is no use of this code as we are not doing anything with the annotations. Let us now process these annotations so that we get a report of the source code that is incomplete (annotated as ToDo).
Step 3: Create an Annotation Processor Factory
The first thing that we need to do to process the annotations is to create an annotation processor factory. An annotation processor factory is responsible for creating an annotation processor for one or more annotation types. JDK1.5 beta2 comes with new classes that are part of the tools.jar. These new classes must be used by developers intending to use apt. There is an interface com.sun.mirror.apt.AnnotationProcessorFactory as a part of these new classes that must be implemented by us. Here is the listing for our factory.
Listing 4: MyAPF.java
package com.rajs.jdk1_5;
import java.util.Collection;
import java.util.Set;
import java.util.Vector;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;
public class MyAPF implements AnnotationProcessorFactory
{
Vector anntTypes = new Vector();
public MyAPF()
{
//all annotations are processed by this factory.
anntTypes.addElement(“*”);
}
public Collection supportedOptions()
{
//no options are supported.
return new Vector();
}
public Collection supportedAnnotationTypes()
{
return anntTypes;
}
public AnnotationProcessor getProcessorFor(
Set <com.sun.mirror.declaration.AnnotationTypeDeclaration> set,
AnnotationProcessorEnvironment env)
{
//return an instance of ToDoAnnotation Processor.
return new ToDoAP(env);
}
} |
In the above listing, we have declared a class that implements the com.sun.mirror.apt.AnnotationProcessorFactory interface. There are three methods in the factory. The first method supportedOptions() returns if there any options that are supported by this annotation processing factory. For beginners like us, at this moment, we can ignore this and return an empty collection. The second method supportedAnnotationTypes() returns the collection of annotation types that can be processed by this factory. Since we have just one annotation type here, we can either give that name here or simply return a “*”, indicating all annotations are processed by this class. The third and the most important method getProcessorFor() returns the AnnotationProcessor that actually processes the annotations. Here we are returning an instance of ToDoAP (discussed in detail below). This method will be invoked by the apt. The tool shall pass the context of the processing using the AnnotationProcessorEnvironment parameter. The AnnotationProcessingEnvironment has methods that provide the state of processing the source code which can be used by the AnnotationProcessor.
Step 4: Create the Annotation Processor
The annotation processor is the class that actually processes the annotations. This is the class that has the ability to create new source files, configuration files (deployment descriptors, for example), class files, or data files. Here is the listing for ToDoAP.java
Listing 5: ToDoAP.java
package com.rajs.jdk1_5;
import java.io.File;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Iterator;
import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.Declaration;
public class ToDoAP implements AnnotationProcessor
{
AnnotationProcessorEnvironment ape;
PrintWriter pw;
public ToDoAP(AnnotationProcessorEnvironment ape)
{
this.ape = ape;
}
/**
* @see com.sun.mirror.apt.AnnotationProcessor#process()
*/
public void process()
{
AnnotationTypeDeclaration todoAtd = (AnnotationTypeDeclaration)ape.getTypeDeclaration(“com.rajs.jdk1_5.ToDo”);
Collection c = ape.getDeclarationsAnnotatedWith(todoAtd);
for(Iterator I = c.iterator(); i.hasNext();)
{
Declaration typeDecl = (Declaration)i.next();
System.out.println(typeDecl.getPosition());
}
}
} |
In the above code, the method of significance is “process()”. In this method, the first line returns the declaration that represents the “com.rajs.jdk1_5.ToDo” annotation. The next line is the most important line – this provides the collection of Declarations that are annotated with “com.rajs.jdk1_5.ToDo”. This is achieved by calling the method “getDeclarationsAnnotatedWith()” method on the AnnotationProcessorEnvionment. The resulting collection of Declaration instances is iterated and the position of the annotation is printed by calling the getPosition() method on each instance of Declaration.
Step 5: Compiling the Code
As mentioned earlier, to compile this code, one needs to have JDK1.5 Beta 2. You can download the same here. Ensure that the classpath contains “%JAVA_HOME%\lib\tools.jar”. Ensure the path contains “%JAVA_HOME%\bin”.Use the “–source 1.5” option along with javac, as we are compiling code containing JDK1.5 specific features.
javac -cp %CLASSPATH% -d bin -source 1.5 src/com/rajs/jdk1_5/*.java |
Step 6: Running “apt”
Finally, we are now ready to run the annotation processor tool. Apt first processes the annotations in the specified source code. Apt recursively processes any new source code that is generated during the earlier processing, till no more source code is there to be processed.. Finally, all the source code will be compiled using javac by apt. Apt has a default inbuilt mechanism by which it identifies the AnnotationProcessorFactories that needs to be used during its execution. However, in this article, we will use a much simpler way of specifying the apt the factory class to be used by passing the same as a command line parameter to apt. Following is the invocation of apt and the output
>apt -cp %CLASSPATH% -d bin -s src -source 1.5 -factory com.rajs.jdk1_5.MyAPF src/com/rajs/jdk1_5/*.java>src/com/rajs/jdk1_5/PartiallyCompleteClass.java:4>src/com/rajs/jdk1_5/PartiallyCompleteClass.java:20>src/com/rajs/jdk1_5/IncompleteClass.java:9 |
As you see the output contains the list of java files and approximate line number where the ToDo annotations are present.
Summary
The article explained in detail the steps involved in executing the new “annotation processing tool” from Sun. Needless to say, we have just touched the waters of annotation. There are lots of advanced features such as DeclarationVisitors, Filer, Messagers that are not covered in this introductory article to limit the scope. Also it is worthwhile mentioning that while “apt” is intended to use for processing annotations, it can be used where there is a need to parse source code. Interested readers can always get into the depth from the references available in the Resources section.
Resources
- Declarative Programming in Java — http://www.onjava.com/lpt/a/4768
- Download JDK1.5 — http://java.sun.com/j2se/1.5.0/download.jsp
- Getting Started with the Annotation Processing Tool — http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html