Source for java.rmi.server.RemoteObjectInvocationHandler

   1: /* RemoteObjectInvocationHandler.java -- RMI stub replacement.
   2:  Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4:  This file is part of GNU Classpath.
   5: 
   6:  GNU Classpath is free software; you can redistribute it and/or modify
   7:  it under the terms of the GNU General Public License as published by
   8:  the Free Software Foundation; either version 2, or (at your option)
   9:  any later version.
  10: 
  11:  GNU Classpath is distributed in the hope that it will be useful, but
  12:  WITHOUT ANY WARRANTY; without even the implied warranty of
  13:  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14:  General Public License for more details.
  15: 
  16:  You should have received a copy of the GNU General Public License
  17:  along with GNU Classpath; see the file COPYING.  If not, write to the
  18:  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19:  02110-1301 USA.
  20: 
  21:  Linking this library statically or dynamically with other modules is
  22:  making a combined work based on this library.  Thus, the terms and
  23:  conditions of the GNU General Public License cover the whole
  24:  combination.
  25: 
  26:  As a special exception, the copyright holders of this library give you
  27:  permission to link this library with independent modules to produce an
  28:  executable, regardless of the license terms of these independent
  29:  modules, and to copy and distribute the resulting executable under
  30:  terms of your choice, provided that you also meet, for each linked
  31:  independent module, the terms and conditions of the license of that
  32:  module.  An independent module is a module which is not derived from
  33:  or based on this library.  If you modify this library, you may extend
  34:  this exception to your version of the library, but you are not
  35:  obligated to do so.  If you do not wish to do so, delete this
  36:  exception statement from your version. */
  37: 
  38: 
  39: package java.rmi.server;
  40: 
  41: import gnu.java.rmi.server.RMIHashes;
  42: 
  43: import java.io.Serializable;
  44: import java.lang.reflect.InvocationHandler;
  45: import java.lang.reflect.Method;
  46: import java.lang.reflect.Proxy;
  47: import java.rmi.Remote;
  48: import java.rmi.RemoteException;
  49: import java.rmi.UnexpectedException;
  50: import java.rmi.registry.Registry;
  51: import java.rmi.server.RemoteObject;
  52: import java.rmi.server.RemoteRef;
  53: import java.rmi.server.UnicastRemoteObject;
  54: import java.util.Hashtable;
  55: 
  56: /**
  57:  * Together with dynamic proxy instance, this class replaces the generated RMI
  58:  * stub (*_Stub) classes that (following 1.5 specification) should be no longer
  59:  * required. It is unusual to use the instances of this class directly in the
  60:  * user program. Such instances are automatically created and returned by
  61:  * {@link Registry} or {@link UnicastRemoteObject} methods if the remote
  62:  * reference is known but the corresponding stub class is not accessible.
  63:  *
  64:  * @see Registry#lookup
  65:  *
  66:  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
  67:  */
  68: public class RemoteObjectInvocationHandler extends RemoteObject implements
  69:     InvocationHandler, Remote, Serializable
  70: {
  71:   /**
  72:    * Use the jdk 1.5 SUID for interoperability.
  73:    */
  74:   static final long serialVersionUID = 2L;
  75: 
  76:   /**
  77:    * The RMI method hash codes, computed once as described in the section 8.3
  78:    * of the Java Remote Method Invocation (RMI) Specification.
  79:    */
  80:   static Hashtable methodHashCodes = new Hashtable();
  81: 
  82:   /**
  83:    * The empty class array to define parameters of .hashCode and .toString.
  84:    */
  85:   static final Class[] noArgsC = new Class[0];
  86: 
  87:   /**
  88:    * The class array to define parameters of .equals
  89:    */
  90:   static final Class[] anObjectC = new Class[] { Object.class };
  91: 
  92:   /**
  93:    * The empty object array to replace null when no args are passed.
  94:    */
  95:   static final Object[] noArgs = new Object[0];
  96: 
  97:   /**
  98:    * Construct the remote invocation handler that forwards calls to the given
  99:    * remote object.
 100:    *
 101:    * @param reference the reference to the remote object where the method
 102:    * calls should be forwarded.
 103:    */
 104:   public RemoteObjectInvocationHandler(RemoteRef reference)
 105:   {
 106:     super(reference);
 107:   }
 108: 
 109:   /**
 110:    * Invoke the remote method. When the known method is invoked on a created RMI
 111:    * stub proxy class, the call is delivered to this method and then transferred
 112:    * to the {@link RemoteRef#invoke(Remote, Method, Object[], long)} of the
 113:    * remote reference that was passed in constructor. The methods are handled as
 114:    * following:
 115:    * <ul>
 116:    * <li> The toString() method is delegated to the passed proxy instance.</li>
 117:    * <li>The .equals method only returns true if the passed object is an
 118:    * instance of proxy class and its invocation handler is equal to this
 119:    * invocation handles.</li>
 120:    * <li>The .hashCode returns the hashCode of this invocation handler (if the.</li>
 121:    * <li>All other methods are converted to remote calls and forwarded to the
 122:    * remote reference. </li>
 123:    * </ul>
 124:    *
 125:    * @param proxyInstance
 126:    *          the instance of the proxy stub
 127:    * @param method
 128:    *          the method being invoked
 129:    * @param parameters
 130:    *          the method parameters
 131:    * @return the method return value, returned by RemoteRef.invoke
 132:    * @throws IllegalAccessException
 133:    *           if the passed proxy instance does not implement Remote interface.
 134:    * @throws UnexpectedException
 135:    *           if remote call throws some exception, not listed in the
 136:    *           <code>throws</code> clause of the method being called.
 137:    * @throws Throwable
 138:    *           that is thrown by remote call, if that exception is listend in
 139:    *           the <code>throws</code> clause of the method being called.
 140:    */
 141:   public Object invoke(Object proxyInstance, Method method, Object[] parameters)
 142:       throws Throwable
 143:   {
 144:     if (!(proxyInstance instanceof Remote))
 145:       {
 146:         String name = proxyInstance == null ? "null"
 147:                                            : proxyInstance.getClass().getName();
 148:         throw new IllegalAccessException(name + " does not implement "
 149:                                          + Remote.class.getName());
 150:       }
 151: 
 152:     if (parameters == null)
 153:       parameters = noArgs;
 154: 
 155:     String name = method.getName();
 156:     switch (name.charAt(0))
 157:       {
 158:       case 'e':
 159:         if (parameters.length == 1 && name.equals("equals")
 160:             && method.getParameterTypes()[0].equals(Object.class))
 161:           {
 162:             if (parameters[0] instanceof Proxy)
 163:               {
 164:                 Object handler = Proxy.getInvocationHandler(parameters[0]);
 165:                 if (handler == null)
 166:                   return Boolean.FALSE;
 167:                 else
 168:                   return handler.equals(this) ? Boolean.TRUE : Boolean.FALSE;
 169:               }
 170:             else
 171:               return Boolean.FALSE;
 172:           }
 173:         break;
 174:       case 'h':
 175:         if (parameters.length == 0 && name.equals("hashCode"))
 176:           {
 177:             int hashC = Proxy.getInvocationHandler(proxyInstance).hashCode();
 178:             return new Integer(hashC);
 179:           }
 180:         break;
 181:       case 't':
 182:         if (parameters.length == 0 && name.equals("toString"))
 183:           return "Proxy stub:"+ref.remoteToString();
 184:         break;
 185:       default:
 186:         break;
 187:       }
 188: 
 189:     Long hash = (Long) methodHashCodes.get(method);
 190:     if (hash == null)
 191:       {
 192:         hash = new Long(RMIHashes.getMethodHash(method));
 193:         methodHashCodes.put(method, hash);
 194:       }
 195: 
 196:     try
 197:       {
 198:         return getRef().invoke((Remote) proxyInstance, method, parameters,
 199:                                hash.longValue());
 200:       }
 201:     catch (RuntimeException exception)
 202:       {
 203:         // RuntimeException is always supported.
 204:         throw exception;
 205:       }
 206:     catch (RemoteException exception)
 207:       {
 208:         // All remote methods can throw RemoteException.
 209:         throw exception;
 210:       }
 211:     catch (Error exception)
 212:       {
 213:         throw exception;
 214:       }
 215:     catch (Exception exception)
 216:       {
 217:         Class[] exceptions = method.getExceptionTypes();
 218:         Class exceptionClass = exception.getClass();
 219: 
 220:         for (int i = 0; i < exceptions.length; i++)
 221:           {
 222:             if (exceptions[i].equals(exceptionClass))
 223:               throw exception;
 224:           }
 225:         throw new UnexpectedException(method.getName(), exception);
 226:       }
 227:   }
 228: 
 229: }