8 Replies Latest reply on Feb 27, 2005 10:04 AM by Shigeru Chiba

    reading java 5.0 annotations

    benedict Newbie

      How do I read Java 5.0 annotations from a CtClass?
      The documentation for AnnotationWriter shows how to construct one, but
      I cannot see how to read one!

      many thanks,
      Benedict

        • 1. Re: reading java 5.0 annotations
          Shigeru Chiba Expert

          The current release 3.0 does not provide high-level API.
          So you must use the lower-level API, but using it is not difficult.
          For example,

          import javassist.*;
          import javassist.bytecode.*;
          
          CtMethod m = ... ;
          MethodInfo minfo = m.getMethodInfo();
          AnnotationsAttribute attr = (AnnotationsAttribute)
           minfo.getAttribute(AnnotationsAttribute.invisibleTag);
          if (atr != null) {
           Annotation an = attr.getAnnotation("Author");
           String s = ((StringMemberValue)a.getMemberValue("name")).getValue();
           System.out.println("@Author(name=" + s + ")");
          }
          


          This prints the @Author annotation of the method m.



          • 2. Re: reading java 5.0 annotations
            benedict Newbie

            Many thanks for prompt reply. However when I do that, I find that 'attr' is always null! Here's part of a test of annotating methods.

            When I annotate a class, I can get the annotation for the class, but then find memberNames() always returns null, so cannot get members from within the annotion.

            I have been using the latest .jar from the site, but I also got the CVS source and rebuilt that, but the effect is the same.

            Sorry to appear so slow.
            Benedict
            --------------------------------------------------------------------



            @Retention(RetentionPolicy.RUNTIME)//
            public @interface Author {

            String name() default "me";

            int age() default 999;
            }

            @Author//
            class Thing {

            @Author public void methodWithAuthor() {
            }

            public void methodWithoutAuthor() {
            }
            }

            /**
            * * Output is:
            *
            * <pre>
            *
            *
            *
            * .classname:ProblemTest$Thing
            * .raw demoClass=javassist.CtClassType@10d448[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ca318a[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ]
            * CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V]
            * attr:null
            * CtMethod: javassist.CtMethod@128f593[public methodWithoutAuthor ()V]
            * attr:null
            *
            *
            *
            * </pre>
            */
            public void testJavassistMethodAnnotations() throws Exception {

            ClassPool pool = ClassPool.getDefault();

            String demoClassName = "ProblemTest$Thing";

            CtClass demoClass = pool.get(demoClassName);
            echo("raw demoClass=" + demoClass);

            CtMethod[] thingMethods = demoClass.getDeclaredMethods();

            for (CtMethod ctMethod : thingMethods) {
            echo("CtMethod: " + ctMethod);

            MethodInfo mInfo = ctMethod.getMethodInfo();

            AnnotationsAttribute attr = (AnnotationsAttribute) mInfo
            .getAttribute(AnnotationsAttribute.invisibleTag);

            System.out.println("attr:" + attr);

            if (attr != null) {
            // we never get here!
            Annotation author = attr.getAnnotation("Author");
            echo("Author annotation:" + author);
            String s = ((StringMemberValue) author.getMemberValue("name")).getValue();
            System.out.println("@Author(name=" + s + ")");
            }
            }
            }


            • 3. Re: reading java 5.0 annotations
              Shigeru Chiba Expert

              Can you replace AnnotationsAttribute.invisibleTag
              with AnnotationsAttribute.visibleTag in the above
              test code and try again?

              • 4. Re: reading java 5.0 annotations
                benedict Newbie

                Thanks for suggestion. I did that: I can now get as far as seeing the @Author attribute, but cannot
                retrieve the name() element. In fact, getMemberNames() always is null.
                Benedict

                Here's a self-contained test class that shows the problem .
                Thanks
                Benedict

                
                import java.lang.annotation.Retention;
                import java.lang.annotation.RetentionPolicy;
                import java.util.Arrays;
                import java.util.List;
                import java.util.Set;
                
                import javassist.ClassPool;
                import javassist.CtClass;
                import javassist.CtMethod;
                import javassist.bytecode.AnnotationsAttribute;
                import javassist.bytecode.AttributeInfo;
                import javassist.bytecode.ClassFile;
                import javassist.bytecode.MethodInfo;
                import javassist.bytecode.SourceFileAttribute;
                import javassist.bytecode.annotation.Annotation;
                import javassist.bytecode.annotation.StringMemberValue;
                import junit.framework.TestCase;
                import junit.textui.TestRunner;
                
                /**
                 * This test demonstrates that when I try to access annotations using Javassist,
                 * I can get the annotation per se, but the membernames are always null, so I
                 * can't access the data within the annotation.
                 * <p>
                 * Output is:
                 *
                 * <pre>
                 .
                
                ---------------------testJavassistMethodAnnotations
                raw demoClass=javassist.CtClassType@2e7263[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ffb8dc[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ]
                
                -----CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V]
                attr:@ProblemTest$Author
                Author annotation:@ProblemTest$Author
                author.getMemberNames is null
                F.
                
                ---------------------testClassAnnotations
                demoCtClass: javassist.CtClassType@2e7263[changed class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ffb8dc[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ]
                classfile: javassist.bytecode.ClassFile@1027b4d
                interfaces: []
                attributes: [javassist.bytecode.SourceFileAttribute@1ed2ae8, javassist.bytecode.InnerClassesAttribute@19c26f5, @ProblemTest$Author]
                
                ---
                attribute:javassist.bytecode.SourceFileAttribute@1ed2ae8
                an AttributeInfo is of class: class javassist.bytecode.SourceFileAttribute - javassist.bytecode.SourceFileAttribute@1ed2ae8
                
                ---
                attribute:javassist.bytecode.InnerClassesAttribute@19c26f5
                an AttributeInfo is of class: class javassist.bytecode.InnerClassesAttribute - javassist.bytecode.InnerClassesAttribute@19c26f5
                
                ---
                attribute:@ProblemTest$Author
                annotations: [@ProblemTest$Author]
                
                ++annotation: @ProblemTest$Author of class class javassist.bytecode.annotation.Annotation
                interfaces: []
                typename:ProblemTest$Author
                memberNames: null
                We have found the Author annotation, but cannot look at the members!
                memberValue-name: null
                stringMemberValue: null
                F
                Time: 0.141
                There were 2 failures:
                1) testJavassistMethodAnnotations(ProblemTest)junit.framework.AssertionFailedError: stringmembervalue for name() is null
                 at ProblemTest.testJavassistMethodAnnotations(ProblemTest.java:166)
                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                 at ProblemTest.main(ProblemTest.java:274)
                2) testClassAnnotations(ProblemTest)junit.framework.AssertionFailedError: On Author annotation, memberNames is null!
                 at ProblemTest.dumpAnnotation(ProblemTest.java:261)
                 at ProblemTest.testClassAnnotations(ProblemTest.java:217)
                 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
                 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
                 at ProblemTest.main(ProblemTest.java:274)
                
                FAILURES!!!
                Tests run: 2, Failures: 2, Errors: 0
                
                
                 * </pre>
                 *
                 * @author bheal .
                 */
                public class ProblemTest extends TestCase {
                
                 @Retention(RetentionPolicy.RUNTIME)//
                 public @interface Author {
                
                 String name() default "me";
                
                 int age() default 999;
                 }
                
                 @Author//
                 class Thing {
                
                 @Author public void methodWithAuthor() {
                 }
                
                 public void methodWithoutAuthor() {
                 }
                 }
                
                 public void setUp() {
                 echo("\n\n---------------------" + getName());
                 }
                
                 /**
                 * * Output is:
                 *
                 * <pre>
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 * .classname:ProblemTest$Thing
                 * .raw demoClass=javassist.CtClassType@10d448[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ca318a[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ]
                 * CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V]
                 * attr:null
                 * CtMethod: javassist.CtMethod@128f593[public methodWithoutAuthor ()V]
                 * attr:null
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 *
                 * </pre>
                 */
                 public void testJavassistMethodAnnotations() throws Exception {
                
                 ClassPool pool = ClassPool.getDefault();
                
                 String demoClassName = "ProblemTest$Thing";
                
                 CtClass demoClass = pool.get(demoClassName);
                 echo("raw demoClass=" + demoClass);
                
                 CtMethod[] thingMethods = demoClass.getDeclaredMethods();
                
                 for (CtMethod ctMethod : thingMethods) {
                 echo("\n-----CtMethod: " + ctMethod);
                
                 MethodInfo mInfo = ctMethod.getMethodInfo();
                
                 // AnnotationsAttribute attr = (AnnotationsAttribute) mInfo
                 // .getAttribute(AnnotationsAttribute.invisibleTag);
                
                 // on suggestion from chiba, changed to 'visibleTag'
                 AnnotationsAttribute attr = (AnnotationsAttribute) mInfo
                 .getAttribute(AnnotationsAttribute.visibleTag);
                
                 System.out.println("attr:" + attr);
                
                 if (attr != null) {
                 // we never get here for invisible tag, but do for visible tag!
                 Annotation authorAnnotation = attr.getAnnotation("ProblemTest$Author");
                 echo("Author annotation:" + authorAnnotation);
                 assertNotNull("no @Author annotation", authorAnnotation);
                 // we manage to get here, but cannot find the name() element
                
                 if ( authorAnnotation.getMemberNames()==null) {
                 // it always is null!
                 echo("author.getMemberNames is null");
                 }
                 StringMemberValue stringMemberValue = ((StringMemberValue) authorAnnotation
                 .getMemberValue("name"));
                 assertNotNull("stringmembervalue for name() is null", stringMemberValue);
                 String nameValue = stringMemberValue.getValue();
                 System.out.println("@Author(name=" + nameValue + ")");
                 }
                
                 // assertNotNull("attr is null, so cannot get Author", attr);
                 }
                 }
                
                 // remove working reflection tests, to avoid possible name clashes
                
                 // on
                 // 'Annotation'
                
                 // public void testAllAnnotations() throws Exception {
                 // Annotation[] allAnnotations = Thing.class.getAnnotations();
                 // echo("all annotations: " + Arrays.asList(allAnnotations));
                 // assertNotNull("no annotations at all", allAnnotations);
                 // }
                 //
                 // public void testFlagAnnotation() throws Exception {
                 // Flag flag = Thing.class.getAnnotation(Flag.class);
                 // assertNotNull("null flag", flag);
                 // assertEquals("wrong value", flag.f1(), 999);
                 // }
                
                 public void testClassAnnotations() throws Exception {
                 ClassPool pool = ClassPool.getDefault();
                 // pool.insertClassPath(".");
                 // echo("pool=" + pool);
                
                 String demoClassName = "ProblemTest$Thing";
                
                 CtClass demoCtClass = pool.get(demoClassName);
                 echo("demoCtClass: " + demoCtClass);
                
                 ClassFile classfile = demoCtClass.getClassFile();
                 echo("classfile: " + classfile);
                 echo("interfaces: " + Arrays.asList(classfile.getInterfaces()));
                 echo("attributes: " + classfile.getAttributes());
                
                 for (AttributeInfo attributeInfo : (List<AttributeInfo>) classfile.getAttributes()) {
                 echo("\n---");
                 echo("attribute:" + attributeInfo);
                 if (attributeInfo instanceof AnnotationsAttribute) {
                 AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute) attributeInfo;
                
                 // note these are javassist annotations, not java ones.
                 Annotation[] annotations = annotationsAttribute.getAnnotations();
                 echo("annotations: " + Arrays.asList(annotations));
                 for (Annotation annotation : annotations) {
                 dumpAnnotation(annotation);
                 }
                 } else {
                 echo("an AttributeInfo is of class: %s - %s %n", attributeInfo.getClass(),
                 attributeInfo);
                 }
                 }
                 }
                
                 /*
                 * MethodInfo minfo = m.getMethodInfo(); AnnotationsAttribute attr =
                 * (AnnotationsAttribute)
                 * minfo.getAttribute(AnnotationsAttribute.invisibleTag); if (atr != null) {
                 * Annotation an = attr.getAnnotation("Author"); String s =
                 * ((StringMemberValue)a.getMemberValue("name")).getValue();
                 * System.out.println("@Author(name=" + s + ")"); }
                 */
                 void dumpAnnotation(Annotation a) {
                 echo("\n++annotation: " + a + " of class " + a.getClass());
                 echo("interfaces: " + Arrays.asList(a.getClass().getInterfaces()));
                 echo("typename:" + a.getTypeName());
                
                 Set<String> memberNames = (Set<String>) a.getMemberNames();
                 echo("memberNames: " + memberNames);
                
                 if (memberNames != null) {
                 for (String memberName : memberNames) {
                 echo("member " + memberName);
                 }
                 }
                
                 if (a.getTypeName().equals("ProblemTest$Author")) {
                 echo("We have found the Author annotation, but cannot look at the members!");
                
                 echo("memberValue-name: " + a.getMemberValue("name"));
                
                 StringMemberValue stringMemberValue = ((StringMemberValue) a.getMemberValue("name"));
                 echo("stringMemberValue: " + stringMemberValue);
                
                 if (stringMemberValue != null) {
                 String nameValue = stringMemberValue.getValue();
                 echo("@Author(name=" + nameValue + ")");
                 }
                
                 assertNotNull("On Author annotation, memberNames is null!", memberNames);
                 }
                 }
                
                 void echo(String format, Object... objects) {
                 System.out.format(format, objects);
                 }
                
                 void echo(String line) {
                 echo("%s%n", line);
                 }
                
                 public static void main(String[] args) {
                 TestRunner.run(ProblemTest.class);
                 }
                }
                
                
                


                • 5. Re: reading java 5.0 annotations
                  Shigeru Chiba Expert

                  Maybe that is because the default value is not included in each
                  class file. (I'm not sure)

                  public @interface Author {
                  
                   String name() default "me";
                  
                   int age() default 999;
                   }


                  Won't you try to read the annotations of the Author interface?

                  Regards,

                  Chiba


                  • 6. Re: reading java 5.0 annotations
                    benedict Newbie

                    Thankyou. As soon as I stop using default values, all is well!
                    So this presumably means that if I want all the effective annotations, I have to walk the superclasses to get inherited annotations, and access the annotation declaration to get the defaults.

                    Perhaps the API could include some utility methods that would do this for one. There would seem to be two dimensions of generality: inherited annotations from superclasses, and 'inherited' data values from default values.

                    Perhaps something like:


                    getDeclaredAnnotations // just the actual annotations with actual data
                    // no inherited ones, no default values

                    getEffectiveAnnotations // fills in defaults, but no inherited ones

                    getAllAnnotations // includes inherited annotations, no default values

                    getAllEffectiveAnnotations // includes inherited annotations ,
                    and fills in default values



                    What do you think?

                    Benedict

                    • 7. Re: reading java 5.0 annotations
                      benedict Newbie

                      >Maybe that is because the default value is not included in each
                      >class file. (I'm not sure)

                      I've just discovered in
                      http://java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat.pdf

                      stuff about an AnnotationDefault attribute which sounds what I need.
                      It says it contains default values for annottions, but if it did, each class would have the defaults bound in at compile time, whereas I had understood that recompiling the annotation led to each class getting the latest defaults.

                      Does it really contain the default values? If so, is there any way to get at it?

                      Many thanks,

                      Benedict

                      • 8. Re: reading java 5.0 annotations
                        Shigeru Chiba Expert

                        Yes, the default value is available from AnnotationDefault
                        attributes. Thank you for your information.

                        I added the support of AnnotationDefault attributes to Javassist.
                        It is available from CVS HEAD or Branch_Javassist_3_0.

                        Chiba