1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.rmi.activation;
  27 
  28 import java.lang.reflect.Constructor;
  29 import java.lang.reflect.InvocationTargetException;
  30 import java.rmi.MarshalledObject;
  31 import java.rmi.Naming;
  32 import java.rmi.Remote;
  33 import java.rmi.RemoteException;
  34 import java.rmi.activation.UnknownGroupException;
  35 import java.rmi.activation.UnknownObjectException;
  36 import java.rmi.server.RMIClassLoader;
  37 import java.rmi.server.UnicastRemoteObject;
  38 import java.security.AccessController;
  39 import sun.security.action.GetIntegerAction;
  40 import sun.security.action.GetPropertyAction;
  41 
  42 /**
  43  * An <code>ActivationGroup</code> is responsible for creating new
  44  * instances of "activatable" objects in its group, informing its
  45  * <code>ActivationMonitor</code> when either: its object's become
  46  * active or inactive, or the group as a whole becomes inactive. <p>
  47  *
  48  * An <code>ActivationGroup</code> is <i>initially</i> created in one
  49  * of several ways: <ul>
  50  * <li>as a side-effect of creating an <code>ActivationDesc</code>
  51  *     without an explicit <code>ActivationGroupID</code> for the
  52  *     first activatable object in the group, or
  53  * <li>via the <code>ActivationGroup.createGroup</code> method
  54  * <li>as a side-effect of activating the first object in a group
  55  *     whose <code>ActivationGroupDesc</code> was only registered.</ul><p>
  56  *
  57  * Only the activator can <i>recreate</i> an
  58  * <code>ActivationGroup</code>.  The activator spawns, as needed, a
  59  * separate VM (as a child process, for example) for each registered
  60  * activation group and directs activation requests to the appropriate
  61  * group. It is implementation specific how VMs are spawned. An
  62  * activation group is created via the
  63  * <code>ActivationGroup.createGroup</code> static method. The
  64  * <code>createGroup</code> method has two requirements on the group
  65  * to be created: 1) the group must be a concrete subclass of
  66  * <code>ActivationGroup</code>, and 2) the group must have a
  67  * constructor that takes two arguments:
  68  *
  69  * <ul>
  70  * <li> the group's <code>ActivationGroupID</code>, and
  71  * <li> the group's initialization data (in a
  72  *      <code>java.rmi.MarshalledObject</code>)</ul><p>
  73  *
  74  * When created, the default implementation of
  75  * <code>ActivationGroup</code> will override the system properties
  76  * with the properties requested when its
  77  * <code>ActivationGroupDesc</code> was created, and will set a
  78  * <code>java.rmi.RMISecurityManager</code> as the default system
  79  * security manager.  If your application requires specific properties
  80  * to be set when objects are activated in the group, the application
  81  * should create a special <code>Properties</code> object containing
  82  * these properties, then create an <code>ActivationGroupDesc</code>
  83  * with the <code>Properties</code> object, and use
  84  * <code>ActivationGroup.createGroup</code> before creating any
  85  * <code>ActivationDesc</code>s (before the default
  86  * <code>ActivationGroupDesc</code> is created).  If your application
  87  * requires the use of a security manager other than
  88  * <code>java.rmi.RMISecurityManager</code>, in the
  89  * ActivativationGroupDescriptor properties list you can set
  90  * <code>java.security.manager</code> property to the name of the security
  91  * manager you would like to install.
  92  *
  93  * @author      Ann Wollrath
  94  * @see         ActivationInstantiator
  95  * @see         ActivationGroupDesc
  96  * @see         ActivationGroupID
  97  * @since       1.2
  98  */
  99 public abstract class ActivationGroup
 100         extends UnicastRemoteObject
 101         implements ActivationInstantiator
 102 {
 103     /**
 104      * @serial the group's identifier
 105      */
 106     private ActivationGroupID groupID;
 107 
 108     /**
 109      * @serial the group's monitor
 110      */
 111     private ActivationMonitor monitor;
 112 
 113     /**
 114      * @serial the group's incarnation number
 115      */
 116     private long incarnation;
 117 
 118     /** the current activation group for this VM */
 119     private static ActivationGroup currGroup;
 120     /** the current group's identifier */
 121     private static ActivationGroupID currGroupID;
 122     /** the current group's activation system */
 123     private static ActivationSystem currSystem;
 124     /** used to control a group being created only once */
 125     private static boolean canCreate = true;
 126 
 127     /** indicate compatibility with the Java 2 SDK v1.2 version of class */
 128     private static final long serialVersionUID = -7696947875314805420L;
 129 
 130     /**
 131      * Constructs an activation group with the given activation group
 132      * identifier.  The group is exported as a
 133      * <code>java.rmi.server.UnicastRemoteObject</code>.
 134      *
 135      * @param   groupID the group's identifier
 136      * @throws  RemoteException if this group could not be exported
 137      * @since   1.2
 138      */
 139     protected ActivationGroup(ActivationGroupID groupID)
 140         throws RemoteException
 141     {
 142         // call super constructor to export the object
 143         super();
 144         this.groupID = groupID;
 145     }
 146 
 147     /**
 148      * The group's <code>inactiveObject</code> method is called
 149      * indirectly via a call to the <code>Activatable.inactive</code>
 150      * method. A remote object implementation must call
 151      * <code>Activatable</code>'s <code>inactive</code> method when
 152      * that object deactivates (the object deems that it is no longer
 153      * active). If the object does not call
 154      * <code>Activatable.inactive</code> when it deactivates, the
 155      * object will never be garbage collected since the group keeps
 156      * strong references to the objects it creates. <p>
 157      *
 158      * <p>The group's <code>inactiveObject</code> method unexports the
 159      * remote object from the RMI runtime so that the object can no
 160      * longer receive incoming RMI calls. An object will only be unexported
 161      * if the object has no pending or executing calls.
 162      * The subclass of <code>ActivationGroup</code> must override this
 163      * method and unexport the object. <p>
 164      *
 165      * <p>After removing the object from the RMI runtime, the group
 166      * must inform its <code>ActivationMonitor</code> (via the monitor's
 167      * <code>inactiveObject</code> method) that the remote object is
 168      * not currently active so that the remote object will be
 169      * re-activated by the activator upon a subsequent activation
 170      * request.<p>
 171      *
 172      * <p>This method simply informs the group's monitor that the object
 173      * is inactive.  It is up to the concrete subclass of ActivationGroup
 174      * to fulfill the additional requirement of unexporting the object. <p>
 175      *
 176      * @param id the object's activation identifier
 177      * @return true if the object was successfully deactivated; otherwise
 178      *         returns false.
 179      * @exception UnknownObjectException if object is unknown (may already
 180      * be inactive)
 181      * @exception RemoteException if call informing monitor fails
 182      * @exception ActivationException if group is inactive
 183      * @since 1.2
 184      */
 185     public boolean inactiveObject(ActivationID id)
 186         throws ActivationException, UnknownObjectException, RemoteException
 187     {
 188         getMonitor().inactiveObject(id);
 189         return true;
 190     }
 191 
 192     /**
 193      * The group's <code>activeObject</code> method is called when an
 194      * object is exported (either by <code>Activatable</code> object
 195      * construction or an explicit call to
 196      * <code>Activatable.exportObject</code>. The group must inform its
 197      * <code>ActivationMonitor</code> that the object is active (via
 198      * the monitor's <code>activeObject</code> method) if the group
 199      * hasn't already done so.
 200      *
 201      * @param id the object's identifier
 202      * @param obj the remote object implementation
 203      * @exception UnknownObjectException if object is not registered
 204      * @exception RemoteException if call informing monitor fails
 205      * @exception ActivationException if group is inactive
 206      * @since 1.2
 207      */
 208     public abstract void activeObject(ActivationID id, Remote obj)
 209         throws ActivationException, UnknownObjectException, RemoteException;
 210 
 211     /**
 212      * Create and set the activation group for the current VM.  The
 213      * activation group can only be set if it is not currently set.
 214      * An activation group is set using the <code>createGroup</code>
 215      * method when the <code>Activator</code> initiates the
 216      * re-creation of an activation group in order to carry out
 217      * incoming <code>activate</code> requests. A group must first be
 218      * registered with the <code>ActivationSystem</code> before it can
 219      * be created via this method.
 220      *
 221      * <p>The group class specified by the
 222      * <code>ActivationGroupDesc</code> must be a concrete subclass of
 223      * <code>ActivationGroup</code> and have a public constructor that
 224      * takes two arguments: the <code>ActivationGroupID</code> for the
 225      * group and the <code>MarshalledObject</code> containing the
 226      * group's initialization data (obtained from the
 227      * <code>ActivationGroupDesc</code>.
 228      *
 229      * <p>If the group class name specified in the
 230      * <code>ActivationGroupDesc</code> is <code>null</code>, then
 231      * this method will behave as if the group descriptor contained
 232      * the name of the default activation group implementation class.
 233      *
 234      * <p>Note that if your application creates its own custom
 235      * activation group, a security manager must be set for that
 236      * group.  Otherwise objects cannot be activated in the group.
 237      * <code>java.rmi.RMISecurityManager</code> is set by default.
 238      *
 239      * <p>If a security manager is already set in the group VM, this
 240      * method first calls the security manager's
 241      * <code>checkSetFactory</code> method.  This could result in a
 242      * <code>SecurityException</code>. If your application needs to
 243      * set a different security manager, you must ensure that the
 244      * policy file specified by the group's
 245      * <code>ActivationGroupDesc</code> grants the group the necessary
 246      * permissions to set a new security manager.  (Note: This will be
 247      * necessary if your group downloads and sets a security manager).
 248      *
 249      * <p>After the group is created, the
 250      * <code>ActivationSystem</code> is informed that the group is
 251      * active by calling the <code>activeGroup</code> method which
 252      * returns the <code>ActivationMonitor</code> for the group. The
 253      * application need not call <code>activeGroup</code>
 254      * independently since it is taken care of by this method.
 255      *
 256      * <p>Once a group is created, subsequent calls to the
 257      * <code>currentGroupID</code> method will return the identifier
 258      * for this group until the group becomes inactive.
 259      *
 260      * @param id the activation group's identifier
 261      * @param desc the activation group's descriptor
 262      * @param incarnation the group's incarnation number (zero on group's
 263      * initial creation)
 264      * @return the activation group for the VM
 265      * @exception ActivationException if group already exists or if error
 266      * occurs during group creation
 267      * @exception SecurityException if permission to create group is denied.
 268      * (Note: The default implementation of the security manager
 269      * <code>checkSetFactory</code>
 270      * method requires the RuntimePermission "setFactory")
 271      * @see SecurityManager#checkSetFactory
 272      * @since 1.2
 273      */
 274     public static synchronized
 275         ActivationGroup createGroup(ActivationGroupID id,
 276                                     final ActivationGroupDesc desc,
 277                                     long incarnation)
 278         throws ActivationException
 279     {
 280         SecurityManager security = System.getSecurityManager();
 281         if (security != null)
 282             security.checkSetFactory();
 283 
 284         if (currGroup != null)
 285             throw new ActivationException("group already exists");
 286 
 287         if (canCreate == false)
 288             throw new ActivationException("group deactivated and " +
 289                                           "cannot be recreated");
 290 
 291         try {
 292             // load group's class
 293             String groupClassName = desc.getClassName();
 294             Class<? extends ActivationGroup> cl;
 295             Class<? extends ActivationGroup> defaultGroupClass =
 296                 sun.rmi.server.ActivationGroupImpl.class;
 297             if (groupClassName == null ||       // see 4252236
 298                 groupClassName.equals(defaultGroupClass.getName()))
 299             {
 300                 cl = defaultGroupClass;
 301             } else {
 302                 Class<?> cl0;
 303                 try {
 304                     cl0 = RMIClassLoader.loadClass(desc.getLocation(),
 305                                                    groupClassName);
 306                 } catch (Exception ex) {
 307                     throw new ActivationException(
 308                         "Could not load group implementation class", ex);
 309                 }
 310                 if (ActivationGroup.class.isAssignableFrom(cl0)) {
 311                     cl = cl0.asSubclass(ActivationGroup.class);
 312                 } else {
 313                     throw new ActivationException("group not correct class: " +
 314                                                   cl0.getName());
 315                 }
 316             }
 317 
 318             // create group
 319             Constructor<? extends ActivationGroup> constructor =
 320                 cl.getConstructor(ActivationGroupID.class,
 321                                   MarshalledObject.class);
 322             ActivationGroup newGroup =
 323                 constructor.newInstance(id, desc.getData());
 324             currSystem = id.getSystem();
 325             newGroup.incarnation = incarnation;
 326             newGroup.monitor =
 327                 currSystem.activeGroup(id, newGroup, incarnation);
 328             currGroup = newGroup;
 329             currGroupID = id;
 330             canCreate = false;
 331         } catch (InvocationTargetException e) {
 332                 e.getTargetException().printStackTrace();
 333                 throw new ActivationException("exception in group constructor",
 334                                               e.getTargetException());
 335 
 336         } catch (ActivationException e) {
 337             throw e;
 338 
 339         } catch (Exception e) {
 340             throw new ActivationException("exception creating group", e);
 341         }
 342 
 343         return currGroup;
 344     }
 345 
 346     /**
 347      * Returns the current activation group's identifier.  Returns null
 348      * if no group is currently active for this VM.
 349      * @return the activation group's identifier
 350      * @since 1.2
 351      */
 352     public static synchronized ActivationGroupID currentGroupID() {
 353         return currGroupID;
 354     }
 355 
 356     /**
 357      * Returns the activation group identifier for the VM.  If an
 358      * activation group does not exist for this VM, a default
 359      * activation group is created. A group can be created only once,
 360      * so if a group has already become active and deactivated.
 361      *
 362      * @return the activation group identifier
 363      * @exception ActivationException if error occurs during group
 364      * creation, if security manager is not set, or if the group
 365      * has already been created and deactivated.
 366      */
 367     static synchronized ActivationGroupID internalCurrentGroupID()
 368         throws ActivationException
 369     {
 370         if (currGroupID == null)
 371             throw new ActivationException("nonexistent group");
 372 
 373         return currGroupID;
 374     }
 375 
 376     /**
 377      * Set the activation system for the VM.  The activation system can
 378      * only be set it if no group is currently active. If the activation
 379      * system is not set via this call, then the <code>getSystem</code>
 380      * method attempts to obtain a reference to the
 381      * <code>ActivationSystem</code> by looking up the name
 382      * "java.rmi.activation.ActivationSystem" in the Activator's
 383      * registry. By default, the port number used to look up the
 384      * activation system is defined by
 385      * <code>ActivationSystem.SYSTEM_PORT</code>. This port can be overridden
 386      * by setting the property <code>java.rmi.activation.port</code>.
 387      *
 388      * <p>If there is a security manager, this method first
 389      * calls the security manager's <code>checkSetFactory</code> method.
 390      * This could result in a SecurityException.
 391      *
 392      * @param system remote reference to the <code>ActivationSystem</code>
 393      * @exception ActivationException if activation system is already set
 394      * @exception SecurityException if permission to set the activation system is denied.
 395      * (Note: The default implementation of the security manager
 396      * <code>checkSetFactory</code>
 397      * method requires the RuntimePermission "setFactory")
 398      * @see #getSystem
 399      * @see SecurityManager#checkSetFactory
 400      * @since 1.2
 401      */
 402     public static synchronized void setSystem(ActivationSystem system)
 403         throws ActivationException
 404     {
 405         SecurityManager security = System.getSecurityManager();
 406         if (security != null)
 407             security.checkSetFactory();
 408 
 409         if (currSystem != null)
 410             throw new ActivationException("activation system already set");
 411 
 412         currSystem = system;
 413     }
 414 
 415     /**
 416      * Returns the activation system for the VM. The activation system
 417      * may be set by the <code>setSystem</code> method. If the
 418      * activation system is not set via the <code>setSystem</code>
 419      * method, then the <code>getSystem</code> method attempts to
 420      * obtain a reference to the <code>ActivationSystem</code> by
 421      * looking up the name "java.rmi.activation.ActivationSystem" in
 422      * the Activator's registry. By default, the port number used to
 423      * look up the activation system is defined by
 424      * <code>ActivationSystem.SYSTEM_PORT</code>. This port can be
 425      * overridden by setting the property
 426      * <code>java.rmi.activation.port</code>.
 427      *
 428      * @return the activation system for the VM/group
 429      * @exception ActivationException if activation system cannot be
 430      *  obtained or is not bound
 431      * (means that it is not running)
 432      * @see #setSystem
 433      * @since 1.2
 434      */
 435     public static synchronized ActivationSystem getSystem()
 436         throws ActivationException
 437     {
 438         if (currSystem == null) {
 439             try {
 440                 String host = AccessController.doPrivileged(
 441                     new GetPropertyAction("sun.rmi.activation.host",
 442                                          ""));
 443                 int port = AccessController.doPrivileged(
 444                     new GetIntegerAction("java.rmi.activation.port",
 445                                          ActivationSystem.SYSTEM_PORT));
 446                 currSystem = (ActivationSystem)
 447                     Naming.lookup("//" + host + ":" + port +
 448                                   "/java.rmi.activation.ActivationSystem");
 449             } catch (Exception e) {
 450                 throw new ActivationException(
 451                     "unable to obtain ActivationSystem", e);
 452             }
 453         }
 454         return currSystem;
 455     }
 456 
 457     /**
 458      * This protected method is necessary for subclasses to
 459      * make the <code>activeObject</code> callback to the group's
 460      * monitor. The call is simply forwarded to the group's
 461      * <code>ActivationMonitor</code>.
 462      *
 463      * @param id the object's identifier
 464      * @param mobj a marshalled object containing the remote object's stub
 465      * @exception UnknownObjectException if object is not registered
 466      * @exception RemoteException if call informing monitor fails
 467      * @exception ActivationException if an activation error occurs
 468      * @since 1.2
 469      */
 470     protected void activeObject(ActivationID id,
 471                                 MarshalledObject<? extends Remote> mobj)
 472         throws ActivationException, UnknownObjectException, RemoteException
 473     {
 474         getMonitor().activeObject(id, mobj);
 475     }
 476 
 477     /**
 478      * This protected method is necessary for subclasses to
 479      * make the <code>inactiveGroup</code> callback to the group's
 480      * monitor. The call is simply forwarded to the group's
 481      * <code>ActivationMonitor</code>. Also, the current group
 482      * for the VM is set to null.
 483      *
 484      * @exception UnknownGroupException if group is not registered
 485      * @exception RemoteException if call informing monitor fails
 486      * @since 1.2
 487      */
 488     protected void inactiveGroup()
 489         throws UnknownGroupException, RemoteException
 490     {
 491         try {
 492             getMonitor().inactiveGroup(groupID, incarnation);
 493         } finally {
 494             destroyGroup();
 495         }
 496     }
 497 
 498     /**
 499      * Returns the monitor for the activation group.
 500      */
 501     private ActivationMonitor getMonitor() throws RemoteException {
 502         synchronized (ActivationGroup.class) {
 503             if (monitor != null) {
 504                 return monitor;
 505             }
 506         }
 507         throw new RemoteException("monitor not received");
 508     }
 509 
 510     /**
 511      * Destroys the current group.
 512      */
 513     private static synchronized void destroyGroup() {
 514         currGroup = null;
 515         currGroupID = null;
 516         // NOTE: don't set currSystem to null since it may be needed
 517     }
 518 
 519     /**
 520      * Returns the current group for the VM.
 521      * @exception ActivationException if current group is null (not active)
 522      */
 523     static synchronized ActivationGroup currentGroup()
 524         throws ActivationException
 525     {
 526         if (currGroup == null) {
 527             throw new ActivationException("group is not active");
 528         }
 529         return currGroup;
 530     }
 531 
 532 }