(reflect) Accessing members of inner annotations types

Peter Levart peter.levart at gmail.com
Fri Jan 3 14:52:42 UTC 2014


I think the problem is as follows... Annotations are implemented as Java 
dynamic Proxy classes. The generated proxy class for a package-private 
interface is created in the same package as the interface so that it has 
access to the interface. The top level interface can be package private, 
but nested interface (nested inside another interface) is by default 
public (all interface members are by default public). The consequence is 
that proxy class of an interface nested inside the interface is 
generated in com.sun.proxy package:

package pkg;

import java.lang.reflect.Proxy;

public class ProxyTest {
     public static void main(String[] args) throws Exception {
         Class<?> iClass = Proxy.getProxyClass(I.class.getClassLoader(), 
new Class[]{I.class});
         Class<?> nClass = Proxy.getProxyClass(I.class.getClassLoader(), 
new Class[]{I.N.class});
         System.out.println("iClass: " + iClass);
         System.out.println("nClass: " + nClass);
     }
}

interface I {
     interface N {}
}

prints:

iClass: class pkg.$Proxy0
nClass: class com.sun.proxy.$Proxy1


This is would be all right until such proxy class (com.sun.proxy.$Proxy1 
in our example) has to access some package-private types in some 
specific package. This happens in your Named.List annotation  
implementation class. It implements a member method with the following 
signature:

public Named[] value() {...

...where the return type is an array of a package-private type Named. 
Public class in com.sun.proxy package can not access package-private 
types in other packages!

Your best solution currently is to implement the top-level annotations 
as public, or have all package-private annotations as top-level annotations.

In future, I think the logic to calculate the package of a proxy class 
could be improved. It currently is as follows:

             String proxyPkg = null;     // package to define proxy class in
             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

             /*
              * Record the package of a non-public proxy interface so 
that the
              * proxy class will be defined in the same package.  Verify 
that
              * all non-public proxy interfaces are in the same package.
              */
             for (Class<?> intf : interfaces) {
                 int flags = intf.getModifiers();
                 if (!Modifier.isPublic(flags)) {
                     accessFlags = Modifier.FINAL;
                     String name = intf.getName();
                     int n = name.lastIndexOf('.');
                     String pkg = ((n == -1) ? "" : name.substring(0, n 
+ 1));
                     if (proxyPkg == null) {
                         proxyPkg = pkg;
                     } else if (!pkg.equals(proxyPkg)) {
                         throw new IllegalArgumentException(
                             "non-public interfaces from different 
packages");
                     }
                 }
             }

             if (proxyPkg == null) {
                 // if no non-public proxy interfaces, use com.sun.proxy 
package
                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
             }


...It could be improved to take into account not only all interfaces 
being implemented but also all types that are encountered in interface 
method signatures. If any of those types is non-public than all of them 
should be in the same package and proxy class would be generated in that 
package...

I can file a bug/rfe for this.

Regards, Peter



On 01/03/2014 02:14 PM, Gunnar Morling wrote:
> Hi,
>
> In the course of running the Bean Validation TCK on JDK 8, I'm
> investigating an issue around reflectively accessing members of annotations
> which are declared as inner type of another, package-private annotation
> type.
>
> The following shows an example:
>
>      @Retention( RetentionPolicy.RUNTIME )
>      /*package-private */ @interface Named {
>
>          @Retention( RetentionPolicy.RUNTIME )
>          /*package-private */ @interface List {
>              Named[] value();
>          }
>      }
>
> The @List annotation is used as this on a type in the same package:
>
>      public class Foo {
>
>          @Named.List( @Named )
>          public void getBar() {}
>      }
>
> I'm then trying to access the @Named annotation using reflection like this:
>
>      Annotation listAnnotation = Foo.class.getMethod( "getBar"
> ).getAnnotations()[0];
>
>      Method method = listAnnotation.getClass().getMethod( "value" );
>      method.setAccessible( true );
>
>      //fails
>      Annotation namedAnnotation = ( (Annotation[]) method.invoke(
> listAnnotation ) )[0];
>
> This is the exception I get:
>
>      IllegalAccessError: tried to access class com.example.Named from class
> com.sun.proxy.$Proxy3
>        at com.sun.proxy.$Proxy3.value(Unknown Source)
>
> Interestingly, this only occurs when the List annotation is declared as an
> inner type within the Named type; it works when the List annotation is a
> top-level package-private type (the Named annotation still being
> package-private) as well as when it is declared as an inner type within
> another package-private class.
>
> I first thought that this might be related to 8004260 ("dynamic proxy class
> should have the same Java language access as the proxy interfaces"), but
> the issue occurs on on JDK 7 as well.
>
> Is this a potential issue with the access check implementation or is
> something wrong here with the way I'm accessing the annotation member?
>
> Thanks for any help,
>
> --Gunnar




More information about the core-libs-dev mailing list