src/share/classes/sun/rmi/server/Activation.java

Print this page
rev 3756 : 6896297: (rmi) fix ConcurrentModificationException causing TCK failure
Reviewed-by: XXX


  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 sun.rmi.server;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.File;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;

  33 import java.io.OutputStream;
  34 import java.io.PrintStream;
  35 import java.io.PrintWriter;
  36 import java.io.Serializable;
  37 import java.lang.Process;
  38 import java.lang.reflect.InvocationTargetException;
  39 import java.lang.reflect.Method;
  40 import java.net.InetAddress;
  41 import java.net.ServerSocket;
  42 import java.net.Socket;
  43 import java.net.SocketAddress;
  44 import java.net.SocketException;
  45 import java.nio.channels.Channel;
  46 import java.nio.channels.ServerSocketChannel;
  47 import java.rmi.AccessException;
  48 import java.rmi.AlreadyBoundException;
  49 import java.rmi.ConnectException;
  50 import java.rmi.ConnectIOException;
  51 import java.rmi.MarshalledObject;
  52 import java.rmi.NoSuchObjectException;


 117  * to obtain a "live" reference to a activatable remote object. Upon
 118  * receiving a request for activation, the activator looks up the
 119  * activation descriptor for the activation identifier, id, determines
 120  * the group in which the object should be activated and invokes the
 121  * activate method on the object's activation group (described by the
 122  * remote interface <code>ActivationInstantiator</code>). The
 123  * activator initiates the execution of activation groups as
 124  * necessary. For example, if an activation group for a specific group
 125  * identifier is not already executing, the activator will spawn a
 126  * child process for the activation group. <p>
 127  *
 128  * The activator is responsible for monitoring and detecting when
 129  * activation groups fail so that it can remove stale remote references
 130  * from its internal tables. <p>
 131  *
 132  * @author      Ann Wollrath
 133  * @since       1.2
 134  */
 135 public class Activation implements Serializable {
 136 


















































 137     /** indicate compatibility with JDK 1.2 version of class */
 138     private static final long serialVersionUID = 2921265612698155191L;
 139 
 140     private static final byte MAJOR_VERSION = 1;
 141     private static final byte MINOR_VERSION = 0;
 142 
 143     /** exec policy object */
 144     private static Object execPolicy;
 145     private static Method execPolicyMethod;
 146     private static boolean debugExec;
 147 
 148     /** maps activation id to its respective group id */
 149     private Map<ActivationID,ActivationGroupID> idTable =
 150         new HashMap<ActivationID,ActivationGroupID>();
 151     /** maps group id to its GroupEntry groups */
 152     private Map<ActivationGroupID,GroupEntry> groupTable =
 153         new HashMap<ActivationGroupID,GroupEntry>();
 154 
 155     private byte majorVersion = MAJOR_VERSION;
 156     private byte minorVersion = MINOR_VERSION;


 257                 synchronized (initLock) {
 258                     initDone = true;
 259                     initLock.notifyAll();
 260                 }
 261             }
 262         }
 263         startupLock = null;
 264 
 265         // restart services
 266         for (int i = gids.length; --i >= 0; ) {
 267             try {
 268                 getGroupEntry(gids[i]).restartServices();
 269             } catch (UnknownGroupException e) {
 270                 System.err.println(
 271                     getTextResource("rmid.restart.group.warning"));
 272                 e.printStackTrace();
 273             }
 274         }
 275     }
 276 













 277     private static class SystemRegistryImpl extends RegistryImpl {
 278 
 279         private static final String NAME = ActivationSystem.class.getName();
 280         private final ActivationSystem systemStub;
 281 
 282         SystemRegistryImpl(int port,
 283                            RMIClientSocketFactory csf,
 284                            RMIServerSocketFactory ssf,
 285                            ActivationSystem systemStub)
 286             throws RemoteException
 287         {
 288             super(port, csf, ssf);
 289             this.systemStub = systemStub;
 290         }
 291 
 292         /**
 293          * Returns the activation system stub if the specified name
 294          * matches the activation system's class name, otherwise
 295          * returns the result of invoking super.lookup with the specified
 296          * name.


 498         public ActivationMonitor activeGroup(ActivationGroupID id,
 499                                              ActivationInstantiator group,
 500                                              long incarnation)
 501             throws ActivationException, UnknownGroupException, RemoteException
 502         {
 503             checkShutdown();
 504             RegistryImpl.checkAccess("ActivationSystem.activeGroup");
 505 
 506             getGroupEntry(id).activeGroup(group, incarnation);
 507             return monitor;
 508         }
 509 
 510         public void unregisterGroup(ActivationGroupID id)
 511             throws ActivationException, UnknownGroupException, RemoteException
 512         {
 513             checkShutdown();
 514             RegistryImpl.checkAccess("ActivationSystem.unregisterGroup");
 515 
 516             // remove entry before unregister so state is updated before
 517             // logged

 518             synchronized (groupTable) {
 519                 GroupEntry entry = getGroupEntry(id);
 520                 groupTable.remove(id);
 521                 entry.unregisterGroup(true);
 522             }

 523         }
 524 
 525         public ActivationDesc setActivationDesc(ActivationID id,
 526                                                 ActivationDesc desc)
 527             throws ActivationException, UnknownObjectException, RemoteException
 528         {
 529             checkShutdown();
 530             RegistryImpl.checkAccess("ActivationSystem.setActivationDesc");
 531 
 532             if (!getGroupID(id).equals(desc.getGroupID())) {
 533                 throw new ActivationException(
 534                     "ActivationDesc contains wrong group");
 535             }
 536             return getGroupEntry(id).setActivationDesc(id, desc, true);
 537         }
 538 
 539         public ActivationGroupDesc setActivationGroupDesc(ActivationGroupID id,
 540                                                           ActivationGroupDesc desc)
 541             throws ActivationException, UnknownGroupException, RemoteException
 542         {




  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 sun.rmi.server;
  27 
  28 import java.io.ByteArrayOutputStream;
  29 import java.io.File;
  30 import java.io.FileOutputStream;
  31 import java.io.IOException;
  32 import java.io.InputStream;
  33 import java.io.ObjectOutputStream;
  34 import java.io.OutputStream;
  35 import java.io.PrintStream;
  36 import java.io.PrintWriter;
  37 import java.io.Serializable;
  38 import java.lang.Process;
  39 import java.lang.reflect.InvocationTargetException;
  40 import java.lang.reflect.Method;
  41 import java.net.InetAddress;
  42 import java.net.ServerSocket;
  43 import java.net.Socket;
  44 import java.net.SocketAddress;
  45 import java.net.SocketException;
  46 import java.nio.channels.Channel;
  47 import java.nio.channels.ServerSocketChannel;
  48 import java.rmi.AccessException;
  49 import java.rmi.AlreadyBoundException;
  50 import java.rmi.ConnectException;
  51 import java.rmi.ConnectIOException;
  52 import java.rmi.MarshalledObject;
  53 import java.rmi.NoSuchObjectException;


 118  * to obtain a "live" reference to a activatable remote object. Upon
 119  * receiving a request for activation, the activator looks up the
 120  * activation descriptor for the activation identifier, id, determines
 121  * the group in which the object should be activated and invokes the
 122  * activate method on the object's activation group (described by the
 123  * remote interface <code>ActivationInstantiator</code>). The
 124  * activator initiates the execution of activation groups as
 125  * necessary. For example, if an activation group for a specific group
 126  * identifier is not already executing, the activator will spawn a
 127  * child process for the activation group. <p>
 128  *
 129  * The activator is responsible for monitoring and detecting when
 130  * activation groups fail so that it can remove stale remote references
 131  * from its internal tables. <p>
 132  *
 133  * @author      Ann Wollrath
 134  * @since       1.2
 135  */
 136 public class Activation implements Serializable {
 137 
 138     /*
 139      * Synchronization occurs over several objects in this code.
 140      * These objects are:
 141      *
 142      *  - startupLock
 143      *  - initLock
 144      *  - Activation.this
 145      *  - instances of GroupEntry
 146      *  - log (a ReliableLog)
 147      *  - idTable (a HashMap)
 148      *  - groupTable (a HashMap)
 149      *
 150      * The startupLock and initLock locks are used only during
 151      * initialization and shutdown. The lock on Activation.this
 152      * is used to guard groupSemaphore and to guard writes to the
 153      * shuttingDown flag (which is volatile and which is read
 154      * without synchronization).
 155      *
 156      * Several operations on GroupEntry instances hold a lock on
 157      * that instance while performing logging operations and while
 158      * while manipulating idTable or groupTable.
 159      *
 160      * Log operations, notably addLogRecord(), hold a lock on the
 161      * log instance. (Methods of the log itself also take locks
 162      * on the log object.) The log.snapshot() operation serializes
 163      * the log, including instances of Activation (this class),
 164      * which in turn must lock idTable and groupTable to prevent
 165      * concurrent modification during serialization.
 166      *
 167      * The idTable and groupTable HashMaps are locked while simple
 168      * operations are performed on them. Note that we cannot
 169      * straightforwardly convert these to ConcurrentHashMap,
 170      * because it has a different serialized form from HashMap.
 171      *
 172      * During serialization, the writeObject() method of this class
 173      * takes locks on groupTable and idTable in that order.
 174      * 
 175      * Based on the above paths through the code, a partial
 176      * ordering for the above locks must be as follows:
 177      *
 178      * 1. GroupEntry instances
 179      * 2. log
 180      * 3. groupTable
 181      * 4. idTable
 182      *
 183      * For example, code may lock the log while holding the
 184      * lock for a GroupEntry, but code must not lock the log
 185      * while holding a lock on groupTable.
 186      */
 187 
 188     /** indicate compatibility with JDK 1.2 version of class */
 189     private static final long serialVersionUID = 2921265612698155191L;
 190 
 191     private static final byte MAJOR_VERSION = 1;
 192     private static final byte MINOR_VERSION = 0;
 193 
 194     /** exec policy object */
 195     private static Object execPolicy;
 196     private static Method execPolicyMethod;
 197     private static boolean debugExec;
 198 
 199     /** maps activation id to its respective group id */
 200     private Map<ActivationID,ActivationGroupID> idTable =
 201         new HashMap<ActivationID,ActivationGroupID>();
 202     /** maps group id to its GroupEntry groups */
 203     private Map<ActivationGroupID,GroupEntry> groupTable =
 204         new HashMap<ActivationGroupID,GroupEntry>();
 205 
 206     private byte majorVersion = MAJOR_VERSION;
 207     private byte minorVersion = MINOR_VERSION;


 308                 synchronized (initLock) {
 309                     initDone = true;
 310                     initLock.notifyAll();
 311                 }
 312             }
 313         }
 314         startupLock = null;
 315 
 316         // restart services
 317         for (int i = gids.length; --i >= 0; ) {
 318             try {
 319                 getGroupEntry(gids[i]).restartServices();
 320             } catch (UnknownGroupException e) {
 321                 System.err.println(
 322                     getTextResource("rmid.restart.group.warning"));
 323                 e.printStackTrace();
 324             }
 325         }
 326     }
 327 
 328     private void writeObject(ObjectOutputStream ostream) throws IOException {
 329         // Synchronize on groupTable and idTable while writing them to the
 330         // object stream in order to avoid ConcurrentModificationException. It
 331         // might be preferable to synchronize them individually, but then we'd
 332         // have to write the fields individually instead of using
 333         // defaultWriteObject().
 334         synchronized (groupTable) {
 335             synchronized (idTable) {
 336                 ostream.defaultWriteObject();
 337             }
 338         }
 339     }
 340 
 341     private static class SystemRegistryImpl extends RegistryImpl {
 342 
 343         private static final String NAME = ActivationSystem.class.getName();
 344         private final ActivationSystem systemStub;
 345 
 346         SystemRegistryImpl(int port,
 347                            RMIClientSocketFactory csf,
 348                            RMIServerSocketFactory ssf,
 349                            ActivationSystem systemStub)
 350             throws RemoteException
 351         {
 352             super(port, csf, ssf);
 353             this.systemStub = systemStub;
 354         }
 355 
 356         /**
 357          * Returns the activation system stub if the specified name
 358          * matches the activation system's class name, otherwise
 359          * returns the result of invoking super.lookup with the specified
 360          * name.


 562         public ActivationMonitor activeGroup(ActivationGroupID id,
 563                                              ActivationInstantiator group,
 564                                              long incarnation)
 565             throws ActivationException, UnknownGroupException, RemoteException
 566         {
 567             checkShutdown();
 568             RegistryImpl.checkAccess("ActivationSystem.activeGroup");
 569 
 570             getGroupEntry(id).activeGroup(group, incarnation);
 571             return monitor;
 572         }
 573 
 574         public void unregisterGroup(ActivationGroupID id)
 575             throws ActivationException, UnknownGroupException, RemoteException
 576         {
 577             checkShutdown();
 578             RegistryImpl.checkAccess("ActivationSystem.unregisterGroup");
 579 
 580             // remove entry before unregister so state is updated before
 581             // logged
 582             GroupEntry entry;
 583             synchronized (groupTable) {
 584                 entry = getGroupEntry(id);
 585                 groupTable.remove(id);

 586             }
 587             entry.unregisterGroup(true);
 588         }
 589 
 590         public ActivationDesc setActivationDesc(ActivationID id,
 591                                                 ActivationDesc desc)
 592             throws ActivationException, UnknownObjectException, RemoteException
 593         {
 594             checkShutdown();
 595             RegistryImpl.checkAccess("ActivationSystem.setActivationDesc");
 596 
 597             if (!getGroupID(id).equals(desc.getGroupID())) {
 598                 throw new ActivationException(
 599                     "ActivationDesc contains wrong group");
 600             }
 601             return getGroupEntry(id).setActivationDesc(id, desc, true);
 602         }
 603 
 604         public ActivationGroupDesc setActivationGroupDesc(ActivationGroupID id,
 605                                                           ActivationGroupDesc desc)
 606             throws ActivationException, UnknownGroupException, RemoteException
 607         {