Issue 7595: No wire format defined for java.lang.reflect.Proxy classes (java2idl-rtf) Source: Oracle (Dr. Andrew Piper, andyp(at)bea.com) Nature: Uncategorized Issue Severity: Summary: Java serialization supports the serialization of proxy classes explicitly. It does this by essentially marshaling into the stream the list of interfaces that the proxy implements and on the other end calling Proxy.getProxyClass() with those interfaces. In RMI-IIOP the proxy class ends up coming across the wire with a repository ID of RMI:\U0024Proxy0:2D4A76C198E9D8DA:0000000000000000 (i.e. $Proxy) which is not a real class and cannot be loaded by the client. I think we probably need to mandate specific behaviour for proxy classes along the lines of the java serialization spec. A simple approach might be to mandate that the valuetype contain a list of repids, the first being the pseudo-repid for the proxy itself and the other being the repids of the interfaces the proxy supports. The actual data would be that of the proxy itself which is basically the InvocationHandler. Resolution: Revised Text: Actions taken: July 20, 2004: received issue Discussion: End of Annotations:===== ender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Tue, 20 Jul 2004 16:38:13 -0700 To: java2idl-rtf@omg.org From: Andy Piper Subject: No wire format defined for java.lang.reflect.Proxy classes Cc: issues@omg.org, little@bea.com, ravia@bea.com Java serialization supports the serialization of proxy classes explicitly. It does this by essentially marshaling into the stream the list of interfaces that the proxy implements and on the other end calling Proxy.getProxyClass() with those interfaces. In RMI-IIOP the proxy class ends up coming across the wire with a repository ID of RMI:\U0024Proxy0:2D4A76C198E9D8DA:0000000000000000 (i.e. $Proxy) which is not a real class and cannot be loaded by the client. I think we probably need to mandate specific behaviour for proxy classes along the lines of the java serialization spec. A simple approach might be to mandate that the valuetype contain a list of repids, the first being the pseudo-repid for the proxy itself and the other being the repids of the interfaces the proxy supports. The actual data would be that of the proxy itself which is basically the InvocationHandler. Thoughts? andy X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Tue, 20 Jul 2004 18:01:42 -0700 To: java2idl-rtf@omg.org, issues@omg.org From: Andy Piper Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Cc: little@bea.com, ravia@bea.com Here's an alternative. Mandate that IIOP implementations wrap Proxys in javax.rmi.CORBA.ProxyDesc (implementation attached). This can then do readResolve() on the remote side and create an appropriate Proxy instance. The only nasty thing about this is that it requires reflection to get at the Proxys InvocationHandler, but I guess that's probably OK. The class marshals the interfaces as Classes rather than Strings to allow the ClassDesc mechanism to provide appropriate codebase information. The implementation is very similar in spirit to ClassDesc. I tried this with our ORB and it works fine. andy X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 09:23:56 -0700 To: Bob.Scheifler@sun.com From: Andy Piper Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com At 07:05 AM 7/21/2004, Bob Scheifler wrote: The only nasty thing about this is that it requires reflection to get at the Proxys InvocationHandler That's what Proxy.getInvocationHandler is for ... D'oh! Of course, thanks - that makes thinks much simpler. The class marshals the interfaces as Classes rather than Strings to allow the ClassDesc mechanism to provide appropriate codebase information. That's contrary to how annotation and resolution of Proxy classes are handled by JRMP (and JERI), which is that the annotation of the Proxy class itself is used, and the annotations of the interfaces are not transmitted, which is consistent with normal marshalling semantics for a class (no annotations are transmitted for interfaces implemented by a class, just the annotation for the Ok, I was looking for a solution that meant a non-compliant client could still work with Proxy's sent by a compliant server. I guess we can use javax.rmi.CORBA.Util.getCodebase() to pick off the codebase and send that with the list of strings. I'm not sure whether the marshaled form should be interface names or repids. The former is obviously simpler, and since these are all interfaces (and therefore do not have any useful information in their repids) probably preferred. class itself). Loading of the Proxy class should be specified to use RMIClassLoader.loadProxyClass, which expects this form of marshalling. I don't think this is the RMI-IIOP way, or at least not right now. The portable way of loading classes is via javax.rmi.CORBA.Util and it doesn't have such an interface. I guess it would be simple enough to loop through all of the interfaces though. We could also extend Util. Pass 2 is attached. andy X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 10:16:24 -0700 To: Bob.Scheifler@sun.com From: Andy Piper Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com At 09:38 AM 7/21/2004, Bob Scheifler wrote: Loading of the Proxy class should be specified to use RMIClassLoader.loadProxyClass, which expects this form of marshalling. I don't think this is the RMI-IIOP way, or at least not right now. On the contrary, The portable way of loading classes is via javax.rmi.CORBA.Util and its existing methods are explicitly specified in terms of RMIClassLoader methods. ... in the Java 2 platform, i.e. Sun's implementation - this implementation is not mandated by the spec I don't believe (although documented), and is obviously replaceable (because we do exactly that :) and it doesn't have such an interface. So a method should be added. Right. But sigh, it would be great if we could agree on a mechanism that would work in existing ORBs. Extending Util requires changes to all ORBs (although the right thing to do long term), and its certainly not going to make Tiger ;) andy Date: Wed, 21 Jul 2004 18:58:31 +0100 From: Simon Nash Organization: IBM X-Mailer: Mozilla 4.8 [en] (Windows NT 5.0; U) X-Accept-Language: en To: Andy Piper CC: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Andy, We definitely need to solve this problem. Your proposal is a good starting point, and I have the following comments and suggestions. 1. Your proposal addresses serializing proxy instances, but not proxy classes. For proxy classes, I would suggest an approach similar to how ClassDesc is used to serialize non-proxy classes. We could introduce a new type javax.rmi.CORBA.ProxyClassDesc defined as follows: package javax.rmi.CORBA; public class ProxyClassDesc implements java.io.Serializable { public Class[] interfaces; static final long serialVersionUID = -4121260193889271640L; } The OutputStream would call Proxy.isProxyClass() to test whether classes passed in to be serialized are proxy classes, and would use ProxyClassDesc instead of ClassDesc to serialize a proxy class. The InputStream would recognize ProxyClassDesc and would create a proxy class using Proxy.getProxyClass(). There is no need for readResolve, since the code to construct the necessary Java type is in the InputStream, just as it is for ClassDesc. We need to decide which classloader should be passed in to the getProxyClass() method. The Java serialization spec for proxies says that the classloader passed in to getProxyClass() should be the first non-null class loader up the execution stack, or null if no non-null class loaders are on the stack. It also says that this classloader should be pased in to Class.forName() to deserialize the proxy interfaces. This has the virtue of consistency and guarantees that there will not be any classloader mismatches between the proxy and its implemented interfaces. In our case, we are using codebase classloaders for the proxy's interfaces, so it is possible that the proxy class's classloader will differ from the classloaders of the proxy's implemented interfaces. I don't see any problem with this, but it is different from non-IIOP deserialization. 2. For serializing proxy instances, we could use a new type javax.rmi.CORBA.ProxyDesc, defined as follows: package javax.rmi.CORBA; public class ProxyDesc implements java.io.Serializable { public Class[] interfaces; public java.lang.reflect.InvocationHandler handler; static final long serialVersionUID = 5975633239836806045L; } This is very similar to your proposal, except that the serialization and deserialization code is in the OutputStream and InputStream rather than in the datatype itself. I think this is preferable, since it avoids the need to hardwire this code into the spec. It's also consistent with how we defined ClassDesc. The OutputStream doesn't need to use reflection to get the handler out of the proxy, since java.lang.reflect.Proxy provides a method getInvocationHandler() for doing this. What do you think? Simon Andy Piper wrote: > > Here's an alternative. Mandate that IIOP implementations wrap Proxys in > javax.rmi.CORBA.ProxyDesc (implementation attached). This can then do > readResolve() on the remote side and create an appropriate Proxy instance. > The only nasty thing about this is that it requires reflection to get at > the Proxys InvocationHandler, but I guess that's probably OK. > > The class marshals the interfaces as Classes rather than Strings to allow > the ClassDesc mechanism to provide appropriate codebase information. The > implementation is very similar in spirit to ClassDesc. > > I tried this with our ORB and it works fine. > > andy > > --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- > Name: ProxyDesc.java > ProxyDesc.java Type: T File (text/java) > Encoding: base64 -- Simon C Nash IBM Distinguished Engineer Hursley Park, Winchester, UK nash@hursley.ibm.com Tel. +44-1962-815156 Fax +44-1962-818999 X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 11:19:07 -0700 To: java2idl-rtf@omg.org From: Andy Piper Subject: Fwd: Re: No wire format defined for java.lang.reflect.Proxy classes Date: Wed, 21 Jul 2004 13:52:14 -0400 From: Bob Scheifler Subject: Re: No wire format defined for java.lang.reflect.Proxy classes To: Andy Piper Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com Reply-to: Bob.Scheifler@Sun.COM X-Accept-Language: en-us, en User-Agent: Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.4) Gecko/20040414 X-PMX-Version: 4.6.0.99824, Antispam-Core: 4.6.0.99824, Antispam-Data: 2004.7.21.108183 X-OriginalArrivalTime: 21 Jul 2004 17:52:19.0751 (UTC) FILETIME=[77DE6370:01C46F4B] and its existing methods are explicitly specified in terms of RMIClassLoader methods. ... in the Java 2 platform, i.e. Sun's implementation Those two phrases are NOT synonymous. - this implementation is not mandated by the spec I don't believe (although documented), The OMG's "Java Language to IDL Mapping" mandates this for "Java 2", meaning all implementations of the Java 2 platform, not just Sun's. and is obviously replaceable (because we do exactly that :) It's not at all obvious to me that a UtilDelegate (if that's what you're referring to) is permitted to violate the specification given for Util. Right. But sigh, it would be great if we could agree on a mechanism that would work in existing ORBs. Extending Util requires changes to all ORBs (although the right thing to do long term), and its certainly not going to make Tiger ;) This issue has been around since JDK 1.3; doesn't seem like it's been too important ... - Bob X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 11:22:27 -0700 To: java2idl-rtf@omg.org From: Andy Piper Subject: Fwd: Re: No wire format defined for java.lang.reflect.Proxy classes Date: Wed, 21 Jul 2004 10:05:42 -0400 From: Bob Scheifler Subject: Re: No wire format defined for java.lang.reflect.Proxy classes To: Andy Piper Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com Reply-to: Bob.Scheifler@Sun.COM X-Accept-Language: en-us, en User-Agent: Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.4) Gecko/20040414 X-PMX-Version: 4.6.0.99824, Antispam-Core: 4.6.0.99824, Antispam-Data: 2004.7.20.108136 X-OriginalArrivalTime: 21 Jul 2004 14:06:04.0470 (UTC) FILETIME=[DC5EE560:01C46F2B] The only nasty thing about this is that it requires reflection to get at the Proxys InvocationHandler That's what Proxy.getInvocationHandler is for ... The class marshals the interfaces as Classes rather than Strings to allow the ClassDesc mechanism to provide appropriate codebase information. That's contrary to how annotation and resolution of Proxy classes are handled by JRMP (and JERI), which is that the annotation of the Proxy class itself is used, and the annotations of the interfaces are not transmitted, which is consistent with normal marshalling semantics for a class (no annotations are transmitted for interfaces implemented by a class, just the annotation for the class itself). Loading of the Proxy class should be specified to use RMIClassLoader.loadProxyClass, which expects this form of marshalling. - Bob X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 12:08:36 -0700 To: Simon Nash From: Andy Piper Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com Comments in-line: At 10:58 AM 7/21/2004, Simon Nash wrote: Andy, We definitely need to solve this problem. Your proposal is a good starting point, and I have the following comments and suggestions. 1. Your proposal addresses serializing proxy instances, but not proxy classes. For proxy classes, I would suggest an approach Good point. similar to how ClassDesc is used to serialize non-proxy classes. We could introduce a new type javax.rmi.CORBA.ProxyClassDesc defined as follows: package javax.rmi.CORBA; public class ProxyClassDesc implements java.io.Serializable { public Class[] interfaces; static final long serialVersionUID = -4121260193889271640L; } Bob rightly points out that this results in the annotation for the Proxy class not being used. I think probably therefore it should be: package javax.rmi.CORBA; public class ProxyClassDesc implements java.io.Serializable { public String[] interfaces; public String codebase; static final long serialVersionUID = -4121260193889271640L; } The OutputStream would call Proxy.isProxyClass() to test whether classes passed in to be serialized are proxy classes, and would use ProxyClassDesc instead of ClassDesc to serialize a proxy class. The InputStream would recognize ProxyClassDesc and would create a proxy class using Proxy.getProxyClass(). There is no need for readResolve, since the code to construct the necessary Java type is in the InputStream, just as it is for ClassDesc. We need to decide which classloader should be passed in to the getProxyClass() method. The Java serialization spec for proxies says that the classloader passed in to getProxyClass() should be the first non-null class loader up the execution stack, or null if no non-null class loaders are on the stack. It also says that this classloader should be pased in to Class.forName() to deserialize the proxy interfaces. This has the virtue of consistency and guarantees that there will not be any classloader mismatches between the proxy and its implemented interfaces. In our case, we are using codebase classloaders for the proxy's interfaces, so it is possible that the proxy class's classloader will differ from the classloaders of the proxy's implemented interfaces. I don't see any problem with this, but it is different from non-IIOP deserialization. Ok. This makes sense. I guess it would be nice if the default implementation did something sensible in the instance that the receiving end treats it as a normal class rather than something special. 2. For serializing proxy instances, we could use a new type javax.rmi.CORBA.ProxyDesc, defined as follows: package javax.rmi.CORBA; public class ProxyDesc implements java.io.Serializable { public Class[] interfaces; public java.lang.reflect.InvocationHandler handler; static final long serialVersionUID = 5975633239836806045L; } Why not: package javax.rmi.CORBA; public class ProxyDesc implements java.io.Serializable { public ProxyClassDesc proxyClass; public java.lang.reflect.InvocationHandler handler; static final long serialVersionUID = 5975633239836806045L; } This is very similar to your proposal, except that the serialization and deserialization code is in the OutputStream and InputStream rather than in the datatype itself. I think this is preferable, since it avoids the need to hardwire this code into the spec. It's also consistent with how we defined ClassDesc. Yeah, you are right - and then Bob and I don't have to argue about what the implementation should look like ;). In the interim I guess we (BEA) will probably augment the definitions we ship so that something sensible happens on older ORBs. The OutputStream doesn't need to use reflection to get the handler out of the proxy, since java.lang.reflect.Proxy provides a method getInvocationHandler() for doing this. Right, Bob pointed this out already. The remaining question is whether Util should be augmented to support RMIClassloader.loadProxyClass()-like semantics. Thanks andy X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Wed, 21 Jul 2004 12:17:43 -0700 To: Bob.Scheifler@sun.com From: Andy Piper Subject: Re: No wire format defined for java.lang.reflect.Proxy classes Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com At 10:52 AM 7/21/2004, Bob Scheifler wrote: and its existing methods are explicitly specified in terms of RMIClassLoader methods. ... in the Java 2 platform, i.e. Sun's implementation Those two phrases are NOT synonymous. I guess I should know better than to argue spec semantics ;) - this implementation is not mandated by the spec I don't believe (although documented), The OMG's "Java Language to IDL Mapping" mandates this for "Java 2", meaning all implementations of the Java 2 platform, not just Sun's. Well I don't see that wording anywhere in the spec (I certainly don't see "must" used anywhere related - maybe this is just an outage in the spec). I guess I read it differently. and is obviously replaceable (because we do exactly that :) It's not at all obvious to me that a UtilDelegate (if that's what you're referring to) is permitted to violate the specification given for Util. RMIClassLoader is also replaceable, so it must be true to some extent. Again the wording in the spec does not imply a mandate to me, but that may just be a spec outage. Right. But sigh, it would be great if we could agree on a mechanism that would work in existing ORBs. Extending Util requires changes to all ORBs (although the right thing to do long term), and its certainly not going to make Tiger ;) This issue has been around since JDK 1.3; doesn't seem like it's been too important ... We suddenly seem to be seeing it a lot with RMI-IIOP access to the JMX runtime in Tiger. I don't know whether that's an artefact of our implementation or yours. Certainly Simon's suggestion neatly skips round the issue. Thanks andy X-Sender: andyp@ussfex01.bea.com X-Mailer: QUALCOMM Windows Eudora Version 6.1.2.0 Date: Mon, 26 Jul 2004 08:34:43 -0700 To: Simon Nash From: Andy Piper Subject: Wire format for java.lang.reflect.Proxy classes v2 Cc: java2idl-rtf@omg.org, little@bea.com, ravia@bea.com So taking everyone's suggestions into account it seems like the following might be ok: Proxy instances will be marshalled using the following data structure: package javax.rmi.CORBA; /** * This class is used to marshal java.lang.Proxy objects over IIOP. */ public class ProxyDesc implements java.io.Serializable { /** * @serial The class names of the interfaces that the Proxy object * implements. */ public String[] interfaces; /** * @serial A space-separated list of codebase URLs. */ public String codebase; /** * @serial The Proxy's InvocationHandler instance. */ public java.lang.reflect.InvocationHandler handler; static final long serialVersionUID = 1234286961190911798L; } The Proxy class will be instantiated using the Util.loadProxyClass() method defined as follows: /** * Loads a dynamic proxy class (see {@link java.lang.reflect.Proxy}) * that implements a set of interfaces with the given names * from a codebase URL path. * *

The interfaces will be resolved similar to classes loaded via * the {@link #loadClass(String,String,ClassLoader)} method using the given * codebase. * *

This method delegates to the * {@link UtilDelegate#loadProxyClass(String,String[],ClassLoader)} * method of the provider instance, passing codebase * as the first argument, interfaces as the second argument, * and defaultLoader as the third argument. * * @param codebase the list of URLs (space-separated) to load * classes from, or null * * @param interfaces the names of the interfaces for the proxy class * to implement * * @param defaultLoader additional contextual class loader * to use, or null * * @return a dynamic proxy class that implements the named interfaces * * @throws ClassNotFoundException if a definition for one of * the named interfaces could not be found at the specified location, * or if creation of the dynamic proxy class failed (such as if * {@link java.lang.reflect.Proxy#getProxyClass(ClassLoader,Class[])} * would throw an IllegalArgumentException for the given * interface list) */ public static Class loadProxyClass(String codebase, String[] interfaces, ClassLoader loader) throws ClassNotFoundException; which shall delegate to the UtilDelegate implementation of the same method defined as follows: /** * Provides the implementation for * {@link Util#loadProxyClass(String,String[],ClassLoader)}. * * Loads a dynamic proxy class (see {@link java.lang.reflect.Proxy} * that implements a set of interfaces with the given names * from a codebase URL path, optionally using the supplied loader. * *

An implementation of this method must either return a proxy * class that implements the named interfaces or throw an exception. * * @param codebase the list of URLs (space-separated) to load * classes from, or null * * @param interfaces the names of the interfaces for the proxy class * to implement * * @return a dynamic proxy class that implements the named interfaces * * @param defaultLoader additional contextual class loader * to use, or null * * @throws ClassNotFoundException if a definition for one of * the named interfaces could not be found at the specified location, * or if creation of the dynamic proxy class failed (such as if * {@link java.lang.reflect.Proxy#getProxyClass(ClassLoader,Class[])} * would throw an IllegalArgumentException for the given * interface list) */ public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader loader) throws ClassNotFoundException; Thoughts? Would Util and UtilDelegate need to be versioned to support this? Or would we just require that ORBs that support Proxy marshalling should support the new Util functions. Thanks andy ProxyDesc.java