Source for java.rmi.activation.Activatable

   1: /* Activatable.java -- A common ancestor for the activatable objects.
   2:    Copyright (c) 1996, 1997, 1998, 1999, 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 java.rmi.activation;
  41: 
  42: import gnu.java.rmi.server.ActivatableServerRef;
  43: import gnu.java.rmi.server.UnicastServer;
  44: import gnu.java.rmi.server.UnicastServerRef;
  45: 
  46: import java.lang.reflect.Field;
  47: import java.rmi.MarshalledObject;
  48: import java.rmi.NoSuchObjectException;
  49: import java.rmi.Remote;
  50: import java.rmi.RemoteException;
  51: import java.rmi.server.ObjID;
  52: import java.rmi.server.RMIClientSocketFactory;
  53: import java.rmi.server.RMIServerSocketFactory;
  54: import java.rmi.server.RemoteObject;
  55: import java.rmi.server.RemoteServer;
  56: import java.rmi.server.UnicastRemoteObject;
  57: 
  58: /**
  59:  * A common ancestor for the implementations of the activatable objects. Such
  60:  * objects require persistent access over time and can be activated by the
  61:  * system. The derived classes also implements the needed interface of some
  62:  * remote object and usually have the two parameter constructor, the first
  63:  * parameter being the {@link ActivationID} and the second the
  64:  * {@link MarshalledObject}. Activatable is the main class that developers need
  65:  * to use to implement and manage activatable objects. It also contains methods
  66:  * for making activatable remote objects that are not derived from the
  67:  * Activatable class.
  68:  *
  69:  * @author Audrius Meskauskas (audriusa@bioinformatics.org) (from stub)
  70:  */
  71: public abstract class Activatable
  72:     extends RemoteServer
  73: {
  74: 
  75:   /**
  76:    * Use SVUID for interoperability.
  77:    */
  78:   static final long serialVersionUID = - 3120617863591563455L;
  79: 
  80:   /**
  81:    * The object activation id.
  82:    */
  83:   final ActivationID id;
  84: 
  85:   /**
  86:    * This constructor is used to register export the object on the given port. A
  87:    * subclass of the Activatable class calls this constructor to register and
  88:    * export the object during initial construction. As a side-effect of
  89:    * activatable object construction, the remote object is both "registered"
  90:    * with the activation system and "exported" (on an anonymous port, if port is
  91:    * zero) to the RMI runtime so that it is available to accept incoming calls
  92:    * from clients.
  93:    *
  94:    * @param codebase the object code base url
  95:    * @param data the data, needed to activate the object.
  96:    * @param restart specifies reactivation mode after crash. If true, the object
  97:    *          is activated when activator is restarted or the activation group
  98:    *          is restarted. If false, the object is only activated on demand.
  99:    *          This flag does has no effect during the normal operation (the
 100:    *          object is normally activated on demand).
 101:    * @param port the port, on which the object will become available. The value
 102:    *          0 means anonymous port.
 103:    * @throws ActivationException if the activation failed
 104:    * @throws RemoteException if the remote call failed.
 105:    */
 106:   protected Activatable(String codebase, MarshalledObject<?> data,
 107:                         boolean restart, int port) throws ActivationException,
 108:       RemoteException
 109:   {
 110:     ActivationDesc descriptor = new ActivationDesc(getClass().getName(),
 111:                                                    codebase, data, restart);
 112:     id = obtainId(descriptor);
 113:     exportObject(this, id, port);
 114:   }
 115: 
 116:   /**
 117:    * This constructor is used to register export the object on the given port,
 118:    * additionally specifying the socket factories. A subclass of the Activatable
 119:    * class calls this constructor to register and export the object during
 120:    * initial construction.
 121:    *
 122:    * @param codebase the object code base url
 123:    * @param data the data, needed to activate the object.
 124:    * @param restart specifies reactivation mode after crash. If true, the object
 125:    *          is activated when activator is restarted or the activation group
 126:    *          is restarted. If false, the object is only activated on demand.
 127:    *          This flag does has no effect during the normal operation (the
 128:    *          object is normally activated on demand).
 129:    * @param port the port, on which the object will become available. The value
 130:    *          0 means anonymous port.
 131:    * @param csf the client socket factory
 132:    * @param ssf the server socket factory
 133:    * @throws ActivationException if the activation failed
 134:    * @throws RemoteException if the remote call failed.
 135:    */
 136:   protected Activatable(String codebase, MarshalledObject<?> data,
 137:                         boolean restart, int port, RMIClientSocketFactory csf,
 138:                         RMIServerSocketFactory ssf) throws ActivationException,
 139:       RemoteException
 140:   {
 141:     ActivationDesc descriptor = new ActivationDesc(getClass().getName(),
 142:                                                    codebase, data, restart);
 143:     id = obtainId(descriptor);
 144:     exportObject(this, id, port);
 145:   }
 146: 
 147:   /**
 148:    * Creates the new instance of activatable with the given activation id and is
 149:    * listening at the given port. A subclass of the Activatable class calls this
 150:    * constructor when the object itself is activated via its special
 151:    * "activation" constructor with the two parameters ({@link ActivationID},
 152:    * {@link MarshalledObject}). As a side effect, the object is exported and is
 153:    * available to accept incoming calls.
 154:    *
 155:    * @param anId the activation id
 156:    * @param port the port, on which the activatable will be listening
 157:    * @throws RemoteException if the activation failed.
 158:    */
 159:   protected Activatable(ActivationID anId, int port) throws RemoteException
 160:   {
 161:     id = anId;
 162:     try
 163:       {
 164:         exportObject(this, anId, port);
 165:       }
 166:     catch (Exception e)
 167:       {
 168:         e.printStackTrace();
 169:         RemoteException acex =
 170:           new RemoteException("cannot export Activatable", e);
 171:         throw acex;
 172:       }
 173:   }
 174: 
 175:   /**
 176:    * Creates the new instance of activatable with the given activation id and is
 177:    * listening at the given port, using the specified client and server sockets
 178:    * factories. A subclass of the Activatable class calls this
 179:    * constructor when the object itself is activated via its special
 180:    * "activation" constructor with the two parameters ({@link ActivationID},
 181:    * {@link MarshalledObject}). As a side effect, the object is exported and is
 182:    * available to accept incoming calls.
 183:    *
 184:    * @param anId the activation id
 185:    * @param port the port, on which the activatable will be listening
 186:    * @param csf the client socket factory
 187:    * @param ssf the server socket factory
 188:    *
 189:    * @throws RemoteException if the remote call failed
 190:    */
 191:   protected Activatable(ActivationID anId, int port, RMIClientSocketFactory csf,
 192:                         RMIServerSocketFactory ssf) throws RemoteException
 193:   {
 194:     id = anId;
 195:     try
 196:       {
 197:         exportObject(this, anId, port, csf, ssf);
 198:       }
 199:     catch (Exception e)
 200:       {
 201:         RemoteException acex = new RemoteException();
 202:         acex.initCause(e);
 203:         throw acex;
 204:       }
 205:   }
 206: 
 207:   /**
 208:    * Get the objects activation identifier.
 209:    *
 210:    * @return the object activation identifier
 211:    */
 212:   protected ActivationID getID()
 213:   {
 214:     return id;
 215:   }
 216: 
 217:   /**
 218:    * Obtain the activation Id from the activation descriptor by registering
 219:    * within the current group.
 220:    */
 221:   static ActivationID obtainId(ActivationDesc descriptor)
 222:       throws RemoteException, UnknownGroupException, ActivationException
 223:   {
 224:     ActivationGroupID id = descriptor.getGroupID();
 225:     ActivationSystem system;
 226: 
 227:     if (id != null)
 228:       system = id.getSystem();
 229:     else
 230:       system = ActivationGroup.currentGroupID().getSystem();
 231:     return system.registerObject(descriptor);
 232:   }
 233: 
 234:   /**
 235:    * This method registers an activatable object. The object is expected to be
 236:    * on the anonymous port (null client and server socket factories).
 237:    *
 238:    * @param desc the object description.
 239:    * @return the remote stub for the activatable object (the first call on this
 240:    *         stub will activate the object).
 241:    * @throws UnknownGroupException if the object group identifier is unknown
 242:    * @throws ActivationException if the activation system is not running
 243:    * @throws RemoteException if the remote call fails
 244:    */
 245:   public static Remote register(ActivationDesc desc)
 246:       throws UnknownGroupException, ActivationException, RemoteException
 247:   {
 248:     ActivationID id = obtainId(desc);
 249:     try
 250:       {
 251:         return toStub(
 252:                       id,
 253:                       Thread.currentThread().getContextClassLoader().loadClass(
 254:                         desc.getClassName()));
 255:       }
 256:     catch (ClassNotFoundException e)
 257:       {
 258:         throw new ActivationException("Class not found: "+desc.getClassName());
 259:       }
 260:   }
 261: 
 262:   /**
 263:    * Inactivates and unexports the object. The subsequent calls will activate
 264:    * the object again. The object is not inactivated if it is currently
 265:    * executing calls.
 266:    *
 267:    * @param id the id of the object being inactivated
 268:    * @return true if the object has been inactivated, false if it has not been
 269:    *         inactivated because of the running or pending calls.
 270:    * @throws UnknownObjectException if the object is unknown.
 271:    * @throws ActivationException if the object group is not active
 272:    * @throws RemoteException if the remote call fails
 273:    */
 274:   public static boolean inactive(ActivationID id)
 275:       throws UnknownObjectException, ActivationException, RemoteException
 276:   {
 277:     if (id.group!=null)
 278:       id.group.inactiveObject(id);
 279:     return UnicastRemoteObject.unexportObject(id.activate(false), false);
 280:   }
 281: 
 282:   /**
 283:    * Unregister the object (the object will no longer be activable with that id)
 284:    *
 285:    * @param id the object id
 286:    * @throws UnknownObjectException if the id is unknown
 287:    * @throws ActivationException if the activation system is not running
 288:    * @throws RemoteException if the remote call fails.
 289:    */
 290:   public static void unregister(ActivationID id) throws UnknownObjectException,
 291:       ActivationException, RemoteException
 292:   {
 293:     ActivationGroup.currentGroupId.getSystem().unregisterObject(id);
 294:     UnicastServer.unregisterActivatable(id);
 295:   }
 296: 
 297:   /**
 298:    * Register and export the object that activatable object that is not derived
 299:    * from the Activatable super class. It creates and registers the object
 300:    * activation descriptor. There is no need to call this method if the object
 301:    * extends Activable, as its work is done in the constructor
 302:    * {@link #Activatable(String, MarshalledObject, boolean, int)}.
 303:    *
 304:    * @param obj the object, that is exported, becoming available at the given
 305:    *          port.
 306:    * @param location the object code location (codebase).
 307:    * @param data the data, needed to activate the object
 308:    * @param restart the restart mode
 309:    * @param port the port, where the object will be available
 310:    *
 311:    * @return the created object activation ID.
 312:    *
 313:    * @throws ActivationException if the activation group is not active
 314:    * @throws RemoteException if the registration or export fails
 315:    */
 316:   public static ActivationID exportObject(Remote obj, String location,
 317:                                           MarshalledObject<?> data,
 318:                                           boolean restart, int port)
 319:       throws ActivationException, RemoteException
 320:   {
 321:     ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(),
 322:                                                    location, data, restart);
 323:     ActivationID id = obtainId(descriptor);
 324:     Remote stub = exportObject(obj, id, port);
 325:     return id;
 326:   }
 327: 
 328:   /**
 329:    * Register and export the object that activatable object that is not derived
 330:    * from the Activatable super class. It creates and registers the object
 331:    * activation descriptor. There is no need to call this method if the object
 332:    * extends Activable, as its work is done in the constructor
 333:    * {@link #Activatable(String, MarshalledObject, boolean, int, RMIClientSocketFactory, RMIServerSocketFactory)}
 334:    *
 335:    * @param obj the object, that is exported, becoming available at the given
 336:    *          port.
 337:    * @param location the object code location (codebase).
 338:    * @param data the data, needed to activate the object
 339:    * @param restart the restart mode
 340:    * @param port the port, where the object will be available
 341:    * @param csf the client socket factory
 342:    * @param ssf the server socket factory
 343:    *
 344:    * @return the created object activation ID.
 345:    *
 346:    * @throws ActivationException if the activation group is not active
 347:    * @throws RemoteException if the registration or export fails
 348:    */
 349:   public static ActivationID exportObject(Remote obj, String location,
 350:                                           MarshalledObject data,
 351:                                           boolean restart, int port,
 352:                                           RMIClientSocketFactory csf,
 353:                                           RMIServerSocketFactory ssf)
 354:       throws ActivationException, RemoteException
 355:   {
 356:     ActivationDesc descriptor = new ActivationDesc(obj.getClass().getName(),
 357:                                                    location, data, restart);
 358:     ActivationID id = obtainId(descriptor);
 359:     Remote stub = exportObject(obj, id, port, csf, ssf);
 360:     return id;
 361: 
 362:   }
 363: 
 364:   /**
 365:    * During activation, this exportObject method should be invoked explicitly by
 366:    * the activatable object, that does is not derived from the Activatable
 367:    * class. There is no need to call this method if the object extends
 368:    * Activable, as its work is done in the constructor
 369:    * {@link #Activatable(ActivationID, int)}
 370:    *
 371:    * @param obj the object
 372:    * @param id the known activation id
 373:    * @param port the object port
 374:    *
 375:    * @return the remote stub of the activatable object
 376:    *
 377:    * @throws RemoteException if the object export fails
 378:    */
 379:   public static Remote exportObject(Remote obj, ActivationID id, int port)
 380:       throws RemoteException
 381:   {
 382:     Remote stub = export(id, obj, port, null);
 383:     return stub;
 384:   }
 385: 
 386:   /**
 387:    * During activation, this exportObject method should be invoked explicitly by
 388:    * the activatable object, that does is not derived from the Activatable
 389:    * class. There is no need to call this method if the object extends
 390:    * Activable, as its work is done in the constructor
 391:    * {@link #Activatable(ActivationID, int)}
 392:    *
 393:    * @param obj the object
 394:    * @param id the known activation id
 395:    * @param port the object port
 396:    * @param csf the client socket factory
 397:    * @param ssf the server socket factory
 398:    *
 399:    * @return the remote stub of the activatable object
 400:    *
 401:    * @throws RemoteException if the object export fails
 402:    */
 403:   public static Remote exportObject(Remote obj, ActivationID id, int port,
 404:                                     RMIClientSocketFactory csf,
 405:                                     RMIServerSocketFactory ssf)
 406:       throws RemoteException
 407:   {
 408:     Remote stub = export(id, obj, port, ssf);
 409:     return stub;
 410: 
 411:   }
 412: 
 413:   /**
 414:    * Make the remote object unavailable for incoming calls. This method also
 415:    * unregisters the object, so it cannot be activated again by incoming call
 416:    * (unless registered).
 417:    *
 418:    * @param obj the object to unexport
 419:    * @param force if true, cancel all pending or running calls to that object
 420:    *          (if false, the object with such calls is not unexported and false
 421:    *          is returned by this method).
 422:    * @return if the object was successfully unexported, false otherwise
 423:    * @throws NoSuchObjectException if such object is not known
 424:    */
 425:   public static boolean unexportObject(Remote obj, boolean force)
 426:       throws NoSuchObjectException
 427:   {
 428:     Object aref = UnicastServer.getExportedRef(obj);
 429: 
 430:     // Unregister it also (otherwise will be activated during the subsequent
 431:     // call.
 432:     if (aref instanceof ActivatableServerRef)
 433:       {
 434:         ActivatableServerRef aar = (ActivatableServerRef) aref;
 435:         UnicastServer.unregisterActivatable(aar.actId);
 436:       }
 437:     return UnicastRemoteObject.unexportObject(obj, force);
 438:   }
 439: 
 440:   static Remote exportObject(Remote obj, int port,
 441:                              RMIServerSocketFactory serverSocketFactory)
 442:     throws RemoteException
 443:   {
 444:     UnicastServerRef sref = null;
 445:     if (obj instanceof RemoteObject)
 446:       sref = (UnicastServerRef) ((RemoteObject) obj).getRef();
 447: 
 448:     if (sref == null)
 449:       sref = new UnicastServerRef(new ObjID(), port, serverSocketFactory);
 450: 
 451:     Remote stub = sref.exportObject(obj);
 452:     // addStub(obj, stub);
 453:     // TODO Need to change the place of the stub repository
 454:     return stub;
 455:   }
 456: 
 457:   /**
 458:    * Create and export the new remote object, making it available at the given
 459:    * port, using sockets, produced by the specified factories.
 460:    *
 461:    * @param port the port, on that the object should become available. Zero
 462:    *          means anonymous port.
 463:    * @param serverSocketFactory the server socket factory
 464:    */
 465:   private static Remote export(ActivationID id, Remote obj, int port,
 466:                                RMIServerSocketFactory serverSocketFactory)
 467:       throws RemoteException
 468:   {
 469:     ActivatableServerRef sref = null;
 470:     sref = new ActivatableServerRef(makeId(id), id, port, serverSocketFactory);
 471:     return sref.exportObject(obj);
 472:   }
 473: 
 474:   /**
 475:    * Make the object ID from the activation ID. The same activation ID always
 476:    * produces the identical object id.
 477:    *
 478:    * @param aid the activation id
 479:    *
 480:    * @return the object id
 481:    */
 482:   private static ObjID makeId(ActivationID aid)
 483:   {
 484:     ObjID id = new ObjID(0);
 485: 
 486:     // The fields of both ObjID and ActivationID must be package private,
 487:     // so we need to use the reflection to access them anyway.
 488:     // Probably other implementations use some very different approach.
 489: 
 490:     try
 491:       {
 492:         Field idUid =  ObjID.class.getDeclaredField("space");
 493:         Field aidUid = ActivationID.class.getDeclaredField("uid");
 494: 
 495:         aidUid.setAccessible(true);
 496:         idUid.setAccessible(true);
 497: 
 498:         idUid.set(id, aidUid.get(aid));
 499:       }
 500:     catch (Exception e)
 501:       {
 502:         InternalError ierr = new InternalError("Unable to set UID field");
 503:         ierr.initCause(e);
 504:         throw ierr;
 505:       }
 506: 
 507:     return id;
 508:   }
 509: 
 510:   /**
 511:    * Connect the object to the UnicastServer (export), but not activate it.
 512:    * The object will be activated on the first call.
 513:    */
 514:   static Remote toStub(ActivationID anId, Class stubFor)
 515:   {
 516:     try
 517:       {
 518:         ActivatableServerRef asr =
 519:           new ActivatableServerRef(makeId(anId), anId, 0, null);
 520:         UnicastServer.exportActivatableObject(asr);
 521:         return asr.exportClass(stubFor);
 522:       }
 523:     catch (RemoteException e)
 524:       {
 525:         InternalError ierr = new InternalError(
 526:           "Failed to obtain activatable stub");
 527:         ierr.initCause(e);
 528:         throw ierr;
 529:       }
 530:   }
 531: }