This patch adds the 1.5 feature to replace the missing RMI stub classes by the dynamically created proxy stubs. Our Proxy is working great with jamvm.

The stub classes that were previously generated by the RMIC compiler, are no longer required, unless for research stubs or backward compatibility.

Removing the need to generate and compile the RMI stubs reduces the build time. This is important for the large projects that extensively use RMI.

The proxy stubs are created (and work) with the several RMI applications I have. The proxy stub usage may be forced by setting java.rmi.server.ignoreStubClasses=true (except bootstrap classes).

2006-02-20  Audrius Meskauskas  <[EMAIL PROTECTED]>

   * gnu/java/rmi/server/RMIObjectInputStream.java (resolveProxyClass):
   Expect that proxy interfaces may have different class loaders.
   * gnu/java/rmi/server/UnicastServerRef.java: Rewritten.
   * java/rmi/registry/Registry.java,
   * java/rmi/server/UnicastRemoteObject.java:
   Documented about proxy stubs.
   * gnu/java/rmi/server/CombinedClassLoader.java,
   java/rmi/server/RemoteObjectInvocationHandler.java: New files.
   * NEWS: Added entry.
Index: NEWS
===================================================================
RCS file: /sources/classpath/classpath/NEWS,v
retrieving revision 1.112
diff -u -r1.112 NEWS
--- NEWS	16 Feb 2006 23:22:53 -0000	1.112
+++ NEWS	20 Feb 2006 08:13:14 -0000
@@ -7,9 +7,7 @@
   `java.security.' `javax.crypto,' and `javax.net.ssl' packages, and
   are service providers implementing the underlying algorithms.
  
-* The new folder tools now contains GIOP and RMI tools, providing necessary
-  support for development using org.omg.*, javax.rmi.CORBA.* and java.rmi.* 
-  packages. The toll set includes GIOP and RMI stub and tie source code
+* The new folder tools includes GIOP and RMI stub and tie source code
   generators, IOR parser and both transient and persistent GIOP naming services. 
 
 * RELAX NG pluggable XML schema datatype library API and an implementation
@@ -17,6 +15,10 @@
   
 * JTable columns are rearrangeable and resizeable with mouse.
 
+* Added experimental support for dynamic creation of the RMI stubs using proxy
+  classes. The rmic compiler is no longer required (unless for research
+  and specific stubs).
+
 New in release 0.20 (Jan 13, 2006)
 
 * New StAX pull parser and SAX-over-StAX driver. Lots of DOM, SAX/StAX,
Index: gnu/java/rmi/server/RMIObjectInputStream.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/rmi/server/RMIObjectInputStream.java,v
retrieving revision 1.9
diff -u -r1.9 RMIObjectInputStream.java
--- gnu/java/rmi/server/RMIObjectInputStream.java	14 Sep 2005 16:52:07 -0000	1.9
+++ gnu/java/rmi/server/RMIObjectInputStream.java	20 Feb 2006 08:13:20 -0000
@@ -46,6 +46,7 @@
 import java.lang.reflect.Proxy;
 import java.net.MalformedURLException;
 import java.rmi.server.RMIClassLoader;
+import java.util.ArrayList;
 
 public class RMIObjectInputStream
 	extends ObjectInputStream {
@@ -76,28 +77,51 @@
     return readObject();
 }
 	
-protected Class resolveProxyClass(String intfs[])
-        throws IOException, ClassNotFoundException
-{
-    String annotation = (String)getAnnotation();
-	
+
+  protected Class resolveProxyClass(String intfs[]) throws IOException,
+      ClassNotFoundException
+  {
+    String annotation = (String) getAnnotation();
+
     Class clss[] = new Class[intfs.length];
-    if(annotation == null)
-        clss[0] = RMIClassLoader.loadClass(intfs[0]);
-    else
-        clss[0] = RMIClassLoader.loadClass(annotation, intfs[0]);
-    
-    //assume all interfaces can be loaded by the same classloader
-    ClassLoader loader = clss[0].getClassLoader();
+
     for (int i = 0; i < intfs.length; i++)
-        clss[i] = Class.forName(intfs[i], false, loader);
-        
-    try {
-    return Proxy.getProxyClass(loader, clss);
-	} catch (IllegalArgumentException e) {
-	    throw new ClassNotFoundException(null, e);
-	}  
-}
+      {
+        if (annotation == null)
+          clss[i] = RMIClassLoader.loadClass(intfs[i]);
+        else
+          clss[i] = RMIClassLoader.loadClass(annotation, intfs[i]);
+      }
+    
+    ClassLoader loader;
+    
+    if (clss.length > 0)
+      {
+        // Chain all class loaders (they may differ).
+        ArrayList loaders = new ArrayList(intfs.length);
+        ClassLoader cx;
+        for (int i = 0; i < clss.length; i++)
+          {
+            cx = clss[i].getClassLoader();
+            if (!loaders.contains(cx))
+              {
+                loaders.add(0, cx);
+              }
+          }
+        loader = new CombinedClassLoader(loaders);
+      }
+    else
+       loader = ClassLoader.getSystemClassLoader();
+
+    try
+      {
+        return Proxy.getProxyClass(loader, clss);
+      }
+    catch (IllegalArgumentException e)
+      {
+        throw new ClassNotFoundException(null, e);
+      }
+  }
 
 protected Object readValue(Class valueClass) throws IOException, ClassNotFoundException {
     if(valueClass.isPrimitive()){
Index: gnu/java/rmi/server/UnicastServerRef.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/rmi/server/UnicastServerRef.java,v
retrieving revision 1.15
diff -u -r1.15 UnicastServerRef.java
--- gnu/java/rmi/server/UnicastServerRef.java	19 Feb 2006 13:54:56 -0000	1.15
+++ gnu/java/rmi/server/UnicastServerRef.java	20 Feb 2006 08:13:21 -0000
@@ -43,36 +43,54 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.rmi.Remote;
 import java.rmi.RemoteException;
 import java.rmi.server.ObjID;
 import java.rmi.server.RMIServerSocketFactory;
+import java.rmi.server.RemoteObjectInvocationHandler;
 import java.rmi.server.RemoteRef;
 import java.rmi.server.RemoteServer;
 import java.rmi.server.RemoteStub;
 import java.rmi.server.ServerNotActiveException;
-import java.rmi.server.ServerRef;
 import java.rmi.server.Skeleton;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 
 public class UnicastServerRef
     extends UnicastRef
-    implements ServerRef
-{ // SHOULD implement ServerRef
+{ 
 
   /**
    * Use GNU Classpath v 0.20 SVUID for interoperability
    */
   private static final long serialVersionUID = - 5585608108300801246L;
-
-  final static private Class[] stubprototype = new Class[] { RemoteRef.class };
-
+  
+  /**
+   * The class array, defining parameters of the jdk 1.2 RMI stub constructor. 
+   */
+  private static final Class[] stubprototype = new Class[] { RemoteRef.class };
+  
+  /**
+   * The exported remote object itself.
+   */
   Remote myself; // save the remote object itself
-
+  
+  /**
+   * The skeleton (if any), associated with the exported remote object.
+   */
   private Skeleton skel;
-
-  private RemoteStub stub;
-
+  
+  /**
+   * The stub, associated with the exported remote object (may be proxy class).
+   */
+  private Remote stub;
+  
+  /**
+   * The method table (RMI hash code to method) of the methods of the 
+   * exported object.
+   */
   private Hashtable methods = new Hashtable();
 
   /**
@@ -88,8 +106,20 @@
     super(id);
     manager = UnicastConnectionManager.getInstance(port, ssf);
   }
-
-  public RemoteStub exportObject(Remote obj) throws RemoteException
+  
+  /**
+   * Export the object and return its remote stub. The method tries to locate
+   * existing stubs and skeletons. If this fails, the method instantiates the
+   * proxy stub class.
+   * 
+   * Stubs and skeletons are always ignored (even if present) if the 
+   * java.rmi.server.ignoreStubClasses property is set to true.
+   * 
+   * @param obj the object being exported.
+   * @return the stub (existing class or proxy) of the exported object.
+   * @throws RemoteException if the export failed due any reason
+   */
+  public Remote exportObject(Remote obj) throws RemoteException
   {
     if (myself == null)
       {
@@ -99,28 +129,36 @@
         // local call
         manager.serverobj = obj;
 
-        // Find and install the stub
-        Class cls = obj.getClass();
-        Class expCls;
-        try
+        String ignoreStubs;
+        
+        ClassLoader loader =obj.getClass().getClassLoader(); 
+        
+        // Stubs are always searched for the bootstrap classes that may have
+        // obsolete pattern and may still need also skeletons.
+        if (loader==null)
+          ignoreStubs = "false";
+        else
+          ignoreStubs = System.getProperty("java.rmi.server.ignoreStubClasses", 
+                                           "false");
+        
+        if (! ignoreStubs.equals("true"))
           {
+            // Find and install the stub
+            Class cls = obj.getClass();
+
             // where ist the _Stub? (check superclasses also)
-            expCls = findStubSkelClass(cls);
-          }
-        catch (Exception ex)
-          {
-            throw new RemoteException("can not find stubs for class: " + cls,
-                                      ex);
-          }
+            Class expCls = expCls = findStubSkelClass(cls);
 
-        stub = (RemoteStub) getHelperClass(expCls, "_Stub");
-        if (stub == null)
-          {
-            throw new RemoteException("failed to export: " + cls);
+            if (expCls != null)
+              {
+                stub = (RemoteStub) getHelperClass(expCls, "_Stub");
+                // Find and install the skeleton (if there is one)
+                skel = (Skeleton) getHelperClass(expCls, "_Skel");
+              }
           }
 
-        // Find and install the skeleton (if there is one)
-        skel = (Skeleton) getHelperClass(expCls, "_Skel");
+        if (stub == null)
+          stub = createProxyStub(obj.getClass(), this);
 
         // Build hash of methods which may be called.
         buildMethodHash(obj.getClass(), true);
@@ -129,21 +167,27 @@
         UnicastServer.exportObject(this);
       }
 
-    return (stub);
-  }
-
-  public RemoteStub exportObject(Remote remote, Object obj)
-      throws RemoteException
-  {
-    // FIX ME
-    return exportObject(remote);
+    return stub;
   }
-
-  public RemoteStub getStub()
+  
+  /**
+   * Get the stub (actual class or proxy) of the exported remote object.
+   * 
+   * @return the remote stub (null if exportObject has not been called).
+   */
+  public Remote getStub()
   {
     return stub;
   }
-
+  
+  /**
+   * Unexport the object (remove methods from the method hashcode table 
+   * and call UnicastServer.unexportObject.
+   * 
+   * @param obj the object being unexported
+   * @param force passed to the UnicastServer.unexportObject.
+   * @return value, returned by the UnicastServer.unexportObject.
+   */
   public boolean unexportObject(Remote obj, boolean force)
   {
     // Remove all hashes of methods which may be called.
@@ -152,10 +196,13 @@
   }
 
   /**
+   * Return the class in the hierarchy for that the stub class is defined.
    * The Subs/Skels might not there for the actual class, but maybe for one of
    * the superclasses.
+   * 
+   * @return the class having stub defined, null if none.
    */
-  private Class findStubSkelClass(Class startCls) throws Exception
+  private Class findStubSkelClass(Class startCls)
   {
     Class cls = startCls;
 
@@ -175,15 +222,26 @@
             if (superCls == null
                 || superCls == java.rmi.server.UnicastRemoteObject.class)
               {
-                throw new Exception("Neither " + startCls
-                                    + " nor one of their superclasses (like"
-                                    + cls + ")" + " has a _Stub");
+                return null;
               }
             cls = superCls;
           }
       }
   }
-
+  
+  /**
+   * Get the helper (assisting) class with the given type. 
+   * 
+   * @param cls the class, for that the helper class is requested. This class
+   * and the requested helper class must share the same class loader.
+   * 
+   * @param type the type of the assisting helper. The only currently supported
+   * non deprecated value is "_Stub" (load jdk 1.1 or 1.2 RMI stub). Another
+   * (deprecated) value is "_Skel" (load skeleton).
+   * 
+   * @return the instantiated instance of the helper class or null if the
+   * helper class cannot be found or instantiated.
+   */
   private Object getHelperClass(Class cls, String type)
   {
     try
@@ -242,7 +300,16 @@
   {
     return RemoteServer.getClientHost();
   }
-
+  
+  /**
+   * Build the method has code table and put it into [EMAIL PROTECTED] #methods}
+   * (mapping RMI hashcode tos method). The same method is used to remove
+   * the table.
+   * 
+   * @param cls the class for that the method table is built. 
+   * @param build if true, the class methods are added to the table. If 
+   * false, they are removed from the table.
+   */
   private void buildMethodHash(Class cls, boolean build)
   {
     Method[] meths = cls.getMethods();
@@ -357,6 +424,52 @@
           return (call.returnValue());
       }
   }
+  
+  /**
+   * Create the 1.2 proxy stub in the case when the pre-generated stub is not
+   * available of the system is explicitly instructed to use proxy stubs.
+   * 
+   * @param stubFor the class for that the proxy class must be constructed.
+   * @param reference the remote reference, used to find the given object
+   * 
+   * @return the applicable proxy stub.
+   * 
+   * @author Audrius Meskauskas ([EMAIL PROTECTED])
+   */
+  Remote createProxyStub(Class stubFor, RemoteRef reference)
+  {
+    // Collect all interfaces, implemented by stubFor and derived from
+    // Remote (also Remote itself):
+    HashSet interfaces = new HashSet();
+    Class c = stubFor;
+    Class[] intfs;
+
+    while (c != null)
+      {
+        intfs = c.getInterfaces();
+        for (int i = 0; i < intfs.length; i++)
+          {
+            if (Remote.class.isAssignableFrom(intfs[i]))
+              interfaces.add(intfs[i]);
+          }
+        c = c.getSuperclass();
+      }
+
+    intfs = new Class[interfaces.size()];
+    Iterator it = interfaces.iterator();
+
+    for (int i = 0; i < intfs.length; i++)
+      intfs[i] = (Class) it.next();
+    
+    RemoteObjectInvocationHandler handler = 
+      new RemoteObjectInvocationHandler(reference);
+    
+    Object proxy = 
+      Proxy.newProxyInstance(stubFor.getClassLoader(), intfs, handler);
+
+    return (Remote) proxy;
+  }
+  
 
 }
 
Index: java/rmi/registry/Registry.java
===================================================================
RCS file: /sources/classpath/classpath/java/rmi/registry/Registry.java,v
retrieving revision 1.7
diff -u -r1.7 Registry.java
--- java/rmi/registry/Registry.java	2 Jul 2005 20:32:40 -0000	1.7
+++ java/rmi/registry/Registry.java	20 Feb 2006 08:13:33 -0000
@@ -47,7 +47,29 @@
 public interface Registry extends Remote
 {
   int REGISTRY_PORT = 1099;
-
+  
+  /**
+   * Find and return the reference to the object that was previously bound
+   * to the registry by this name. For remote objects, this method returns
+   * the stub instances, containing the code for remote invocations.
+   * 
+   * Since jdk 1.5 this method does not longer require the stub class 
+   * (nameImpl_Stub) to be present. If such class is not found, the stub is 
+   * replaced by the dynamically constructed proxy class. No attempt to find 
+   * and load the stubs is made if the system property 
+   * java.rmi.server.ignoreStubClasses is set to true (set to reduce the 
+   * starting time if the stubs are surely not present and exclusively 1.2
+   * RMI is used).
+   * 
+   * @param name the name of the object
+   * 
+   * @return the reference to that object on that it is possible to invoke
+   * the (usually remote) object methods.
+   * 
+   * @throws RemoteException
+   * @throws NotBoundException
+   * @throws AccessException
+   */
   Remote lookup(String name)
     throws RemoteException, NotBoundException, AccessException;
 
Index: java/rmi/server/UnicastRemoteObject.java
===================================================================
RCS file: /sources/classpath/classpath/java/rmi/server/UnicastRemoteObject.java,v
retrieving revision 1.13
diff -u -r1.13 UnicastRemoteObject.java
--- java/rmi/server/UnicastRemoteObject.java	19 Feb 2006 13:56:26 -0000	1.13
+++ java/rmi/server/UnicastRemoteObject.java	20 Feb 2006 08:13:33 -0000
@@ -125,14 +125,14 @@
     //this.csf = csf;
     //this.ssf = ssf;
     this.ref = new UnicastServerRef(new ObjID(), port, serverSocketFactory);
-    exportObject(this);
+    exportObject(this, port);
   }
 
   protected UnicastRemoteObject(RemoteRef ref)
     throws RemoteException
   {
 	super((UnicastServerRef) ref);
-	exportObject(this);
+	exportObject(this, 0);
   }
 
   public Object clone()
@@ -143,7 +143,12 @@
   
   /**
    * Export object, making it available for the remote calls at the 
-   * anonymous port.
+   * anonymous port. 
+   * 
+   * This method returns the instance of the abstract class, not an interface.
+   * Hence it will not work with the proxy stubs that are supported since
+   * jdk 1.5 (such stubs cannot be derived from the RemoteStub). Only use
+   * this method if you are sure that the stub class will be accessible.
    * 
    * @param obj the object being exported.
    * 
@@ -161,6 +166,13 @@
    * Export object, making it available for the remote calls at the 
    * specified port.
    * 
+   * Since jdk 1.5 this method does not longer require the stub class to be
+   * present. If such class is not found, the stub is replaced by the 
+   * dynamically constructed proxy class. No attempt to find and load the stubs
+   * is made if the system property java.rmi.server.ignoreStubClasses
+   * is set to true (set to reduce the starting time if the stubs are 
+   * surely not present and exclusively 1.2 RMI is used).
+   * 
    * @param obj the object being exported.
    * @param port the remote object port
    * 
@@ -178,6 +190,13 @@
    * Create and export the new remote object, making it available at the
    * given port, using sockets, produced by the specified factories.
    * 
+   * Since jdk 1.5 this method does not longer require the stub class to be
+   * present. If such class is not found, the stub is replaced by the 
+   * dynamically constructed proxy class. No attempt to find and load the stubs
+   * is made if the system property java.rmi.server.ignoreStubClasses
+   * is set to true (set to reduce the starting time if the stubs are 
+   * surely not present and exclusively 1.2 RMI is used).
+   * 
    * @param port the port, on that the object should become available.
    * Zero means anonymous port.
    * 
Index: gnu/java/rmi/server/CombinedClassLoader.java
===================================================================
RCS file: gnu/java/rmi/server/CombinedClassLoader.java
diff -N gnu/java/rmi/server/CombinedClassLoader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/rmi/server/CombinedClassLoader.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,149 @@
+/* CombinedClassLoader.java -- Multiple class loader support for proxy.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.rmi.server;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * This class supports the multiple class loaders to load the resources. It is
+ * used for constructing proxy classes that implement interfaces, loaded by
+ * the several different class loaders.
+ * 
+ * @author Audrius Meskauskas ([EMAIL PROTECTED]) 
+ */
+public class CombinedClassLoader extends ClassLoader
+{
+  /**
+   * The class loader array.
+   */
+  ClassLoader[] loaders;
+  
+  /**
+   * Create a new combined class loader that uses the given collection of
+   * loaders to load the classes and resources. The loader order is equal to
+   * the order, returned by the collection interator. The duplicate loaders
+   * are discarded and the system class loader is added as the last loader.
+   * 
+   * @param a_loaders the loadery collection (may contain duplicate instances
+   * that will be discarded.
+   */
+  public CombinedClassLoader(Collection a_loaders)
+  {
+    ArrayList sLoaders = new ArrayList(a_loaders.size());
+    
+    Iterator iter = a_loaders.iterator();
+    Object cl;
+    while (iter.hasNext())
+      {
+        cl = iter.next();
+        if (!sLoaders.contains(cl))
+          sLoaders.add(iter.next());
+      }
+    
+    loaders = new ClassLoader[sLoaders.size()];
+
+    for (int i = 0; i < loaders.length; i++)
+      loaders[i] = (ClassLoader) sLoaders.get(i);
+  }
+
+  /**
+   * Find the class with the given name.
+   */
+  protected Class findClass(String name) throws ClassNotFoundException
+  {
+    for (int i = 0; i < loaders.length; i++)
+      {
+        try
+          {
+            return findClass(name);
+          }
+        catch (ClassNotFoundException e)
+          {
+            // try another.
+          }
+      }
+    return super.findClass(name);
+  }
+
+  /**
+   * Find the library with the given name
+   */
+  protected String findLibrary(String name)
+  {
+    for (int i = 0; i < loaders.length; i++)
+      {
+        String lib = findLibrary(name);
+        if (lib != null)
+          return lib;
+      }
+    return super.findLibrary(name);
+  }
+
+  /**
+   * Find resource with the given name.
+   */
+  protected URL findResource(String name)
+  {
+    for (int i = 0; i < loaders.length; i++)
+      {
+        URL resource = findResource(name);
+        if (resource != null)
+          return resource;
+      }
+    return super.findResource(name);
+  }
+
+  /**
+   * Find resources with the given name.
+   */
+  protected Enumeration findResources(String name) throws IOException
+  {
+    for (int i = 0; i < loaders.length; i++)
+      {
+        Enumeration resource = findResources(name);
+        if (resource != null)
+          return resource;
+      }
+    return super.findResources(name);  }
+}
Index: java/rmi/server/RemoteObjectInvocationHandler.java
===================================================================
RCS file: java/rmi/server/RemoteObjectInvocationHandler.java
diff -N java/rmi/server/RemoteObjectInvocationHandler.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ java/rmi/server/RemoteObjectInvocationHandler.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,221 @@
+/* RemoteObjectInvocationHandler.java -- RMI stub replacement.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library.  Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module.  An independent module is a module which is not derived from
+ or based on this library.  If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so.  If you do not wish to do so, delete this
+ exception statement from your version. */
+
+
+package java.rmi.server;
+
+import gnu.java.rmi.server.RMIHashes;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.UnexpectedException;
+import java.rmi.registry.Registry;
+import java.rmi.server.RemoteObject;
+import java.rmi.server.RemoteRef;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.Hashtable;
+
+/**
+ * Together with dynamic proxy instance, this class replaces the generated RMI
+ * stub (*_Stub) classes that (following 1.5 specification) should be no longer
+ * required. It is unusual to use the instances of this class directly in the
+ * user program. Such instances are automatically created and returned by
+ * [EMAIL PROTECTED] Registry} or [EMAIL PROTECTED] UnicastRemoteObject} methods if the remote
+ * reference is known but the corresponding stub class is not accessible.
+ * 
+ * @see Registry#lookup
+ *
+ * @author Audrius Meskauskas ([EMAIL PROTECTED])
+ */
+public class RemoteObjectInvocationHandler extends RemoteObject implements
+    InvocationHandler, Remote, Serializable
+{
+  /**
+   * Use the jdk 1.5 SUID for interoperability.
+   */
+  static final long serialVersionUID = 2L;
+  
+  /**
+   * The RMI method hash codes, computed once as described in the section 8.3
+   * of the Java Remote Method Invocation (RMI) Specification.
+   */
+  static Hashtable methodHashCodes = new Hashtable();
+  
+  /**
+   * The empty class array to define parameters of .hashCode and .toString.
+   */
+  static final Class[] noArgsC = new Class[0];
+  
+  /**
+   * The class array to define parameters of .equals
+   */
+  static final Class[] anObjectC = new Class[] { Object.class };
+  
+  /**
+   * Construct the remote invocation handler that forwards calls to the given
+   * remote object.
+   * 
+   * @param reference the reference to the remote object where the method
+   * calls should be forwarded.
+   */
+  public RemoteObjectInvocationHandler(RemoteRef reference)
+  {
+    super(reference);
+  }
+
+  /**
+   * Invoke the remote method. When the known method is invoked on a created RMI
+   * stub proxy class, the call is delivered to this method and then transferred
+   * to the [EMAIL PROTECTED] RemoteRef#invoke(Remote, Method, Object[], long)} of the
+   * remote reference that was passed in constructor. The methods are handled as
+   * following:
+   * <ul>
+   * <li> The toString() method is delegated to the passed proxy instance.</li>
+   * <li>The .equals method only returns true if the passed object is an
+   * instance of proxy class and its invocation handler is equal to this
+   * invocation handles.</li>
+   * <li>The .hashCode returns the hashCode of this invocation handler (if the.</li>
+   * <li>All other methods are converted to remote calls and forwarded to the
+   * remote reference. </li>
+   * </ul>
+   * 
+   * @param proxyInstance
+   *          the instance of the proxy stub
+   * @param method
+   *          the method being invoked
+   * @param parameters
+   *          the method parameters
+   * @return the method return value, returned by RemoteRef.invoke
+   * @throws IllegalAccessException
+   *           if the passed proxy instance does not implement Remote interface.
+   * @throws UnexpectedException
+   *           if remote call throws some exception, not listed in the
+   *           <code>throws</code> clause of the method being called.
+   * @throws Throwable
+   *           that is thrown by remote call, if that exception is listend in
+   *           the <code>throws</code> clause of the method being called.
+   */
+  public Object invoke(Object proxyInstance, Method method, Object[] parameters)
+      throws Throwable
+  {
+    if (!(proxyInstance instanceof Remote))
+      {
+        String name = proxyInstance == null ? "null"
+                                           : proxyInstance.getClass().getName();
+        throw new IllegalAccessException(name + " does not implement "
+                                         + Remote.class.getName());
+      }
+
+    String name = method.getName();
+    switch (name.charAt(0))
+      {
+      case 'e':
+        if (parameters.length == 1 && name.equals("equals")
+            && method.getParameterTypes()[0].equals(Object.class))
+          {
+            if (parameters[0] instanceof Proxy)
+              {
+                Object handler = Proxy.getInvocationHandler(parameters[0]);
+                if (handler == null)
+                  return Boolean.FALSE;
+                else
+                  return handler.equals(this) ? Boolean.TRUE : Boolean.FALSE;
+              }
+            else
+              return Boolean.FALSE;
+          }
+        break;
+      case 'h':
+        if (parameters.length == 0 && name.equals("hashCode"))
+          {
+            int hashC = Proxy.getInvocationHandler(proxyInstance).hashCode();
+            return new Integer(hashC);
+          }
+        break;
+      case 't':
+        if (parameters.length == 0 && name.equals("toString"))
+          return proxyInstance.toString();
+        break;
+      default:
+        break;
+      }
+
+    Long hash = (Long) methodHashCodes.get(method);
+    if (hash == null)
+      {
+        hash = new Long(RMIHashes.getMethodHash(method));
+        methodHashCodes.put(method, hash);
+      }
+
+    try
+      {
+        return getRef().invoke((Remote) proxyInstance, method, parameters,
+                               hash.longValue());
+      }
+    catch (RuntimeException exception)
+      {
+        // RuntimeException is always supported.
+        throw exception;
+      }
+    catch (RemoteException exception)
+      {
+        // All remote methods can throw RemoteException.
+        throw exception;
+      }
+    catch (Error exception)
+      {
+        throw exception;
+      }
+    catch (Exception exception)
+      {
+        Class[] exceptions = method.getExceptionTypes();
+        Class exceptionClass = exception.getClass();
+
+        for (int i = 0; i < exceptions.length; i++)
+          {
+            if (exceptions[i].equals(exceptionClass))
+              throw exception;
+          }
+        throw new UnexpectedException(method.getName(), exception);
+      }
+  }
+
+}

Reply via email to