Mathias Ricken

Mathias Ricken


Home

Bio


CV/Resume


Curriculum Vitae (pdf)
(doc)  (txt)  (embedded)

Resume (pdf)
(doc)  (txt)  (embedded)


Publications


External linkArchive on A Concurrent Affair

RSSPublication RSS Feed


Research Projects


External linkConcutest: A Framework for Testing Concurrent Programs

Mint: Multi-stage Programming for Java

xajavac: Extended Annotations-Enabled javac

LAPT-javac: Local Variable-Enabled javac

DrJava IDE

Programming for Change: The Temperature Calculator

Object-Oriented Design Festival

Design Patterns for Parsing

Assignments for an Objects-First Curriculum

Design Patterns for Marine Biology Simulation

Geometry Synthesis

DrC# IDE

Geometry Synthesis by Analogy

Teaching


External linkCOMP 410 - Software Engineering Methodology

Past Classes

Work Blog


External linkA Concurrent Affair

RSSBlog RSS Feed


Personal Website


External linksuperscalar.org

External linkMy researchr
External linkMy Academia.edu
External linkPapers
External linkTalks
External linkTeaching

License

      xajavac: Extended Annotation Enabled javac

Contents

Introduction

Java 5.0 introduced annotations, meta-data that can be attached to other parts of the program. The annotations are implemented as interfaces, encoded as regular Java class files. Annotation interfaces implicitly implement the java.lang.annotation.Annotation interface, but otherwise do not support subtyping. Even though Java interfaces can extend multiple other interfaces, as shown below, the extends clause is not permitted for annotations:

interface Parent { ... }
interface Parent2 { ... }
interface Child extends Parent, Parent2 { ... }

@interface ParentAnnot { ... }
@interface ParentAnnot2 { ... }
@interface ChildAnnod extends ParentAnnot, ParentAnnot2 { ... } // error!

Furthermore, annotations may only contain primitive data, strings, class literals (Example: MyClass.class), enumerations, other annotations, and arrays of the beforementioned as members. Specifically, most objects cannot be stored in an annotation (except for instances of String, Class<?> and enumeration types, as explained). java.lang.annotation.Annotation is the implicit supertype of all annotations, but not considered an annotation itself; therefore, it cannot be a member of an annotation:

@interface Erroneous {
  java.lang.annotation.Annotation value(); // error!
}

Generics are not allowed either. The lack of subtyping and generics leads to very poor code reuse: It is impossible to write one annotation that accepts more than one kind of annotation as member; the exact type of all members has to be determined in the annotation. In the Concutest invariant checker, I would have liked to define annotations that serve as Boolean operators, combining the results of other annotations:

@interface InvariantAnnotation { }
@interface OnlyThreadWithName extends InvariantAnnotation {
  String value();
}
@interface OnlyEventThread extends InvariantAnnotation { }
@interface Or extends InvariantAnnotation {
  InvariantAnnotation[] value();
}
@interface And extends InvariantAnnotation {
  InvariantAnnotation[] value();
}

@Or({@And({@OnlyThreadWithName("foo"), @OnlyEventThread}),
     @OnlyThreadWithName("bar")})
void f() { }

Alas, this was impossible. Even though the @Or and @And annotations work the same, I had to define them anew for every new class of invariant annotation, and mixing different invariant annotations was even more complicated. This is the same problem that abstract data types such as lists presented and that was so elegantly solved, either with subtyping or generics: The same list class can be used for all types of data.

When I examined the Java compiler and the class files it produces for annotations, I found nothing that prevented subtyping for annotations. In the compiler, I merely had to remove a few checks to allow the extends clause; the class file format could remain completely unchanged. To allow convenient access to annotations at runtime, I had to extend the reflection API a little, but this was not a difficult task, and the extensions could easily be integrated into the standard Java API.

To use the modified compiler, download the xajavac.jar file (using the xajavac Binaries link below) and invoke it using java -jar xajavac.jar <arguments>, where <arguments> stands for the arguments you would usually pass to javac.

xajavac allows the extends clause for annotations. Just like with interfaces, one or more annotations may be extended. The types that are extended, however, all have to be annotations themselves. Extending classes or interfaces that are not annotations results in an error. If no extends clause exists, then java.lang.annotation.Annotation is implicitly made the superclass, just like in standard Java. Since java.lang.annotation.Annotation is not an annotation itself, it cannot be mentioned in the extends clause of an annotation.

Formally, I have changed the grammar of Java from what has been published in The Java Language Specification, Third Edition in the following places. Here is the abridged original grammar:

AnnotationTypeDeclaration:
        @ interface Identifier AnnotationTypeBody

And here is my changed grammar:

AnnotationTypeDeclaration:
        [final] @ interface Identifier [extends AnnotationTypeList] AnnotationTypeBody
AnnotationTypeList:
        AnnotationType {  ,   AnnotationType}
AnnotationType:
        Identifier

As stated before, there is a context-sensitive requirement for the AnotationType used in the extends clause that cannot be expressed in this context-free grammar: Each AnnotationType mentioned in an extends clause must itself be an annotation type introduced using the AnnotationTypeDeclaration.

Consider these examples:

@interface BaseAnnotation { // java.lang.annotation.Annotation as implicit superclass
  int value();
}

@interface SubAnnotation extends BaseAnnotation { // extends BaseAnnotation
  String s();
}

@interface AnotherAnnotation { // java.lang.annotation.Annotation as implicit superclass
  Class c();
}

@interface ThirdAnnotation { // java.lang.annotation.Annotation as implicit superclass
  Class value();
}

@interface SubSubAnnotation extends SubAnnotation, AnotherAnnotation {
  // extends both SubAnnotation and AnotherAnnotation
}

@interface ErroneousAnnotation extends java.lang.annotation.Annotation {
  // error: Annotation is not an annotation itself
  int value();
}

@interface ErroneousAnnotation2 extends BaseAnnotation {
  // error: value member already exists in BaseAnnotation
  // cannot be overridden
  int value();
}

@interface ErroneousAnnotation3 extends BaseAnnotation {
  // error: value member already exists in BaseAnnotation
  // cannot be overloaded based on return type
  String value();
}

@interface ErroneousAnnotation4 extends BaseAnnotation, ThirdAnnotation {
  // error: value member exists in both BaseAnnotation and ThirdAnnotation
  // ambiguous
}

The JSR 308: Annotations on Types proposal mentions subtyping for annotations as future improvements but considers it out-of-scope for the current proposal.

The JSR 308 proposal listed one problem when annotations allow subtyping: There may be trust issues, because an annotation in a secure framework may be subclassed, and then a non-secure annotation may be used. I don't think this is a major issue: The problem with untrusted code comes up with regular classes and frameworks already. To ensure that an annotation cannot be subclassed, I allowed the final modifier for annotations, just as it is allowed for non-abstract classes. The example below would generate an error:

final @interface FinalAnnotation {
  String value();
}

// ERROR: cannot inherit from final FinalAnnotation
@interface SubAnnotation extends FinalAnnotation {
  int i();
}

The extended reflection API I make available here consists of the AnnotatedElementEx interface and the ClassEx<T>, ConstructorEx<T>, MethodEx, FieldEx, and PackageEx classes in the edu.rice.cs.cunit.subAnnot package. I decided to keep the extended reflection API separate from the reflection API found in the standard Java API. I could have written a library that replaces the original classes in java.lang and java.lang.annotation, but that seemed more disruptive.

All of the ___Ex classes correspond to the standard ___ classes from the standard Java API. They take a standard Java API object in as argument to the constructor and provide a public final field, called java for brevity, to retrieve the original object again. The extended classes support all the methods that the original classes support, except that the argument and return types have been replaced to use the extended classes. For example, the

public Method[] getMethods() throws SecurityException

method in java.lang.Class has been changed to

public MethodEx[] getMethods() throws SecurityException

in edu.rice.cs.cunit.subAnnot.ClassEx. That way, once a developer has started working with the extended classes, all the objects that can be acquired using reflection will be instances of the extended classes as well. To retrieve the underlying standard Java API object, use the java field. Here is an example:

import edu.rice.cs.cunit.subAnnot.*;
import java.lang.annot.*;
public class Test {
  public static void main(String[] args) {
    // create a ClassEx instance from a Class instance
    ClassEx<Test> c = new ClassEx<Test>(Test.class);
    MethodEx[] ms = c.getMethods();
    // to retrieve a Method instance from a MethodEx instance
    Method m = ms[0].java; // java is a public final field
  }
}

The major change that was necessary to incorporate subtyping for annotations was a change in the semantics of the

public <A extends Annotation> A getAnnotation(Class<A> annotationClass)

method. First, this method now also considers subclasses of the specified class annotationClass as annotations that should be returned. Second, since there now may be several annotations that fit this criteria, it is not sufficient anymore to return just one annotation; instead, a list of annotations is returned, where each annotation is of the class specified by annotationClass, or a subclass of that class:

public <A extends java.lang.annotation.Annotation> java.util.List<A> getAnnotations(ClassEx<A> c)

This method could subsume the original getAnnotation(Class<A> c) method, but for legacy purposes, it may make sense to leave the original method in and provide the extended getAnnotations(Class<A> c) method as well to allow access to annotations with subtyping.

For further instructions on how to use the extended reflection API, see the subannot Javadoc.

To achieve subtyping for annotations, only minimal changes to the Java compiler had to be made; the class format remains completely unchanged; and the changes to the reflection API that are necessary to allow reflection for annotations with subtyping were easy to implement. In fact, in a future official change of Java, these changes should be intregrated into the reflection classes in java.lang and java.lang.annotation, and those changes would be even simpler than my external implementation.

Considering how small the changes were, I strongly urge Sun to incorporate subtyping for annotations soon.


Downloads

The binaries and source code to my modified version of javac are available for download here. IMPORTANT: WITH YOUR DOWNLOAD OF THESE FILES, YOU ACCEPT THE JAVA RESEARCH LICENSE 1.5 (review license agreement).

The binaries and the Javadoc for the extended annotation API are available for download here. Acceptance of the Java Research License is not necessary.

The source code will be released soon.

Below is the source and a Windows binary for a tool that allows you to replace the original javac executable and redirect calls to any other program, for example xajavac.jar.

I have also created a compiler adapter for Apache Ant 1.7.0. The single Java source file and the jar file created from it are available for download below. To use the compiler adapter, first make the class available to Ant by adding the -lib path/to/ant-xajavac.jar parameter to the Ant command line. Then set the following two properties:

build.compiler=org.apache.tools.ant.taskdefs.compilers.Xajavac
xajavac.path=path/to/xajavac.jar

The compiler adapter can also be used to launch other Java compilers as long as they are contained in a jar file and have com.sun.tools.javac.Main as main class, just like the original Java compiler.

Here are two presentations I gave in a PLT seminar:

And here is my poster for the Corporate Affiliates Meeting at the Computer Science Department of Rice University:


         
http://www.cs.rice.edu/~mgricken/research/xajavac/index.shtml
Copyright © 2002-2011 by Mathias Ricken. All rights reserved.