Source for gnu.java.rmi.server.UnicastServerRef

   1: /* UnicastServerRef.java --
   2:    Copyright (c) 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2006
   3:    Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package gnu.java.rmi.server;
  41: 
  42: import java.io.ObjectInputStream;
  43: import java.lang.reflect.Constructor;
  44: import java.lang.reflect.InvocationTargetException;
  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.server.ObjID;
  50: import java.rmi.server.RMIServerSocketFactory;
  51: import java.rmi.server.RemoteObjectInvocationHandler;
  52: import java.rmi.server.RemoteRef;
  53: import java.rmi.server.RemoteServer;
  54: import java.rmi.server.RemoteStub;
  55: import java.rmi.server.ServerNotActiveException;
  56: import java.rmi.server.Skeleton;
  57: import java.util.HashSet;
  58: import java.util.Hashtable;
  59: import java.util.Iterator;
  60: 
  61: /**
  62:  * This class connects the local, remotely available (exported) object to
  63:  * the local RMI server that accepts the remote calls.
  64:  */
  65: public class UnicastServerRef
  66:     extends UnicastRef
  67: {
  68: 
  69:   /**
  70:    * Use GNU Classpath v 0.20 SVUID for interoperability
  71:    */
  72:   private static final long serialVersionUID = - 5585608108300801246L;
  73: 
  74:   /**
  75:    * The class array, defining parameters of the jdk 1.2 RMI stub constructor.
  76:    */
  77:   private static final Class[] stubprototype = new Class[] { RemoteRef.class };
  78: 
  79:   /**
  80:    * The exported remote object itself.
  81:    */
  82:   Remote myself; // save the remote object itself
  83: 
  84:   /**
  85:    * The skeleton (if any), associated with the exported remote object.
  86:    */
  87:   protected Skeleton skel;
  88: 
  89:   /**
  90:    * The stub, associated with the exported remote object (may be proxy class).
  91:    */
  92:   protected Remote stub;
  93: 
  94:   /**
  95:    * The method table (RMI hash code to method) of the methods of the
  96:    * exported object.
  97:    */
  98:   protected Hashtable methods = new Hashtable();
  99: 
 100:   /**
 101:    * Used by serialization.
 102:    */
 103:   UnicastServerRef()
 104:   {
 105:   }
 106: 
 107:   public UnicastServerRef(ObjID id, int port, RMIServerSocketFactory ssf)
 108:       throws RemoteException
 109:   {
 110:     super(id);
 111:     manager = UnicastConnectionManager.getInstance(port, ssf);
 112:   }
 113: 
 114:   /**
 115:    * Export the object and return its remote stub. The method tries to locate
 116:    * existing stubs and skeletons. If this fails, the method instantiates the
 117:    * proxy stub class.
 118:    *
 119:    * Stubs and skeletons are always ignored (even if present) if the
 120:    * java.rmi.server.ignoreStubClasses property is set to true.
 121:    *
 122:    * @param obj the object being exported.
 123:    * @return the stub (existing class or proxy) of the exported object.
 124:    * @throws RemoteException if the export failed due any reason
 125:    */
 126:   public Remote exportObject(Remote obj) throws RemoteException
 127:   {
 128:     if (myself == null)
 129:       {
 130:         myself = obj;
 131:         // Save it to server manager, to let client calls in the same VM to
 132:         // issue local call
 133:         manager.serverobj = obj;
 134: 
 135:         String ignoreStubs;
 136: 
 137:         ClassLoader loader =obj.getClass().getClassLoader();
 138: 
 139:         // Stubs are always searched for the bootstrap classes that may have
 140:         // obsolete pattern and may still need also skeletons.
 141:         if (loader==null)
 142:           ignoreStubs = "false";
 143:         else
 144:           ignoreStubs = System.getProperty("java.rmi.server.ignoreStubClasses",
 145:                                            "false");
 146: 
 147:         if (! ignoreStubs.equals("true"))
 148:           {
 149:             // Find and install the stub
 150:             Class cls = obj.getClass();
 151: 
 152:             // where ist the _Stub? (check superclasses also)
 153:             Class expCls = findStubSkelClass(cls);
 154: 
 155:             if (expCls != null)
 156:               {
 157:                 stub = (RemoteStub) getHelperClass(expCls, "_Stub");
 158:                 // Find and install the skeleton (if there is one)
 159:                 skel = (Skeleton) getHelperClass(expCls, "_Skel");
 160:               }
 161:           }
 162: 
 163:         if (stub == null)
 164:           stub = createProxyStub(obj.getClass(), this);
 165: 
 166:         // Build hash of methods which may be called.
 167:         buildMethodHash(obj.getClass(), true);
 168: 
 169:         // Export it.
 170:         UnicastServer.exportObject(this);
 171:       }
 172: 
 173:     return stub;
 174:   }
 175: 
 176:   /**
 177:    * Get the stub (actual class or proxy) of the exported remote object.
 178:    *
 179:    * @return the remote stub (null if exportObject has not been called).
 180:    */
 181:   public Remote getStub()
 182:   {
 183:     return stub;
 184:   }
 185: 
 186:   /**
 187:    * Unexport the object (remove methods from the method hashcode table
 188:    * and call UnicastServer.unexportObject.
 189:    *
 190:    * @param obj the object being unexported
 191:    * @param force passed to the UnicastServer.unexportObject.
 192:    * @return value, returned by the UnicastServer.unexportObject.
 193:    */
 194:   public boolean unexportObject(Remote obj, boolean force)
 195:   {
 196:     // Remove all hashes of methods which may be called.
 197:     buildMethodHash(obj.getClass(), false);
 198:     return UnicastServer.unexportObject(this, force);
 199:   }
 200: 
 201:   /**
 202:    * Return the class in the hierarchy for that the stub class is defined.
 203:    * The Subs/Skels might not there for the actual class, but maybe for one of
 204:    * the superclasses.
 205:    *
 206:    * @return the class having stub defined, null if none.
 207:    */
 208:   protected Class findStubSkelClass(Class startCls)
 209:   {
 210:     Class cls = startCls;
 211: 
 212:     while (true)
 213:       {
 214:         try
 215:           {
 216:             String stubClassname = cls.getName() + "_Stub";
 217:             ClassLoader cl = cls.getClassLoader();
 218:             Class scls = cl == null ? Class.forName(stubClassname)
 219:                                    : cl.loadClass(stubClassname);
 220:             return cls; // found it
 221:           }
 222:         catch (ClassNotFoundException e)
 223:           {
 224:             Class superCls = cls.getSuperclass();
 225:             if (superCls == null
 226:                 || superCls == java.rmi.server.UnicastRemoteObject.class)
 227:               {
 228:                 return null;
 229:               }
 230:             cls = superCls;
 231:           }
 232:       }
 233:   }
 234: 
 235:   /**
 236:    * Get the helper (assisting) class with the given type.
 237:    *
 238:    * @param cls the class, for that the helper class is requested. This class
 239:    * and the requested helper class must share the same class loader.
 240:    *
 241:    * @param type the type of the assisting helper. The only currently supported
 242:    * non deprecated value is "_Stub" (load jdk 1.1 or 1.2 RMI stub). Another
 243:    * (deprecated) value is "_Skel" (load skeleton).
 244:    *
 245:    * @return the instantiated instance of the helper class or null if the
 246:    * helper class cannot be found or instantiated.
 247:    */
 248:   protected Object getHelperClass(Class cls, String type)
 249:   {
 250:     try
 251:       {
 252:         String classname = cls.getName();
 253:         ClassLoader cl = cls.getClassLoader();
 254:         Class scls = cl == null ? Class.forName(classname + type)
 255:                                : cl.loadClass(classname + type);
 256:         if (type.equals("_Stub"))
 257:           {
 258:             try
 259:               {
 260:                 // JDK 1.2 stubs
 261:                 Constructor con = scls.getConstructor(stubprototype);
 262:                 return (con.newInstance(new Object[] { this }));
 263:               }
 264:             catch (NoSuchMethodException e)
 265:               {
 266:               }
 267:             catch (InstantiationException e)
 268:               {
 269:               }
 270:             catch (IllegalAccessException e)
 271:               {
 272:               }
 273:             catch (IllegalArgumentException e)
 274:               {
 275:               }
 276:             catch (InvocationTargetException e)
 277:               {
 278:               }
 279:             // JDK 1.1 stubs
 280:             RemoteStub stub = (RemoteStub) scls.newInstance();
 281:             UnicastRemoteStub.setStubRef(stub, this);
 282:             return (stub);
 283:           }
 284:         else
 285:           {
 286:             // JDK 1.1 skel
 287:             return (scls.newInstance());
 288:           }
 289:       }
 290:     catch (ClassNotFoundException e)
 291:       {
 292:       }
 293:     catch (InstantiationException e)
 294:       {
 295:       }
 296:     catch (IllegalAccessException e)
 297:       {
 298:       }
 299:     return (null);
 300:   }
 301: 
 302:   public String getClientHost() throws ServerNotActiveException
 303:   {
 304:     return RemoteServer.getClientHost();
 305:   }
 306: 
 307:   /**
 308:    * Build the method has code table and put it into {@link #methods}
 309:    * (mapping RMI hashcode tos method). The same method is used to remove
 310:    * the table.
 311:    *
 312:    * @param cls the class for that the method table is built.
 313:    * @param build if true, the class methods are added to the table. If
 314:    * false, they are removed from the table.
 315:    */
 316:   protected void buildMethodHash(Class cls, boolean build)
 317:   {
 318:     Method[] meths = cls.getMethods();
 319:     for (int i = 0; i < meths.length; i++)
 320:       {
 321:         /* Don't need to include any java.xxx related stuff */
 322:         if (meths[i].getDeclaringClass().getName().startsWith("java."))
 323:           {
 324:             continue;
 325:           }
 326:         long hash = RMIHashes.getMethodHash(meths[i]);
 327:         if (build)
 328:           methods.put(new Long(hash), meths[i]);
 329:         else
 330:           methods.remove(new Long(hash));
 331:         // System.out.println("meth = " + meths[i] + ", hash = " + hash);
 332:       }
 333:   }
 334: 
 335:   Class getMethodReturnType(int method, long hash) throws Exception
 336:   {
 337:     if (method == - 1)
 338:       {
 339:         Method meth = (Method) methods.get(new Long(hash));
 340:         return meth.getReturnType();
 341:       }
 342:     else
 343:       return null;
 344:   }
 345: 
 346:   /**
 347:    * This method is called from the {@link UnicastServer#incomingMessageCall}
 348:    * to deliver the remote call to this object.
 349:    */
 350:   public Object incomingMessageCall(UnicastConnection conn, int method,
 351:                                     long hash) throws Exception
 352:   {
 353:     // System.out.println("method = " + method + ", hash = " + hash);
 354:     // If method is -1 then this is JDK 1.2 RMI - so use the hash
 355:     // to locate the method
 356:     if (method == - 1)
 357:       {
 358:         Method meth = (Method) methods.get(new Long(hash));
 359:         // System.out.println("class = " + myself.getClass() + ", meth = " +
 360:         // meth);
 361:         if (meth == null)
 362:           {
 363:             throw new NoSuchMethodException(
 364:               myself.getClass().getName()+" hash "+hash);
 365:           }
 366: 
 367:         ObjectInputStream in = conn.getObjectInputStream();
 368:         int nrargs = meth.getParameterTypes().length;
 369:         Object[] args = new Object[nrargs];
 370:         for (int i = 0; i < nrargs; i++)
 371:           {
 372:             /**
 373:              * For debugging purposes - we don't handle CodeBases quite right so
 374:              * we don't always find the stubs. This lets us know that.
 375:              */
 376:             try
 377:               {
 378:                 // need to handle primitive types
 379:                 args[i] = ((RMIObjectInputStream) in)
 380:                   .readValue(meth.getParameterTypes()[i]);
 381: 
 382:               }
 383:             catch (Exception t)
 384:               {
 385:                 t.printStackTrace();
 386:                 throw t;
 387:               }
 388:           }
 389:         //We must reinterpret the exception thrown by meth.invoke()
 390:         //return (meth.invoke(myself, args));
 391:         Object ret = null;
 392:         try
 393:           {
 394:             ret = meth.invoke(myself, args);
 395:           }
 396:         catch (InvocationTargetException e)
 397:           {
 398:             Throwable cause = e.getTargetException();
 399:             if (cause instanceof Exception)
 400:               {
 401:                 throw (Exception) cause;
 402:               }
 403:             else if (cause instanceof Error)
 404:               {
 405:                 throw (Error) cause;
 406:               }
 407:             else
 408:               {
 409:                 throw new Error(
 410:                   "The remote method threw a java.lang.Throwable that"+
 411:                   " is neither java.lang.Exception nor java.lang.Error.",
 412:                   e);
 413:               }
 414:           }
 415:         return ret;
 416:       }
 417:     // Otherwise this is JDK 1.1 style RMI - we find the skeleton
 418:     // and invoke it using the method number.  We wrap up our
 419:     // connection system in a UnicastRemoteCall so it appears in a
 420:     // way the Skeleton can handle.
 421:     else
 422:       {
 423:         if (skel == null)
 424:           throw new NoSuchMethodException("JDK 1.1 call - Skeleton required");
 425: 
 426:         UnicastRemoteCall call = new UnicastRemoteCall(conn);
 427:         skel.dispatch(myself, call, method, hash);
 428:         if (! call.isReturnValue())
 429:           return RMIVoidValue.INSTANCE;
 430:         else
 431:           return (call.returnValue());
 432:       }
 433:   }
 434: 
 435:   /**
 436:    * Create the 1.2 proxy stub in the case when the pre-generated stub is not
 437:    * available of the system is explicitly instructed to use proxy stubs.
 438:    *
 439:    * @param stubFor the class for that the proxy class must be constructed.
 440:    * @param reference the remote reference, used to find the given object
 441:    *
 442:    * @return the applicable proxy stub.
 443:    *
 444:    * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
 445:    */
 446:   Remote createProxyStub(Class stubFor, RemoteRef reference)
 447:   {
 448:     // Collect all interfaces, implemented by stubFor and derived from
 449:     // Remote (also Remote itself):
 450:     HashSet interfaces = new HashSet();
 451:     Class c = stubFor;
 452:     Class[] intfs;
 453: 
 454:     while (c != null)
 455:       {
 456:         intfs = c.getInterfaces();
 457:         for (int i = 0; i < intfs.length; i++)
 458:           {
 459:             if (Remote.class.isAssignableFrom(intfs[i]))
 460:               interfaces.add(intfs[i]);
 461:           }
 462:         c = c.getSuperclass();
 463:       }
 464: 
 465:     intfs = new Class[interfaces.size()];
 466:     Iterator it = interfaces.iterator();
 467: 
 468:     for (int i = 0; i < intfs.length; i++)
 469:       intfs[i] = (Class) it.next();
 470: 
 471:     RemoteObjectInvocationHandler handler =
 472:       new RemoteObjectInvocationHandler(reference);
 473: 
 474:     Object proxy =
 475:       Proxy.newProxyInstance(stubFor.getClassLoader(), intfs, handler);
 476: 
 477:     return (Remote) proxy;
 478:   }
 479: 
 480: 
 481: }