X

Musings on JDK development

JSR 269 in Mustang Build 57

The initial reference implementation of href="http://www.jcp.org/en/jsr/detail?id=269">JSR 269 shipped as
part of Mustang
build 57! JSR 269 has two basic pieces, an API that models the Java
programming language and an API for writing annotation
processors
. Those pieces are in the new packages
javax.lang.model.\* and javax.annotation.processing,
respectively. The JSR 269 functionality is accessed by using new
options to the javac command. By including JSR 269 support,
the javac command now acts analogously to the href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/index.html">apt
command in Sun's JDK 5.

To get an overview of annotation processing, see href="http://developers.sun.com/learning/javaoneonline/2005/coreplatform/TS-7425.html">our
2005 JavaOne session on that topic.

Annotation processing is now enabled by default in javac;
we designed our implementation of JSR 269 so that there should only be
negligible speed impact on javac when no annotation processors
are run. To just run annotation processing without compiling source code to class
files, use the -proc:only option.

Writing your first annotation processor


Annotation processing is a form of meta-programming, that is,
programming based on the structure of a program. However, for
starters, let's consider a "Hello World" annotation
processor:
import javax.annotation.processing.\*;
import static javax.lang.model.SourceVersion.\*;
import javax.lang.model.element.\*;
import javax.lang.model.type.\*;
import javax.lang.model.util.\*;
import java.util.Set;
@SupportedAnnotationTypes("\*")

// Process all annotations
@SupportedSourceVersion(RELEASE_6)
public class HelloWorldProcessor extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations,

RoundEnvironment roundEnv) {

if (!roundEnv.processingOver())

processingEnv.getMessager().printNotice("Hello World");

return false; // Don't claim any annotations
}
}

To compile this program, add tools.jar to the classpath of javac:

javac -cp Path-to-tools.jar HelloWorldProcessor.java

(We plan to make adding tools.jar to the classpath href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6328941">more
convenient in the future.) To run the annotation processor, use
the new -processor option to javac:

javac -processor HelloWorldProcessor Foo.java

The output should include:

Note: Hello World

(HelloWorldProcessor can be run against
HelloWorldProcessor.java; to do so, both tools.jar
and the class files for HelloWorldProcessor need to be on the
classpath.)
This simple annotation processor doesn't actually examine the
structure of a program, but it illustrates a few points about how to
write and run annotation processors:
  • Annotation processors are run by javac before the input
    source files are compiled.
  • Annotation processors must implement the
    javax.annotation.processing.Processor interface to get
    registered with javac. This interface has methods that
    inform javac about what the annotation processor does,
    including what annotations it processes and what source version it
    supports.
  • Extending the
    javax.annotation.processing.AbstractProcessor class is a
    convenient way to write a processor since annotations can be used to
    provide the value the methods return. When extending
    AbstractProcessor, the only method that needs to be
    implemented is process.
  • A processor can get run multiple times in one javac
    invocation. If the test (!roundEnv.processingOver()) is
    removed, "Note: Hello World" will be printed twice.
    The javac infrastructure can call a processor's
    process method once per round. A round corresponds to
    analyzing a set of types.
  • Processors get access to resources, like a Messager, by
    getting helper objects from different environments. The
    ProcessingEnvironment (the processingEnv field in
    AbstractProcessor) provides round-independent utilities while
    the RoundEnvironment argument to process provides
    round-dependent ones.

Future Topics


"Hello World" just scratches the surface of what
annotation processors can do! For example, annotation processors can
  • process annotations
  • recursively generate new source files and class files
  • analyze class files (try javac -Xprint java.lang.Object)

In future blog entries, I'll discuss these topics as well as providing
a comparison between apt and JSR 269 annotation processing,
best practices for writing and running annotation processors,
suggestions on how to use JSR 269 visitors, and describing how the API
copes with the href="http://www.daimi.au.dk/~madst/tool/papers/expression.txt">expression
problem.

Join the discussion

Comments ( 10 )
  • Peter Ah&amp;eacute;'s Weblog Friday, October 21, 2005
    [Trackback] Joe just announced the next big thing in compilers. We have been
    working hard the last months to the get this ready. There's still
    a lot to be done, but you can actually run an annotation processor
    directly in javac. This is without hurting perfo...
  • Matthias Ernst Saturday, October 22, 2005
    Joe,
    what's the rationale behind @SupportedAnnotationTypes? Why didn't you use a plain method #getSupportedAnnotationTypes? Or #supportsAnnotationType(AnnotationType).
    I'm afraid this is not a good use of annotations.
    That's like implementing
    @FileNameFilter("\*.jpg")
    public class Jpegs {}
  • Joe Darcy Sunday, October 23, 2005
    Matthias,
    The <tt>"@SupportedFoo"</tt> annotations are used by the <tt>AbstractProcessor</tt> class to provide the data returned by the corresponding <tt>"getSupportedFoo"</tt> method of the <tt>Processor</tt> interface. Just having the interface methods is not convenient, as shown by the boilerplate code used for factories in <tt>apt</tt>. Just using annotations on a processor class without a method to call is not very type safe; there is no way to require a particular annotation be present on a class. The JSR 269 <tt>AbstractProcessor</tt> combines the best aspects of these two approaches; the type safety of implementing an interface is maintained with the convenience of using annotations to populate the returned data. If you don't like using annotations for this purpose, you can implement the <tt>Processor</tt> interface directly or override the <tt>AbstractProcessor</tt> methods. However, I think the 269 pattern here is very neat and useful; I certainly like using it for the annotation processors I write.
  • Matthias Monday, October 24, 2005
    Ok, I understand. So you're basically using the annotation because it has a more convenient syntax for returning a "constant" Set<String> than Java? In annotation land, a value can be short-hand for a singleton array, and AbstractProcessor does the array->set conversion. Things that could be expressed in Java, too, using a var-args method:
    AbstractProcessor:
    protected Set<String> set(String ... strings) {
    return Collections.immutableSet(new HashSet(Arrays.asList(strings)));
    }
    MyProcessor:
    public Set<String> getSupportedAnnotationTypes() {
    return set("\*");
    }
    The method boiler-plate still sucks, though.
    Now if the annotation shorthand for implementing a parameterless interface method through a constant were in the language ...
  • Manabu Nakamura Monday, October 24, 2005
    Can I process annotations on local classes and anonymous classes by using Pluggable Annotation Processing API?
  • Peter Ah&amp;eacute; Tuesday, October 25, 2005
    Another reason for using annotations is that
    information about supported annotation types
    and source versions is data about the
    annotation processor, that is, meta-data.
  • Joe Darcy Wednesday, October 26, 2005
    Concerning whether annotations on local and anonymous classes can be processed by JSR 269, the current answer is "no." However, we are considering adding the API elements needed to accommodate this. As a prerequisite, the JSR 269 language model can already describe local and anonymous classes with the NestingKind enum values (ANONYMOUS, LOCAL, MEMBER, or TOP_LEVEL) returned by TypeElement.getNestingKind.
  • Aviad Ben Dov Saturday, February 4, 2006
    Will I be able to receive callbacks for instances previously annotated by a processed annotation?
    I am asking this to implement some @Wrapper annotation, such as @WeakReference, so that it replaces a declaration with a wrapping declaration and in any part of the code it uses the wrapped object it replaces it to a <code>get()</code> call.
    Do you think this will be possible with the new tool?
  • Dave Minter Monday, May 29, 2006
    Does 269 allow for source to be generated that replaces an input?
    I.e. can I process class Foo to generate class Foo (in the same package), and then pass that on to the normal compiler chain (or recurse)?
    It would be awesome if so!
  • Joe Darcy Wednesday, May 31, 2006
    No, neither apt nor JSR 269 allow source files being processed to be overwritten and replaced with processor-augmented content.
    Additional questions can be addressed in the annotation processing forum.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.