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