JBREFLECT - Exception processing non-public annotation
jaikiran Jan 30, 2010 4:39 PMPete has reported a failure in CDI testsuite against the AS trunk [https://jira.jboss.org/jira/browse/JBAS-7676]. The testcase involves a @Stateless annotated EJB. Apart from this annotation, the EJB class also has one other annotation. Here's how the EJB class and its related classes looks like: {code:java} package org.jboss.jsr299.tck.tests.definition.qualifier.enterprise; @Stateless public class BorderCollie extends LongHairedDog implements BorderCollieLocal { } {code} {code:java} package org.jboss.jsr299.tck.tests.definition.qualifier.enterprise; @Hairy(clipped=false) class LongHairedDog { } {code} {code:java} package org.jboss.jsr299.tck.tests.definition.qualifier.enterprise; @Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) ...//removed irrelevant stuff @interface Hairy { public abstract boolean clipped(); } {code} During annotation scanning of this EJB class, it runs into this error, which causes the EJB metadata of this class to be messed up and the deployment failing: {code:java} 2010-01-31 00:44:49,980 TRACE [org.jboss.mcann.repository.GenericAnnotationResourceVisitor] (HDScanner) Scanning class org.jboss.jsr299.tck.tests.definition.qualifier.enterprise.BorderCollie for annotations 2010-01-31 00:44:49,984 TRACE [org.jboss.mcann.repository.GenericAnnotationResourceVisitor] (HDScanner) Exception reading resource: org/jboss/jsr299/tck/tests/definition/qualifier/enterprise/BorderCollie.class: java.lang.RuntimeException: Error retrieving annotation attribute values at org.jboss.reflect.plugins.AnnotationValueFactory.createAnnotationValue(AnnotationValueFactory.java:107) at org.jboss.reflect.plugins.introspection.IntrospectionTypeInfoFactoryImpl.createAnnotationValue(IntrospectionTypeInfoFactoryImpl.java:137) at org.jboss.reflect.plugins.introspection.IntrospectionTypeInfoFactoryImpl.getAnnotations(IntrospectionTypeInfoFactoryImpl.java:130) at org.jboss.reflect.plugins.InheritableAnnotationHolder.getDeclaredAnnotations(InheritableAnnotationHolder.java:95) at org.jboss.reflect.plugins.ClassInfoImpl.getAnnotations(ClassInfoImpl.java:179) at org.jboss.reflect.plugins.AbstractAnnotatedInfo.getUnderlyingAnnotations(AbstractAnnotatedInfo.java:63) at org.jboss.mcann.repository.GenericAnnotationResourceVisitor.handleClass(GenericAnnotationResourceVisitor.java:154) ...//removed irrelevant stacktrace Caused by: java.lang.IllegalAccessException: Class org.jboss.reflect.plugins.introspection.ReflectionUtils can not access a member of class org.jboss.jsr299.tck.tests.definition.qualifier.enterprise.Hairy with modifiers "public abstract" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at org.jboss.reflect.plugins.introspection.ReflectionUtils.invoke(ReflectionUtils.java:59) at org.jboss.reflect.plugins.introspection.ReflectMethodInfoImpl.invoke(ReflectMethodInfoImpl.java:151) at org.jboss.reflect.plugins.AnnotationValueFactory.createAnnotationValue(AnnotationValueFactory.java:100) ... 60 more {code} The root cause of this issue seems to be a problem in JBREFLECT, which runs into issue trying to create annotation value information out of a non-public annotation (notice that "Hairy" in the above example is non-public). I was able to reproduce this issue with a simple testcase in JBREFLECT trunk: {code:java} Index: src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotationTestCase.java =================================================================== --- src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotationTestCase.java (revision 0) +++ src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotationTestCase.java (revision 0) @@ -0,0 +1,89 @@ +/** + * + */ +package org.jboss.test.classinfo.annotation.test; + +import java.lang.annotation.Annotation; + +import junit.framework.Test; + +import org.jboss.reflect.plugins.introspection.IntrospectionTypeInfoFactory; +import org.jboss.reflect.spi.ClassInfo; +import org.jboss.reflect.spi.TypeInfo; +import org.jboss.reflect.spi.TypeInfoFactory; +import org.jboss.test.ContainerTest; + +/** + * NonPublicAnnotationTestCase + * + * Tests that a Class annotated with a non-public annotation is + * correctly parsed for annotations. + *+ * This test is responsible the testing fix for the bug in JBREFLECT + * which causes the following exception: + *
+ *+ * Caused by: java.lang.IllegalAccessException: Class org.jboss.reflect.plugins.introspection.ReflectionUtils can not access a member of class org.jboss.test.classinfo.annotation.test.NonPublicAnnotation with modifiers "public abstract" + * at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) + * at java.lang.reflect.Method.invoke(Method.java:588) + * at org.jboss.reflect.plugins.introspection.ReflectionUtils.invoke(ReflectionUtils.java:59) + * at org.jboss.reflect.plugins.introspection.ReflectMethodInfoImpl.invoke(ReflectMethodInfoImpl.java:151) + * at org.jboss.reflect.plugins.AnnotationValueFactory.createAnnotationValue(AnnotationValueFactory.java:100) + * ... 33 more + * + *
+ * @author Jaikiran Pai + * @version $Revision: $ + */ +public class NonPublicAnnotationTestCase extends ContainerTest +{ + /** + * Constructor + * @param name + */ + public NonPublicAnnotationTestCase(String name) + { + super(name); + } + + public static Test suite() + { + return suite(NonPublicAnnotationTestCase.class); + } + + /** + * Test that the {@link NonPublicAnnotation} when applied to {@link SimpleAnnotatedClass} + * is correctly parsed and is available through {@link ClassInfo#getUnderlyingAnnotations()} + * + * @throws Exception + */ + public void testNonPublicAnnotationOnClass() throws Exception + { + ClassInfo info = getClassInfo(SimpleAnnotatedClass.class); + + System.out.println("---> Getting annotations"); + Annotation[] annotations = info.getUnderlyingAnnotations(); + assertEquals("Incorrect number of annotations found on " + SimpleAnnotatedClass.class, 1, annotations.length); + Annotation annotation = annotations[0]; + assertTrue("Incorrect annotation type found on " + SimpleAnnotatedClass.class, (annotation instanceof NonPublicAnnotation)); + NonPublicAnnotation expectedAnnotation = (NonPublicAnnotation) annotation; + assertEquals("Incorrect value found for annotation " + NonPublicAnnotation.class + + " on class " + SimpleAnnotatedClass.class, SimpleAnnotatedClass.ANNOTATION_ELEMENT_NAME, expectedAnnotation.name()); + } + + /** + * Utitlity method + * @param clazz + * @return + */ + protected ClassInfo getClassInfo(Class clazz) + { + TypeInfoFactory factory = new IntrospectionTypeInfoFactory(); + TypeInfo info = factory.getTypeInfo(clazz); + assertNotNull(info); + assertTrue(info instanceof ClassInfo); + ClassInfo cinfo = (ClassInfo) info; + getLog().debug(cinfo); + return cinfo; + } +} Index: src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotation.java =================================================================== --- src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotation.java (revision 0) +++ src/test/java/org/jboss/test/classinfo/annotation/test/NonPublicAnnotation.java (revision 0) @@ -0,0 +1,31 @@ +/** + * + */ +package org.jboss.test.classinfo.annotation.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * NonPublicAnnotation + * + * A non-public annotation. + * + * NOTE: This {@link NonPublicAnnotation}, {@link SimpleAnnotatedClass} and + * even the {@link NonPublicAnnotationTestCase} are all placed in the same package + * *intentionally*, so that this annotation is accessible for the {@link SimpleAnnotatedClass} + * as well as {@link NonPublicAnnotationTestCase} during the tests. + * + * @author Jaikiran Pai + * @version $Revision: $ + */ +@Target (ElementType.TYPE) +@Retention (RetentionPolicy.RUNTIME) +@interface NonPublicAnnotation { + + public abstract String name(); + + public abstract boolean isTest(); +} Index: src/test/java/org/jboss/test/classinfo/annotation/test/SimpleAnnotatedClass.java =================================================================== --- src/test/java/org/jboss/test/classinfo/annotation/test/SimpleAnnotatedClass.java (revision 0) +++ src/test/java/org/jboss/test/classinfo/annotation/test/SimpleAnnotatedClass.java (revision 0) @@ -0,0 +1,25 @@ +/** + * + */ +package org.jboss.test.classinfo.annotation.test; + + +/** + * SimpleAnnotatedClass + * + * A simple class used in tests + * + * NOTE: The {@link NonPublicAnnotation}, {@link SimpleAnnotatedClass} and + * even the {@link NonPublicAnnotationTestCase} are all placed in the same package + * *intentionally*, so that this annotation is accessible for the {@link SimpleAnnotatedClass} + * as well as {@link NonPublicAnnotationTestCase} during the tests. + * + * @author Jaikiran Pai + * @version $Revision: $ + */ +@NonPublicAnnotation (name=SimpleAnnotatedClass.ANNOTATION_ELEMENT_NAME, isTest=true) +public class SimpleAnnotatedClass +{ + + public static final String ANNOTATION_ELEMENT_NAME = "SomeName"; +} {code} The testcase fails with the same exception: {code:java} ------------------------------------------------------------------------------- Test set: org.jboss.test.classinfo.annotation.test.NonPublicAnnotationTestCase ------------------------------------------------------------------------------- Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.141 sec <<< FAILURE! testNonPublicAnnotationOnClass(org.jboss.test.classinfo.annotation.test.NonPublicAnnotationTestCase) Time elapsed: 0.066 sec <<< ERROR! java.lang.RuntimeException: Error retrieving annotation attribute values ...//removed irrelevant stacktrace at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009) Caused by: java.lang.IllegalAccessException: Class org.jboss.reflect.plugins.introspection.ReflectionUtils can not access a member of class org.jboss.test.classinfo.annotation.test.NonPublicAnnotation with modifiers "public abstract" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at org.jboss.reflect.plugins.introspection.ReflectionUtils.invoke(ReflectionUtils.java:59) at org.jboss.reflect.plugins.introspection.ReflectMethodInfoImpl.invoke(ReflectMethodInfoImpl.java:151) at org.jboss.reflect.plugins.AnnotationValueFactory.createAnnotationValue(AnnotationValueFactory.java:100) ... 33 more {code} Is there a way to disable Java language access check for the methods through JBREFLECT project? Or does that have to be done (selectively) through the underlying (ReflectMethodInfoImpl) impl?