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

Reply via email to