Re: (reflect) Accessing members of inner annotations types
Hi, Many thanks for the investigation and suggested workarounds. Bean Validation recommends to declare an inner @List annotation to specify several constraints of the same type on a given element, but its ok to deviate from that pattern in the rare cases of package-private constraint types. > I can file a bug/rfe for this. I think that would be great; Taking the referenced types into account when determining the package for generated proxy classes sounds reasonable to me. Thanks again, --Gunnar 2014/1/8 Joel Borggren-Franck > On 2014-01-08, Peter Levart wrote: > > On 01/08/2014 01:00 PM, Joel Borggren-Franck wrote: > > >> > > >Perhaps an alternative would be to check if the interface a proxy is > > >proxying is nested. In that case use the outermost interface's access > > >level for the package calculation. > > > > > >This would probably lead to a few more proxies being generated into the > > >"real" package compared to your proposal. I don't know if that is ok or > > >not. > > > The nested public interface need not be referencing the outer > > class/interface. In this case, proxy class can be generated in > > com.sun.proxy. > > Yes, this is the reason for my remark about more proxies being generated > into the real package. I'm not sure this is a problem, however it is > moot considering you second point. > > > More importantly, even if the outer interface/class > > was public, the nested interface could be referencing some other > > package-private type. For example: > > > > Ugh. Acknowledged. (This feels like a really bad practice but that > doesn't matter here.) > > cheers > /Joel >
Re: (reflect) Accessing members of inner annotations types
On 2014-01-08, Peter Levart wrote: > On 01/08/2014 01:00 PM, Joel Borggren-Franck wrote: > >> > >Perhaps an alternative would be to check if the interface a proxy is > >proxying is nested. In that case use the outermost interface's access > >level for the package calculation. > > > >This would probably lead to a few more proxies being generated into the > >"real" package compared to your proposal. I don't know if that is ok or > >not. > The nested public interface need not be referencing the outer > class/interface. In this case, proxy class can be generated in > com.sun.proxy. Yes, this is the reason for my remark about more proxies being generated into the real package. I'm not sure this is a problem, however it is moot considering you second point. > More importantly, even if the outer interface/class > was public, the nested interface could be referencing some other > package-private type. For example: > Ugh. Acknowledged. (This feels like a really bad practice but that doesn't matter here.) cheers /Joel
Re: (reflect) Accessing members of inner annotations types
On 01/08/2014 01:00 PM, Joel Borggren-Franck wrote: Hi Peter, On 2014-01-03, Peter Levart wrote: On 01/03/2014 03:52 PM, Peter Levart wrote: 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! Investigating this further, I found that the declaration itself is not problematic. It's the code in the implemented proxy method that tries to access the package-private Named class. Here's how the bytecode looks like for such proxy method: ... I think the error is thrown at the "checkcast" bytecode. The improvement suggested still holds. If the proxy class was generated in the specific package, error would not be thrown. But the requirement to take into account all implemented interfaces and all types encountered in the interface method signatures to calculate the package of proxy class it too strict. Only implemented interfaces and return types of all interface methods need to be taken into consideration. Here's an example of bytecode that illustrates how method parameters are passed to InvocationHandler: Perhaps an alternative would be to check if the interface a proxy is proxying is nested. In that case use the outermost interface's access level for the package calculation. This would probably lead to a few more proxies being generated into the "real" package compared to your proposal. I don't know if that is ok or not. Hi Joel, The nested public interface need not be referencing the outer class/interface. In this case, proxy class can be generated in com.sun.proxy. More importantly, even if the outer interface/class was public, the nested interface could be referencing some other package-private type. For example: public class Outer { @Retention(RUNTIME) @interface PkgPrivate {} @Retention(RUNTIME) public @interface Public { PkgPrivate value(); } @Public(@PkgPrivate) public void m() {} } I think the criteria for package selection should be closely tied to requirements of the generated proxy class code. And the generated code references the return type of the method. Regards, Peter cheers /Joel
Re: (reflect) Accessing members of inner annotations types
Hi Peter, On 2014-01-03, Peter Levart wrote: > On 01/03/2014 03:52 PM, Peter Levart wrote: > >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! > > Investigating this further, I found that the declaration itself is > not problematic. It's the code in the implemented proxy method that > tries to access the package-private Named class. Here's how the > bytecode looks like for such proxy method: > ... I think the error is thrown at the "checkcast" bytecode. The > improvement suggested still holds. If the proxy class was generated > in the specific package, error would not be thrown. But the > requirement to take into account all implemented interfaces and all > types encountered in the interface method signatures to calculate > the package of proxy class it too strict. Only implemented > interfaces and return types of all interface methods need to be > taken into consideration. Here's an example of bytecode that > illustrates how method parameters are passed to InvocationHandler: > Perhaps an alternative would be to check if the interface a proxy is proxying is nested. In that case use the outermost interface's access level for the package calculation. This would probably lead to a few more proxies being generated into the "real" package compared to your proposal. I don't know if that is ok or not. cheers /Joel
Re: (reflect) Accessing members of inner annotations types
On 01/03/2014 03:52 PM, Peter Levart wrote: 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! Investigating this further, I found that the declaration itself is not problematic. It's the code in the implemented proxy method that tries to access the package-private Named class. Here's how the bytecode looks like for such proxy method: public final pkg.Named[] value() throws ; Signature: ()[Lpkg/Named; flags: ACC_PUBLIC, ACC_FINAL Code: stack=10, locals=2, args_size=1 0: aload_0 1: getfield #16 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler; 4: aload_0 5: getstatic #67 // Field m3:Ljava/lang/reflect/Method; 8: aconst_null 9: invokeinterface #28, 4 // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; *14: checkcast #69 // class "[Lpkg/Named;"* 17: areturn 18: athrow 19: astore_1 20: new #42 // class java/lang/reflect/UndeclaredThrowableException 23: dup 24: aload_1 25: invokespecial #45 // Method java/lang/reflect/UndeclaredThrowableException."":(Ljava/lang/Throwable;)V 28: athrow Exception table: fromto target type 01818 Class java/lang/Error 01818 Class java/lang/RuntimeException 01819 Class java/lang/Throwable ... I think the error is thrown at the "checkcast" bytecode. The improvement suggested still holds. If the proxy class was generated in the specific package, error would not be thrown. But the requirement to take into account all implemented interfaces and all types encountered in the interface method signatures to calculate the package of proxy class it too strict. Only implemented interfaces and return types of all interface methods need to be taken into consideration. Here's an example of bytecode that illustrates how method parameters are passed to InvocationHandler: public final void doWith(pkg.Named[]) throws ; Signature: ([Lpkg/Named;)V flags: ACC_PUBLIC, ACC_FINAL Code: stack=10, locals=3, args_size=2 0: aload_0 1: getfield #16 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler; 4: aload_0 5: getstatic #57 // Field m3:Ljava/lang/reflect/Method; 8: iconst_1 9: anewarray #22 // class java/lang/Object 12: dup 13: iconst_0 14: aload_1 15: aastore 16: invokeinterface #28, 4 // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object; 21: pop 22: return 23: athrow 24: astore_2 25: new #42 // class java/lang/reflect/UndeclaredThrowableException 28: dup 29: aload_2 30: invokespecial #45 // Method java/lang/reflect/UndeclaredThrowableException."":(Ljava/lang/Throwable;)V 33: athrow Exception table: fromto target type 02323 Class java/lang/Error 02323 Class java/lang/RuntimeException 02324 Class java/lang/Throwable ... as can be seen, no parameter types are referenced in order to wrap the parameters with Object[] array. Regards, Peter
Re: (reflect) Accessing members of inner annotations types
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
(reflect) Accessing members of inner annotations types
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