(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