Chapter 7. Annotation Compiler for JDK 1.4

7.1. Annotations with JDK 1.4.2

Annotations are only available in JDK 1.5, but using our annotation compiler you can acheive similar functionality with JDK 1.4.2.

Annotations must map to an annotation type, in JDK 1.5 they are defined as:

   package com.mypackage;
   
   public @interface MyAnnotation
   {
      String myString();
      int myInteger();
   }            
         

Annotation types for use with the annotation compiler are defined in exactly the same way for JDK 1.4.2, with the important difference that '@interface' is replaced by 'interface'. i.e. the simulater annotation type is a normal Java interface:

   package com.mypackage;
   
   public interface MyAnnotation
   {
      String myString();
      int myInteger();
   }            
         

The syntax for using annotations in JDK 1.4.2 is almost exactly the same as JDK 1.5 annotations except for these subtle differences:

  • they are embedded as doclet tags
  • You use a double at sign, i.e. '@@'
  • You MUST have a space after the tag name otherwise you will get a compilation error. (This is the quirkiness of the QDox doclet compiler used to compile the annotations.')
  • You cannot import the annotation type, you must use the fully qualified name of the interface.
  • You can only annotate top-level and inner classes, and their constructors, methods and fields. Annotating anonyomus classes, local classes, and parameters for constructors or methods is not supported.
  • You cannot specify default values for an annotation's value

This example shows an annotated class in JDK 1.4.2:

   package com.mypackage;
   
   /**
    * @@com.mypackage.MyAnnotation (myString="class", myInteger=5)
    */
   public class MyClass
   {
      /**
       * @@com.mypackage.MyAnnotation (myString="field", myInteger=4)
       */
      private String myField;
      
      /**
       * @@com.mypackage.MyAnnotation (myString="constructor", myInteger=3)
       */
      public MyClass()
      {
      }
      
      /**
       * @@com.mypackage.MyAnnotation (myString="method", myInteger=3)
       */
      public int myMethod()
      {
      }
   }            
         

7.2. Enums in JDK 1.4.2

Another JDK 1.5 feature that JBoss AOP helps introduce to JBoss 1.4.2 are Enums. As an example we can look at the org.jboss.aop.advice.Scope enum that is used for the @Aspect annotation. Here is the JDK 1.5 version.

            package org.jboss.aop.advice;
            
            public enum Scope
            {
               PER_VM, PER_CLASS, PER_INSTANCE, PER_JOINPOINT
            }         
         

And it's usage in JDK 1.5

   package com.mypackage;

   @Aspect (scope=org.jboss.aop.advice.Scope.PER_VM)
   public class SomeAspect
   {
   }     
         

The usage in JDK 1.4.2 is similar:

   package com.mypackage;
   
   /**
    * @@org.jboss.aop.Aspect (scope=org.jboss.aop.advice.Scope.PER_VM)
    */
   public class SomeAspect
   {
      //...
   }     
         

However the declaration of the enumeration is different in 1.4.2:

   package org.jboss.aop.advice;
   
   import java.io.ObjectStreamException;
   
   public class Scope extends org.jboss.lang.Enum
   {
      private Scope(String name, int v)
      {
         super(name, v);
      }
   
      public static final Scope PER_VM = new Scope("PER_VM", 0);
      public static final Scope PER_CLASS = new Scope("PER_CLASS", 1);
      public static final Scope PER_INSTANCE = new Scope("PER_INSTANCE", 2);
      public static final Scope PER_JOINPOINT = new Scope("PER_JOINPOINT", 3);
      
      private static final Scope[] values = {PER_VM, PER_CLASS, PER_INSTANCE, PER_JOINPOINT};
   
      Object readResolve() throws ObjectStreamException
      {
         return values[ordinal];
      }
   
   }
         

To create your own enum class for use within annotations, you need to inherit from org.jboss.lang.Enum. Each enum has two values, a String name, and an integer ordinal. The value used for the ordinal must be the same as it's index in the static array.

7.3. Using Annotations within Annotations

The annotation compiler allows you to use annotations within annotations. This is best illustrated with an example. The definitions of the annotation interfaces in JDK 1.4.2:

            com.mypackage;
            
            public interface Outer
            {
               Inner[] values();
            }
         

            com.mypackage;
            
            public interface Inner
            {
               String str();
               int integer();
            }
         

The annotations can be applied as follows

            com.mypackage;
            
            /**
             * @@com.mypackage.Outer ({@@com.mypackage.Inner (str="x", integer=1), \ 
                   @@com.mypackage.Inner (str="y", integer=2)})
             */
            public class Test
            {
               Inner[] values();
            }
         

7.4. Using the Annotation Compiler

In order to use the JDK 1.4.2 annotations you have to precompile your files with an annotation compiler.

To use the annotation compiler you can create a simple ant build.xml file

         
<?xml version="1.0" encoding="UTF-8"?>

<project default="run" name="JBoss/AOP">
   <target name="prepare">

Include the jars AOP depends on

      <path id="javassist.classpath">
         <pathelement path="../../../javassist.jar"/>
      </path>
      <path id="trove.classpath">
         <pathelement path="../../../trove.jar"/>
      </path>
      <path id="concurrent.classpath">
         <pathelement path="../../../concurrent.jar"/>
      </path>
      <path id="jboss.common.classpath">
         <pathelement path="../../../jboss-common.jar"/>
      </path>
      <path id="jboss.aop.classpath">
         <pathelement path="../../../jboss-aop.jar"/>
      </path>
      <path id="qdox.classpath">
         <pathelement path="../../../qdox.jar"/>
      </path>
      <path id="classpath">
         <path refid="javassist.classpath"/>
         <path refid="trove.classpath"/>
         <path refid="jboss.aop.classpath"/>
         <path refid="jboss.common.classpath"/>
         <path refid="concurrent.classpath"/>
         <path refid="qdox.classpath"/>
      </path>

Define the ant task that does the annnotation compilation

      <taskdef
         name="annotationc" 
         classname="org.jboss.aop.ant.AnnotationC"
         classpathref="jboss.aop.classpath"/>
   </target>

   <target name="compile" depends="prepare">]></programlisting>
         Compile the source files
         <programlisting><!CDATA[
      <javac srcdir="."
         destdir="."
         debug="on"
         deprecation="on"
         optimize="off"
         includes="**">
            <classpath refid="classpath"/>
      </javac>

Run the annotation compiler

      <annotationc compilerclasspathref="classpath" classpath="." bytecode="true">
         <src path="."/>
      </annotationc>
   </target>
</project>

The org.jboss.aop.ant.AnnotationC ant task takes several parameters.

  • compilerclasspath or compilerclasspathref - These are interchangable, and represent the jars needed for the annotation compiler to work. The compilerclasspath version takes the paths of the jar files, and the compilerclasspathref version takes the name of a predefined ant path.
  • bytecode - The default is false. If true the annotation compiler instruments (i.e. modifies) the class files with the annotations. In this case, the classes must be precompiled with javac and classpath or classpathref must be specified.
  • classpath or classpathref - Path to the compiled classes to be instrumented with annotations, if bytecode="true". The classpath version takes the path of the directory, and the classpathref version takes the name of a predefined ant path.
  • xml - Default is false. If true an xml file is generated containing information of how to attach the annotations at a later stage in the aop process.
  • output - If xml="true", this lets you specify the name you would like for the generated xml file. The default name is metadata-aop.xml
  • verbose - Default is false. If true, verbose output is generated, which comes in handy for diagnosing unexpected results.

You cannot currently specify both bytecode and xml.

You can also run org.jboss.aop.ant.AnnotationC from the command line, you need

   $ java -cp <all the JBoss AOP jars and the directory containing files we want to AOP> \ 
         org.jboss.aop.annotation.compiler.AnnotationCompiler \ 
         [-xml [-o outputfile ]] [-bytecode]<files>+
         

In the /bin folder of the distribution we have provided batch/script files to make this easier. It includes all the aop libs for you, so you just have to worry about your files. The usage is:

   $ annotationc <classpath> [-verbose] [-xml [-o outputfile]] [-bytecode] <dir_or_file>+
         

  • classpath - path to your classes and any jars your code depends on

The other parameters are the same as above.