You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2014/05/09 09:03:19 UTC

svn commit: r1593493 [14/24] - in /river/jtsk/skunk/qa_refactor/trunk: qa/ qa/src/com/sun/jini/test/impl/end2end/jssewrapper/ qa/src/com/sun/jini/test/impl/joinmanager/ qa/src/com/sun/jini/test/impl/mahalo/ qa/src/com/sun/jini/test/impl/outrigger/match...

Modified: river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/phoenix/Activation.java
URL: http://svn.apache.org/viewvc/river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/phoenix/Activation.java?rev=1593493&r1=1593492&r2=1593493&view=diff
==============================================================================
--- river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/phoenix/Activation.java (original)
+++ river/jtsk/skunk/qa_refactor/trunk/src/com/sun/jini/phoenix/Activation.java Fri May  9 07:03:18 2014
@@ -1,1884 +1,1920 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * 
- *      http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.sun.jini.phoenix;
-
-import com.sun.jini.proxy.BasicProxyTrustVerifier;
-import com.sun.jini.proxy.MarshalledWrapper;
-import com.sun.jini.reliableLog.LogHandler;
-import com.sun.jini.reliableLog.ReliableLog;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Serializable;
-import java.lang.Process;
-import java.net.URL;
-import java.rmi.*;
-import java.rmi.activation.*;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
-import java.rmi.server.UID;
-import java.security.CodeSource;
-import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import javax.security.auth.Subject;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import net.jini.config.Configuration;
-import net.jini.config.ConfigurationException;
-import net.jini.config.ConfigurationNotFoundException;
-import net.jini.config.ConfigurationProvider;
-import net.jini.config.NoSuchEntryException;
-import net.jini.core.constraint.RemoteMethodControl;
-import net.jini.export.Exporter;
-import net.jini.io.MarshalInputStream;
-import net.jini.io.MarshalOutputStream;
-import net.jini.io.MarshalledInstance;
-import net.jini.jeri.BasicILFactory;
-import net.jini.jeri.BasicJeriExporter;
-import net.jini.jeri.ServerEndpoint;
-import net.jini.jeri.tcp.TcpServerEndpoint;
-import net.jini.security.BasicProxyPreparer;
-import net.jini.security.ProxyPreparer;
-import net.jini.security.TrustVerifier;
-import net.jini.security.proxytrust.ServerProxyTrust;
-
-/**
- * Phoenix main class.
- *
- * @author Sun Microsystems, Inc.
- * 
- * @since 2.0
- */
-class Activation implements Serializable {
-    private static final long serialVersionUID = -6825932652725866242L;
-    private static final String PHOENIX = "com.sun.jini.phoenix";
-    private static ResourceBundle resources;
-    private static Logger logger = Logger.getLogger(PHOENIX);
-    private static final int PHOENIX_PORT = 1198;
-    private static final Object logLock = new Object();
-    
-    static {
-        try {
-            resources = ResourceBundle.getBundle(
-                "com.sun.jini.phoenix.resources.phoenix");
-        } catch (MissingResourceException mre) {
-            logger.log( Level.CONFIG, "[missing resource file]", mre);
-            resources = null;
-        }
-    }
-
-    /** maps activation id uid to its respective group id */
-    private final Map<UID,ActivationGroupID> idTable = new HashMap<UID,ActivationGroupID>();
-    /** maps group id to its GroupEntry groups */
-    private final Map<ActivationGroupID,GroupEntry> groupTable = new HashMap<ActivationGroupID,GroupEntry>();
-    
-    /** login context */
-    private transient LoginContext login;
-    /** number of simultaneous group exec's */
-    private transient int groupSemaphore;
-    /** counter for numbering groups */
-    private transient int groupCounter = 0;
-    /** persistent store */
-    private transient ReliableLog log;
-    /** number of log updates since last log snapshot */
-    private transient int numUpdates = 0;
-    /** take log snapshot after this many updates */
-    private transient int snapshotInterval;
-    /** the default java command for groups */
-    private transient String[] command;
-    /** timeout on wait for child process to be created or destroyed */
-    private transient long groupTimeout;
-    /** timeout on wait for unexport to succeed */
-    private transient long unexportTimeout;
-    /** timeout on wait between unexport attempts */
-    private transient long unexportWait;
-    /** ActivatorImpl instance */
-    private transient Activator activator;
-    /** exporter for activator */
-    private transient Exporter activatorExporter;
-    /** stub for activator */
-    private transient Activator activatorStub;
-    /** SystemImpl instance */
-    private transient ActivationSystem system;
-    /** exporter for system */
-    private transient Exporter systemExporter;
-    /** stub for system */
-    private transient ActivationSystem systemStub;
-    /** MonitorImpl instance */
-    private transient ActivationMonitor monitor;
-    /** exporter for monitor */
-    private transient Exporter monitorExporter;
-    /** stub for monitor */
-    private transient ActivationMonitor monitorStub;
-    /** RegistryImpl instance */
-    private transient Registry registry;
-    /** exporter for registry */
-    private transient Exporter registryExporter;
-    /** stub for registry */
-    private transient Registry registryStub;
-    /** MarshalledObject(ActivationGroupData) or null */
-    private transient MarshalledObject groupData;
-    /** Location of ActivationGroupImpl or null */
-    private transient String groupLocation;
-    /** preparer for ActivationInstantiators */
-    private transient ProxyPreparer groupPreparer;
-    private transient GroupOutputHandler outputHandler;
-    /** true if shutdown has been called */
-    private volatile boolean shuttingDown = false;
-    /** Runtime shutdown hook */
-    private transient Thread shutdownHook;
-    /** Non-null if phoenix was started by the service starter */
-    private transient PhoenixStarter starter;
-
-    /**
-     * Create an uninitialized instance of Activation that can be
-     * populated with log data.  This is only called when the initial
-     * snapshot is taken during the first incarnation of phoenix.
-     */
-    private Activation() {}
-
-    private void init(ReliableLog log,
-		      LoginContext login,
-		      Configuration config,
-		      String[] configOptions,
-		      PhoenixStarter starter)
-	throws Exception
-    {
-	this.log = log;
-	this.login = login;
-	this.starter = starter;
-	groupSemaphore = getInt(config, "groupThrottle", 3);
-	snapshotInterval = getInt(config, "persistenceSnapshotThreshold", 200);
-	groupTimeout = getInt(config, "groupTimeout", 60000);
-	unexportTimeout = getInt(config, "unexportTimeout", 60000);
-	unexportWait = getInt(config, "unexportWait", 10);
-	String[] opts = (String[]) config.getEntry(
-		PHOENIX, "groupOptions", String[].class, new String[0]);
-	command = new String[opts.length + 2];
-	command[0] = (System.getProperty("java.home") +
-		      File.separator + "bin" + File.separator + "java");
-	System.arraycopy(opts, 0, command, 1, opts.length);
-	command[command.length - 1] =
-	    "com.sun.jini.phoenix.ActivationGroupInit";
-	shutdownHook = new ShutdownHook();
-	Runtime.getRuntime().addShutdownHook(shutdownHook);
-	groupPreparer = getPreparer(config, "instantiatorPreparer");
-	groupData = new MarshalledInstance(
-            new ActivationGroupData((String[]) config.getEntry(
-                    PHOENIX, 
-                    "groupConfig",
-                    String[].class,
-                    configOptions
-            ))).convertToMarshalledObject();
-	outputHandler = (GroupOutputHandler) config.getEntry(
-		PHOENIX, "groupOutputHandler", GroupOutputHandler.class,
-		new GroupOutputHandler() {
-		    public void handleOutput(ActivationGroupID id,
-					     ActivationGroupDesc desc,
-					     long incarnation,
-					     String groupName,
-					     InputStream out,
-					     InputStream err)
-		    {
-  			PipeWriter.plugTogetherPair(
-				groupName, out, System.out, err, System.err);
-		    }
-		});
-	
-	groupLocation = (String)
-	    config.getEntry(PHOENIX, "groupLocation",
-			    String.class, getDefaultGroupLocation());
-	
-	ActivationGroupID[] gids = (ActivationGroupID[])
-	    groupTable.keySet().toArray(
-				    new ActivationGroupID[groupTable.size()]);
-	activator = new ActivatorImpl();
-	ServerEndpoint se = TcpServerEndpoint.getInstance(PHOENIX_PORT);
-	activatorExporter =
-	    getExporter(config, "activatorExporter",
-			new BasicJeriExporter(se, new BasicILFactory(),
-					      false, true,
-					     PhoenixConstants.ACTIVATOR_UUID));
-	system = new SystemImpl();
-	systemExporter =
-	    getExporter(config, "systemExporter",
-			new BasicJeriExporter(se, new SystemAccessILFactory(),
-					      false, true,
-			             PhoenixConstants.ACTIVATION_SYSTEM_UUID));
-	monitor = new MonitorImpl();
-	monitorExporter =
-	    getExporter(config, "monitorExporter",
-			new BasicJeriExporter(se, new AccessILFactory()));
-	registry = new RegistryImpl();
-	registryExporter =
-	    getExporter(config, "registryExporter", new RegistrySunExporter());
-	monitorStub = (ActivationMonitor) monitorExporter.export(monitor);
-	synchronized (activatorExporter) {
-	    systemStub = (ActivationSystem) systemExporter.export(system);
-	    activatorStub = (Activator) activatorExporter.export(activator);
-            activatorExporter.notifyAll();
-	}
-	registryStub = (Registry) registryExporter.export(registry);
-	logger.info(getTextResource("phoenix.daemon.started"));
-	for (int i = gids.length; --i >= 0; ) {
-	    try {
-		getGroupEntry(gids[i]).restartServices();
-	    } catch (UnknownGroupException e) {
-	    }
-	}
-    }
-
-    private static String getDefaultGroupLocation() {
-	ProtectionDomain pd = Activation.class.getProtectionDomain();
-	CodeSource cs = pd.getCodeSource();
-	URL location = null;
-	if (cs != null) {
-	    location = cs.getLocation();
-	}
-	if (location != null) {
-	    String loc = location.toString();
-	    if (loc.endsWith(".jar")) {
-		return loc.substring(0, loc.length() - 4) + "-group.jar";
-	    }
-	}
-	return null;
-    }
-
-    /**
-     * Return a configuration for the specified options.
-     */
-    private static Configuration getConfig(String[] configOptions,
-					   PhoenixStarter starter)
-	throws ConfigurationException
-    {
-	try {
-	    return ConfigurationProvider.getInstance(
-		configOptions, Activation.class.getClassLoader());
-	    
-	} catch (ConfigurationNotFoundException e) {
-	    if (starter == null) {
-		bomb("phoenix.missing.config",
-		     Arrays.asList(configOptions).toString());
-	    }
-	    throw e;
-	}
-    }
-
-    /**
-     * Return an int configuration entry.
-     */
-    private static int getInt(Configuration config, String name, int defValue)
-	throws ConfigurationException
-    {
-	return ((Integer) config.getEntry(PHOENIX, name, int.class,
-					  Integer.valueOf(defValue))).intValue();
-    }
-
-    /**
-     * Return the exporter with the specified name from the specified
-     * configuration.
-     */
-    private static Exporter getExporter(Configuration config,
-					String name,
-					Exporter defaultExporter)
-	throws ConfigurationException
-    {
-	return (Exporter)
-	    config.getEntry(PHOENIX, name, Exporter.class, defaultExporter);
-    }
-
-    /**
-     * Return a ProxyPreparer configuration entry.
-     */
-    private static ProxyPreparer getPreparer(Configuration config, String name)
-	throws ConfigurationException
-    {
-	return (ProxyPreparer) config.getEntry(PHOENIX, name,
-					       ProxyPreparer.class,
-					       new BasicProxyPreparer());
-    }
-
-    class ActivatorImpl extends AbstractActivator implements ServerProxyTrust {
-	ActivatorImpl() {
-	}
-    
-	public MarshalledWrapper activate(ActivationID id, boolean force)
-    	    throws ActivationException
-	{
-	    UID uid = getUID(id);
-	    return getGroupEntry(uid).activate(uid, force);
-	}
-
-	public TrustVerifier getProxyVerifier() {
-	    return new ConstrainableAID.Verifier(activatorStub);
-	}
-    }
-
-    class MonitorImpl extends AbstractMonitor implements ServerProxyTrust {
-	MonitorImpl() {
-	}
-	
-	public void inactiveObject(ActivationID id)
-	    throws UnknownObjectException
-	{
-	    UID uid = getUID(id);
-	    getGroupEntry(uid).inactiveObject(uid);
-	}
-    
-	public void activeObject(ActivationID id, MarshalledObject mobj)
-    	    throws UnknownObjectException
-	{
-	    UID uid = getUID(id);
-	    getGroupEntry(uid).activeObject(uid, mobj);
-	}
-	
-	public void inactiveGroup(ActivationGroupID id,
-				  long incarnation)
-	    throws UnknownGroupException
-	{
-	    getGroupEntry(id).inactiveGroup(incarnation, false);
-	}
-	
-	public TrustVerifier getProxyVerifier() {
-	    return new BasicProxyTrustVerifier(monitorStub);
-	}
-    }
-    
-    class SystemImpl extends AbstractSystem implements ServerProxyTrust {
-	SystemImpl() {
-	}
-	
-	/** returns a ConstrainableAID */
-	public ActivationID registerObject(ActivationDesc desc)
-	    throws ActivationException
-	{
-	    UID uid = new UID();
-	    ActivationGroupID groupID = desc.getGroupID();
-	    getGroupEntry(groupID).registerObject(uid, desc, true);
-	    synchronized (activatorExporter) {
-		return getAID(uid);
-	    }
-	}
-
-	public void unregisterObject(ActivationID id)
-	    throws ActivationException
-	{
-	    UID uid = getUID(id);
-	    getGroupEntry(getGroupID(uid)).unregisterObject(uid, true);
-	}
-	
-	public ActivationGroupID registerGroup(ActivationGroupDesc desc)
-	    throws ActivationException
-	{
-	    ActivationGroupID id = new ActivationGroupID(systemStub);
-	    synchronized (logLock) {
-		addLogRecord(new LogRegisterGroup(id, desc));
-		GroupEntry entry = new GroupEntry(id, desc);
-		synchronized (groupTable) {
-		    groupTable.put(id, entry);
-		}
-	    }
-	    return id;
-	}
-	
-	public ActivationMonitor activeGroup(ActivationGroupID id,
-					     ActivationInstantiator group,
-					     long incarnation)
-	    throws ActivationException, RemoteException
-	{
-	    group = (ActivationInstantiator) groupPreparer.prepareProxy(group);
-	    getGroupEntry(id).activeGroup(group, incarnation);
-	    return monitorStub;
-	}
-	
-	public void unregisterGroup(ActivationGroupID id)
-	    throws ActivationException
-	{
-	    GroupEntry groupEntry;
-	    synchronized (groupTable) {
-		groupEntry = getGroupEntry(id);		
-		groupTable.remove(id);
-	    }
-	    groupEntry.unregisterGroup(true);
-	}
-
-	public ActivationDesc setActivationDesc(ActivationID id,
-						ActivationDesc desc)
-	    throws ActivationException
-	{
-	    UID uid = getUID(id);
-	    if (!getGroupID(uid).equals(desc.getGroupID())) {
-		throw new ActivationException(
-				       "ActivationDesc contains wrong group");
-	    }
-	    return getGroupEntry(uid).setActivationDesc(uid, desc, true);
-	}
-
-	public ActivationGroupDesc setActivationGroupDesc(
-						     ActivationGroupID id,
-						     ActivationGroupDesc desc)
-	    throws ActivationException
-	{
-	    return getGroupEntry(id).setActivationGroupDesc(id, desc, true);
-	}
-
-	public ActivationDesc getActivationDesc(ActivationID id)
-	    throws UnknownObjectException
-	{
-	    UID uid = getUID(id);
-	    return getGroupEntry(uid).getActivationDesc(uid);
-	}
-	      
-	public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID id)
-	    throws UnknownGroupException
-	{
-	    GroupEntry entry = getGroupEntry(id);
-            synchronized (entry){
-                return entry.desc;
-            }
-	}
-	
-	public void shutdown() {
-	    synchronized (Activation.this) {
-		if (!shuttingDown) {
-		    shuttingDown = true;
-		    new Shutdown().start();
-		}
-	    }
-	}
-
-	public Map<ActivationGroupID,ActivationGroupDesc> getActivationGroups() {
-            Collection<GroupEntry> entries;
-            synchronized (groupTable){
-                entries = new ArrayList<GroupEntry>(groupTable.values());
-            }
-            // release lock on groupTable before obtaining lock on entry.
-            Map<ActivationGroupID,ActivationGroupDesc> map 
-                    = new HashMap<ActivationGroupID,ActivationGroupDesc>(entries.size());
-            for (Iterator<GroupEntry> iter = entries.iterator();
-                 iter.hasNext(); )
-            {
-                GroupEntry entry = iter.next();
-                synchronized (entry){
-                    if (!entry.removed) {
-                        map.put(entry.groupID, entry.desc);
-                    }
-                }
-            }
-            return map;
-	    
-	}
-
-	public Map<ActivationID,ActivationDesc> getActivatableObjects(ActivationGroupID id)
-	    throws UnknownGroupException
-	{
-	    synchronized (activatorExporter) {
-                // to wait for it to be exported
-                while (activatorStub == null){
-                    try {
-                        activatorExporter.wait();
-                    } catch (InterruptedException ex) {
-                        // restore interrupt.
-                        Thread.currentThread().interrupt();
-                    }
-                }
-	    }
-	    return getGroupEntry(id).getActivatableObjects();
-	}
-	
-	public TrustVerifier getProxyVerifier() {
-	    return new BasicProxyTrustVerifier(systemStub);
-	}
-    }
-
-    /**
-     * A read-only registry containing a single entry for the system.
-     */
-    class RegistryImpl extends AbstractRegistry {
-	/** The name of the single entry */
-	private final String NAME = ActivationSystem.class.getName();
-
-	RegistryImpl() {
-	}
-
-	/**
-	 * Returns the single object if the specified name matches the single
-	 * name, otherwise throws NotBoundException.
-	 */
-	public Remote lookup(String name) throws NotBoundException {
-	    if (name.equals(NAME)) {
-		return systemStub;
-	    }
-	    throw new NotBoundException(name);
-	}
-
-	/** Always throws SecurityException. */
-	public void bind(String name, Remote obj) {
-	    throw new SecurityException("read-only registry");
-	}
-
-	/** Always throws SecurityException. */
-	public void unbind(String name) {
-	    throw new SecurityException("read-only registry");
-	}
-
-	/** Always throws SecurityException. */
-	public void rebind(String name, Remote obj) {
-	    throw new SecurityException("read-only registry");
-	}
-
-	/** Returns a list containing the single name. */
-	public String[] list() {
-	    return new String[]{NAME};
-	}
-    }
-
-    /**
-     * If shutting down, throw an ActivationException.
-     */
-    private void checkShutdown() throws ActivationException {
-	if (shuttingDown) {
-	    throw new ActivationException(
-				       "activation system is shutting down");
-	}
-    }
-
-    /**
-     * Thread to shutdown phoenix.
-     */
-    private class Shutdown extends Thread {
-	Shutdown() {
-	    super("Shutdown");
-	}
-
-	public void run() {
-	    try {
-		long stop = System.currentTimeMillis() + unexportTimeout;
-		boolean force = false;
-		while (!registryExporter.unexport(force) ||
-		       !activatorExporter.unexport(force) ||
-		       !systemExporter.unexport(force) ||
-		       !monitorExporter.unexport(force))
-		{
-		    long rem = stop - System.currentTimeMillis();
-		    if (rem <= 0) {
-			force = true;
-		    } else {
-			try {
-			    Thread.sleep(Math.min(rem, unexportWait));
-			} catch (InterruptedException e) {
-			}
-		    }
-		}
-		// destroy all child processes (groups)
-		GroupEntry[] groupEntries;
-		synchronized (groupTable) {
-		    groupEntries = groupTable.values().toArray(new GroupEntry[groupTable.size()]);
-		}
-		for (int i = 0; i < groupEntries.length; i++) {
-		    groupEntries[i].shutdown();
-		}
-		
-		Runtime.getRuntime().removeShutdownHook(shutdownHook);
-		try {
-		    synchronized (logLock) {
-			log.close();
-		    }
-		} catch (IOException e) {
-		}
-		try {
-		    if (login != null) {
-			login.logout();
-		    }
-		} catch (LoginException e) {
-		}
-	    } catch (Throwable t) {
-		logger.log(Level.WARNING, "exception during shutdown", t);
-	    } finally {
-		logger.info(getTextResource("phoenix.daemon.shutdown"));
-		if (starter == null) {
-		    System.exit(0);
-		} else {
-		    starter.unregister();
-		}
-	    }
-	}
-    }
-    
-    /** Thread to destroy children in the event of abnormal termination. */
-    private class ShutdownHook extends Thread {
-	ShutdownHook() {
-	    super("Phoenix Shutdown Hook");
-	}
-
-	public void run() {
-	    synchronized (Activation.this) {
-		shuttingDown = true;
-	    }
-	    // destroy all child processes (groups) quickly
-	    synchronized (groupTable) {
-		for (Iterator<GroupEntry> iter = groupTable.values().iterator();
-		     iter.hasNext(); )
-		{
-		    iter.next().shutdownFast();
-		}
-	    }
-	}
-    }
-
-    private ActivationID getAID(UID uid) {
-	if (activatorStub instanceof RemoteMethodControl) {
-	    return new ConstrainableAID(activatorStub, uid);
-	} else {
-	    return new AID(activatorStub, uid);
-	}
-    }
-
-    private static UID getUID(ActivationID id) throws UnknownObjectException {
-	Class c = id.getClass();
-	if (c == AID.class || c == ConstrainableAID.class) {
-	    return ((AID) id).getUID();
-	}
-	throw new UnknownObjectException("object unknown");
-    }
-
-    /**
-     * Returns the groupID for a given id of an object in the group.
-     * Throws UnknownObjectException if the object is not registered.
-     */
-    private ActivationGroupID getGroupID(UID uid)
-	throws UnknownObjectException
-    {
-	synchronized (idTable) {
-	    ActivationGroupID groupID = idTable.get(uid);
-	    if (groupID != null) {
-		return groupID;
-	    }
-	}
-	throw new UnknownObjectException("object unknown");
-    }
-    
-    /**
-     * Returns the group entry for the group id. Throws
-     * UnknownGroupException if the group is not registered.
-     */
-    private GroupEntry getGroupEntry(ActivationGroupID id)
-	throws UnknownGroupException
-    {
-	if (id.getClass() == ActivationGroupID.class) {
-            GroupEntry entry;
-	    synchronized (groupTable) {
-		entry = groupTable.get(id);
-	    }
-            // groupTable lock is released before obtaining lock on entry.
-            if (entry != null){
-                synchronized (entry){
-                    if (!entry.removed) {
-                        return entry;
-                    }
-                }
-            }
-	}
-	throw new UnknownGroupException("group unknown");
-    }
-
-    /**
-     * Returns the group entry for the object's id. Throws
-     * UnknownObjectException if the object is not registered.
-     */
-    private GroupEntry getGroupEntry(UID uid) throws UnknownObjectException {
-	ActivationGroupID gid = getGroupID(uid);
-	synchronized (groupTable) {
-	    GroupEntry entry = groupTable.get(gid);
-	    if (entry != null) {
-		return entry;
-	    }
-	}
-	throw new UnknownObjectException("object's group removed");
-    }
-
-    /**
-     * Container for group information: group's descriptor, group's
-     * instantiator, flag to indicate pending group creation, and
-     * table of the group's active objects.
-     *
-     * WARNING: GroupEntry objects should not be written into log file
-     * updates.  GroupEntrys are inner classes of Activation and they
-     * can not be serialized independent of this class.  If the
-     * complete Activation system is written out as a log update, the
-     * point of having updates is nullified.  
-     */
-    private class GroupEntry implements Serializable {
-	
-	private static final long serialVersionUID = 7222464070032993304L;
-	private static final int MAX_TRIES = 2;
-	private static final int NORMAL = 0;
-	private static final int CREATING = 1;
-	private static final int TERMINATE = 2;
-	private static final int TERMINATING = 3;
-	
-        /* 15th May 2013 - Peter Firmstone.
-         * 
-         * I suspect the original designer made a
-         * compromise with visibility to avoid deadlock, lock
-         * ordering should be revisited.
-         * 
-         * Current lock order:
-         * 1. sync (this)
-         * 2. sync (logLock)
-         * 3. sync (idTable)
-         * 
-         * This lock order appears to be deliberately avoided:
-         * 1. sync (groupTable)
-         * 2. sync (this)
-         * 
-         * Instead of avoiding locking, I've released each lock before obtaining
-         * the next:
-         * 1. sync (groupTable)
-         * 2. end sync.
-         * 3. sync (this)
-         * 4. end sync.
-         *
-         * Although this is not atomic, it guarantees visiblity, which is 
-         * better than no locking at all.
-         */
-        
-        
-	ActivationGroupDesc desc;
-	ActivationGroupID groupID;
-	long incarnation = 0;
-	Map<UID,ObjectEntry> objects = new HashMap<UID,ObjectEntry>(11);
-	HashSet<UID> restartSet = new HashSet<UID>();
-	
-	transient ActivationInstantiator group = null;
-	transient int status = NORMAL;
-	transient long waitTime = 0;
-	transient String groupName = null;
-	transient Process child = null;
-	transient boolean removed = false;
-	transient Watchdog watchdog = null;
-	
-	GroupEntry(ActivationGroupID groupID, ActivationGroupDesc desc) {
-	    this.groupID = groupID;
-	    this.desc = desc;
-	}
-
-        
-	void restartServices() {
-	    Iterator<UID> iter = null;
-	    
-	    synchronized (this) {
-		if (restartSet.isEmpty()) {
-		    return;
-		}
-
-		/*
-		 * Clone the restartSet so the set does not have to be locked
-		 * during iteration. Locking the restartSet could cause
-		 * deadlock if an object we are restarting caused another
-		 * object in this group to be activated.
-		 */
-                
-		iter = new HashSet<UID>(restartSet).iterator();
-	    }
-	    
-	    while (iter.hasNext()) {
-		UID uid = iter.next();
-		try {
-		    activate(uid, true);
-		} catch (Exception e) {
-		    if (shuttingDown) {
-			return;
-		    }
-		    logger.log(Level.WARNING, "unable to restart service", e);
-		}
-	    }
-	}
-	
-	synchronized void activeGroup(ActivationInstantiator inst,
-				      long instIncarnation)
-	    throws ActivationException
-	{
-	    if (group != null && group.equals(inst) &&
-		incarnation == instIncarnation)
-	    {
-		return;
-	    } else if (child != null && status != CREATING) {
-		throw new ActivationException("group not being created");
-	    } else if (incarnation != instIncarnation) {
-		throw new ActivationException("invalid incarnation");
-	    } else if (group != null) {
-		throw new ActivationException("group already active");
-	    }
-	    
-	    group = inst;
-	    status = NORMAL;
-	    notifyAll();
-	}
-
-	private void checkRemoved() throws UnknownGroupException {
-	    if (removed) {
-		throw new UnknownGroupException("group removed");
-	    }
-	}
-
-	private ObjectEntry getObjectEntry(UID uid)
-	    throws UnknownObjectException
-	{
-	    if (removed) {
-		throw new UnknownObjectException("object's group removed");
-	    }
-	    ObjectEntry objEntry = objects.get(uid);
-	    if (objEntry == null) {
-		throw new UnknownObjectException("object unknown");
-	    }
-	    return objEntry;
-	}
-
-	synchronized void registerObject(UID uid, 
-					 ActivationDesc desc,
-					 boolean addRecord)
-    	    throws ActivationException
-	{
-	    checkRemoved();
-	    synchronized (logLock) {
-		if (addRecord) {
-		    addLogRecord(new LogRegisterObject(uid, desc));
-		}
-		objects.put(uid, new ObjectEntry(desc));
-		if (desc.getRestartMode()) {
-		    restartSet.add(uid);
-		}	    
-		synchronized (idTable) {
-		    idTable.put(uid, groupID);
-		}
-	    }
-	}
-
-	synchronized void unregisterObject(UID uid, boolean addRecord)
-    	    throws ActivationException
-	{
-	    ObjectEntry objEntry = getObjectEntry(uid);
-	    synchronized (logLock) {
-		if (addRecord) {
-		    addLogRecord(new LogUnregisterObject(uid));
-		}
-		objEntry.removed();
-		objects.remove(uid);
-		if (objEntry.desc.getRestartMode()) {
-		    restartSet.remove(uid);
-		}
-		synchronized (idTable) {
-		    idTable.remove(uid);
-		}
-	    }
-	}
-	
-	synchronized Map<ActivationID,ActivationDesc> getActivatableObjects() {
-	    Map<ActivationID,ActivationDesc> map = new HashMap<ActivationID,ActivationDesc>(objects.size());
-	    for (Iterator<Map.Entry<UID,ObjectEntry>> iter = objects.entrySet().iterator();
-		 iter.hasNext(); )
-	    {
-		Map.Entry<UID,ObjectEntry> ent = iter.next();
-		map.put(getAID(ent.getKey()),ent.getValue().desc);
-	    }
-	    return map;
-	}
-
-	synchronized void unregisterGroup(boolean addRecord)
-    	   throws ActivationException
-	{
-	    checkRemoved();
-	    
-	    synchronized (logLock) {
-		if (addRecord) {
-		    addLogRecord(new LogUnregisterGroup(groupID));
-		}
-		removed = true;
-		for (Iterator<Map.Entry<UID,ObjectEntry>> iter = objects.entrySet().iterator();
-		     iter.hasNext(); )
-		{
-		    Map.Entry<UID,ObjectEntry> ent = iter.next();
-		    UID uid = ent.getKey();
-		    synchronized (idTable) {
-			idTable.remove(uid);
-		    }
-		    ObjectEntry objEntry = ent.getValue();
-		    objEntry.removed();
-		}
-		objects.clear();
-		restartSet.clear();
-		reset();
-		childGone();
-	    }
-	}
-
-	synchronized ActivationDesc setActivationDesc(UID uid,
-						      ActivationDesc desc,
-						      boolean addRecord)
-	    throws ActivationException
-	{
-	    ObjectEntry objEntry = getObjectEntry(uid);
-	    synchronized (logLock) {
-		if (addRecord) {
-		    addLogRecord(new LogUpdateDesc(uid, desc));
-		}
-		ActivationDesc oldDesc = objEntry.desc;
-		objEntry.desc = desc;
-		if (desc.getRestartMode()) {
-		    restartSet.add(uid);
-		} else {
-		    restartSet.remove(uid);
-		}
-		return oldDesc;
-	    }
-	}
-
-	synchronized ActivationDesc getActivationDesc(UID uid)
-	    throws UnknownObjectException
-	{
-	    return getObjectEntry(uid).desc;
-	}
-
-	synchronized ActivationGroupDesc setActivationGroupDesc(
-		ActivationGroupID id,
-		ActivationGroupDesc desc,
-		boolean addRecord)
-    	    throws ActivationException
-	{
-	    checkRemoved();
-	    synchronized (logLock) {
-		if (addRecord) {
-		    addLogRecord(new LogUpdateGroupDesc(id, desc));
-		}
-		ActivationGroupDesc oldDesc = this.desc;
-		this.desc = desc;
-		return oldDesc;
-	    }
-	}
-
-	synchronized void inactiveGroup(long incarnation, boolean failure)
-	    throws UnknownGroupException
-	{
-	    checkRemoved();
-	    if (this.incarnation != incarnation) {
-		throw new UnknownGroupException("invalid incarnation");
-	    }
-	    reset();
-	    if (failure) {
-		terminate();
-	    } else if (child != null && status == NORMAL) {
-		status = TERMINATE;
-		watchdog.noRestart();
-	    }
-	}
-
-	synchronized void activeObject(UID uid, MarshalledObject mobj)
-	    throws UnknownObjectException
-	{
-	    getObjectEntry(uid).stub =
-		new MarshalledWrapper(new MarshalledInstance(mobj));
-	}
-
-	synchronized void inactiveObject(UID uid)
-    	    throws UnknownObjectException
-	{
-	    getObjectEntry(uid).reset();
-	}
-
-	private void reset() {
-	    group = null;
-	    for (Iterator<ObjectEntry> iter = objects.values().iterator(); iter.hasNext(); )
-	    {
-		iter.next().reset();
-	    }
-	}
-	
-	private void childGone() {
-	    if (child != null) {
-		child = null;
-		watchdog.dispose();
-		watchdog = null;
-		status = NORMAL;
-		notifyAll();
-	    }
-	}
-
-	private void terminate() {
-	    if (child != null && status != TERMINATING) {
-		child.destroy();
-		status = TERMINATING;
-		waitTime = System.currentTimeMillis() + groupTimeout;
-		notifyAll();
-	    }
-	}
-
-	private void await() {
-	    while (true) {
-		switch (status) {
-		case NORMAL:
-		    return;
-		case TERMINATE:
-		    terminate();
-		case TERMINATING:
-		    try {
-			child.exitValue();
-		    } catch (IllegalThreadStateException e) {
-			long now = System.currentTimeMillis();
-			if (waitTime > now) {
-			    try {
-				wait(waitTime - now);
-			    } catch (InterruptedException ee) {
-			    }
-			    continue;
-			}
-			logger.log(Level.WARNING,
-				   "group did not terminate: {0}", groupName);
-		    }
-		    childGone();
-		    return;
-		case CREATING:
-		    try {
-			wait();
-		    } catch (InterruptedException e) {
-		    }
-		}
-	    }
-	}
-
-	// no synchronization to avoid delay wrt getInstantiator
-	void shutdownFast() {
-	    Process p = child;
-	    if (p != null) {
-		p.destroy();
-	    }
-	}
-
-	synchronized void shutdown() {
-	    reset();
-	    terminate();
-	    await();
-	}
-
-	MarshalledWrapper activate(UID uid, boolean force)
-	    throws ActivationException
-	{
-	    Exception detail = null;
-
-	    /*
-	     * Attempt to activate object and reattempt (several times)
-	     * if activation fails due to communication problems.
-	     */
-	    for (int tries = MAX_TRIES; tries > 0; tries--) {
-		ActivationInstantiator inst;
-		long curIncarnation;
-
-		// look up object to activate
-		ObjectEntry objEntry;
-		synchronized (this) {
-		    objEntry = getObjectEntry(uid);
-		    // if not forcing activation, return cached stub
-		    if (!force && objEntry.stub != null) {
-			return objEntry.stub;
-		    }
-		    inst = getInstantiator(groupID);
-		    curIncarnation = incarnation;
-		}
-
-		boolean groupInactive = false;
-		boolean failure = false;
-		// activate object
-		try {
-		    return objEntry.activate(uid, force, inst);
-		} catch (NoSuchObjectException e) {
-		    groupInactive = true;
-		    detail = e;
-		} catch (ConnectException e) {
-		    groupInactive = true;
-		    failure = true;
-		    detail = e;
-		} catch (ConnectIOException e) {
-		    groupInactive = true;
-		    failure = true;
-		    detail = e;
-		} catch (InactiveGroupException e) {
-		    groupInactive = true;
-		    detail = e;
-		} catch (RemoteException e) {
-		    // REMIND: wait some here before continuing?
-		    if (detail == null) {
-			detail = e;
-		    }
-		}
-		
-		if (groupInactive) {
-		    // group has failed; mark inactive
-		    try {
-			getGroupEntry(groupID).inactiveGroup(curIncarnation,
-							     failure);
-		    } catch (UnknownGroupException e) {
-			// not a problem
-		    }
-		}
-	    }
-
-	    /** 
-	     * signal that object activation failed, nested exception
-	     * specifies what exception occurred when the object did not
-	     * activate 
-	     */
-	    throw new ActivationException("object activation failed after " +
-					  MAX_TRIES + " tries", detail);
-	}
-
-	/**
-	 * Returns the instantiator for the group specified by id and
-	 * entry. If the group is currently inactive, exec some
-	 * bootstrap code to create the group.
-	 */
-	private ActivationInstantiator getInstantiator(ActivationGroupID id)
-	    throws ActivationException
-	{
-	    await();
-	    if (group != null) {
-		return group;
-	    }
-	    checkRemoved();
-	    boolean acquired = false;
-	    try {
-		groupName = Pstartgroup();
-		acquired = true;
-		String[] argv = activationArgs(desc);
-		if (logger.isLoggable(Level.FINE)) {
-		    logger.log(Level.FINE, "{0} exec {1}",
-			       new Object[]{groupName, Arrays.asList(argv)});
-		}
-		try {
-		    child = Runtime.getRuntime().exec(argv);
-		    status = CREATING;
-		    ++incarnation;
-		    watchdog = new Watchdog();
-		    watchdog.start();
-
-		    synchronized (logLock) {
-			addLogRecord(
-			    new LogGroupIncarnation(id, incarnation));
-		    }
-		    
-		    outputHandler.handleOutput(id, desc, incarnation,
-					       groupName,
-					       child.getInputStream(),
-					       child.getErrorStream());
-		    MarshalOutputStream out =
-			new MarshalOutputStream(child.getOutputStream(),
-						Collections.EMPTY_LIST);
-		    out.writeObject(id);
-		    ActivationGroupDesc gd = desc;
-		    if (gd.getClassName() == null) {
-			MarshalledObject data = gd.getData();
-			if (data == null) {
-			    data = groupData;
-			}
-			String loc = gd.getLocation();
-			if (loc == null) {
-			    loc = groupLocation;
-			}
-			gd = new ActivationGroupDesc(
-				"com.sun.jini.phoenix.ActivationGroupImpl",
-				loc,
-				data,
-				gd.getPropertyOverrides(),
-				gd.getCommandEnvironment());
-		    }
-		    out.writeObject(gd);
-		    out.writeLong(incarnation);
-		    out.flush();
-		    out.close();
-		    
-		} catch (Exception e) {
-		    terminate();
-		    if (e instanceof ActivationException) {
-			throw (ActivationException) e;
-		    } else {
-			throw new ActivationException(
-				"unable to create activation group", e);
-		    }
-		}
-		try {
-		    long now = System.currentTimeMillis();
-		    long stop = now + groupTimeout;
-		    do {
-			wait(stop - now);
-			if (group != null) {
-			    return group;
-			}
-			now = System.currentTimeMillis();
-			// protect against premature return from wait
-		    } while (status == CREATING && now < stop);
-		} catch (InterruptedException e) {
-		}
-		terminate();
-		throw new ActivationException(
-					    "timeout creating child process");
-	    } finally {
-		if (acquired) {
-		    Vstartgroup();
-		}
-	    }
-	}
-
-	/**
-	 * Waits for process termination and then restarts services.
-	 */
-	private class Watchdog extends Thread {
-	    private Process groupProcess = child;
-	    private long groupIncarnation = incarnation;
-	    private boolean canInterrupt = true;
-	    private boolean shouldQuit = false;
-	    private boolean shouldRestart = true;
-
-	    Watchdog() {
-		super("Watchdog-" + groupName + "-" + incarnation);
-		setDaemon(true);
-	    }
-
-	    public void run() {
-		if (shouldQuit) {
-		    return;
-		}
-		/*
-		 * Wait for the group to crash or exit.
-		 */
-		try {
-		    groupProcess.waitFor();
-		} catch (InterruptedException exit) {
-		    return;
-		}
-
-		boolean restart = false;
-		synchronized (GroupEntry.this) {
-		    if (shouldQuit) {
-			return;
-		    }
-		    canInterrupt = false;
-		    interrupted(); // clear interrupt bit
-		    /*
-		     * Since the group crashed, we should
-		     * reset the entry before activating objects
-		     */
-		    if (groupIncarnation == incarnation) {
-			restart = shouldRestart && !shuttingDown;
-			reset();
-			childGone();
-		    }
-		}
-		
-		/*
-		 * Activate those objects that require restarting
-		 * after a crash.
-		 */
-		if (restart) {
-		    restartServices();
-		}
-	    }
-
-	    /** 
-	     * Marks this thread as one that is no longer needed.
-	     * If the thread is in a state in which it can be interrupted,
-	     * then the thread is interrupted.
-	     */
-	    void dispose() {
-		shouldQuit = true;
-		if (canInterrupt) {
-		    interrupt();
-		}
-	    }
-
-	    /**
-	     * Marks this thread as no longer needing to restart objects.
-	     */
-	    void noRestart() {
-		shouldRestart = false;
-	    }
-	}
-    }
-	
-    private String[] activationArgs(ActivationGroupDesc desc) {
-	ActivationGroupDesc.CommandEnvironment cmdenv;
-	cmdenv = desc.getCommandEnvironment();
-
-	// argv is the literal command to exec
-	List<String> argv = new ArrayList<String>();
-
-	// Command name/path
-	argv.add((cmdenv != null && cmdenv.getCommandPath() != null)
-		    ? cmdenv.getCommandPath()
-		    : command[0]);
-
-	// Group-specific command options
-	if (cmdenv != null && cmdenv.getCommandOptions() != null) {
-	    argv.addAll(Arrays.asList(cmdenv.getCommandOptions()));
-	}
-
-	// Properties become -D parameters
-	Properties props = desc.getPropertyOverrides();
-	if (props != null) {
-	    for (Enumeration p = props.propertyNames(); p.hasMoreElements(); )
-	    {
-		String name = (String) p.nextElement();
-		/* Note on quoting: it would be wrong
-		 * here, since argv will be passed to
-		 * Runtime.exec, which should not parse
-		 * arguments or split on whitespace.
-		 */
-		argv.add("-D" + name + "=" + props.getProperty(name));
-	    }
-	}
-
-	// finally, phoenix-global command options and the classname 
-	int i;
-	for (i = 1; i < command.length; i++) {
-	    argv.add(command[i]);
-	}
-
-	String[] realArgv = new String[argv.size()];
-	System.arraycopy(argv.toArray(), 0, realArgv, 0,
-			 realArgv.length);
-
-	return (realArgv);
-    }
-
-    private class ObjectEntry implements Serializable {
-	private static final long serialVersionUID = -808474359039620126L;
-	/** descriptor for object */
-	ActivationDesc desc;
-	/** the stub (if active) */
-	volatile transient MarshalledWrapper stub = null;
-	volatile transient boolean removed = false;
-
-	ObjectEntry(ActivationDesc desc) {
-	    this.desc = desc;
-	}
-
-	synchronized MarshalledWrapper activate(UID uid,
-						boolean force,
-						ActivationInstantiator inst)
-    	    throws RemoteException, ActivationException
-	{
-	    /* stub could be set to null by a concurrent group reset */
-	    MarshalledWrapper nstub = stub;
-	    if (removed) {
-		throw new UnknownObjectException("object removed");
-	    } else if (!force && nstub != null) {
-		return nstub;
-	    }
-	    MarshalledInstance marshalledProxy =
-		new MarshalledInstance(inst.newInstance(getAID(uid), desc));
-	    nstub = new MarshalledWrapper(marshalledProxy);
-	    stub = nstub;
-	    return nstub;
-	}
-	
-	void reset() {
-	    stub = null;
-	}
-
-	void removed() {
-	    removed = true;
-	}
-    }
-
-    /**
-     * Adds a record to the activation log. If the number of updates
-     * passes a predetermined threshold, record a snapshot before
-     * adding the record to the log.
-     */
-    private void addLogRecord(LogRecord rec) throws ActivationException {
-	assert Thread.holdsLock(logLock);
-	
-	checkShutdown();
-	if (numUpdates >= snapshotInterval) {
-	    snapshot();
-	}
-	try {
-	    log.update(rec, true);
-	    numUpdates++;
-	} catch (Exception e) {
-	    logger.log(Level.WARNING, "log update throws", e);
-	    snapshot();
-	}
-    }
-
-    private void snapshot() throws ActivationException {
-	assert Thread.holdsLock(logLock);
-	try {
-	    log.snapshot();
-	    numUpdates = 0;
-	} catch (Exception e) {
-	    logger.log(Level.SEVERE, "log snapshot throws", e);
-	    try {
-		system.shutdown();
-	    } catch (RemoteException ignore) {
-		// can't happen
-	    }
-	    throw new ActivationException("log snapshot failed", e);
-	}
-    }
-    
-    /**
-     * Handler for the log that knows how to take the initial snapshot
-     * and apply an update (a LogRecord) to the current state.
-     */
-    private static class ActLogHandler extends LogHandler {
-
-	private Activation state = null;
-	
-        ActLogHandler() {
-	}
-
-	public Activation getState() {
-	    return state;
-	}
-	
-	public void snapshot(OutputStream out) throws Exception {
-	    if (state == null) {
-		state = new Activation();
-	    }
-	    MarshalOutputStream s =
-		new MarshalOutputStream(out, Collections.EMPTY_LIST);
-	    s.writeObject(state);
-	    s.flush();
-	}
-	
-	public void recover(InputStream in) throws Exception {
-	    MarshalInputStream s =
-		new MarshalInputStream(in,
-				       ActLogHandler.class.getClassLoader(),
-				       false, null, Collections.EMPTY_LIST);
-	    s.useCodebaseAnnotations();
-	    state = (Activation) s.readObject();
-	}
-
-	public void writeUpdate(OutputStream out, Object value)
-	    throws Exception
-	{
-	    MarshalOutputStream s =
-		new MarshalOutputStream(out, Collections.EMPTY_LIST);
-	    s.writeObject(value);
-	    s.flush();
-	}
-
-	public void readUpdate(InputStream in) throws Exception {
-	    MarshalInputStream  s =
-		new MarshalInputStream(in,
-				       ActLogHandler.class.getClassLoader(),
-				       false, null, Collections.EMPTY_LIST);
-	    s.useCodebaseAnnotations();
-	    applyUpdate(s.readObject());
-	}
-
-	public void applyUpdate(Object update) throws Exception {
-	    ((LogRecord) update).apply(state);
-	}
-    }
-
-    /**
-     * Abstract class for all log records. The subclass contains
-     * specific update information and implements the apply method
-     * that applies the update information contained in the record
-     * to the current state.
-     */
-    private static abstract class LogRecord implements Serializable {
-
-	private static final long serialVersionUID = 8395140512322687529L;
-	abstract Object apply(Object state) throws Exception;
-    }
-
-    /**
-     * Log record for registering an object.
-     */
-    private static class LogRegisterObject extends LogRecord {
-
-	private static final long serialVersionUID = -6280336276146085143L;
-	private UID uid;
-	private ActivationDesc desc;
-
-	LogRegisterObject(UID uid, ActivationDesc desc) {
-	    this.uid = uid;
-	    this.desc = desc;
-	}
-	
-	Object apply(Object state) {
-	    try {
-		((Activation) state).getGroupEntry(desc.getGroupID()).
-		    registerObject(uid, desc, false);
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-
-    /**
-     * Log record for unregistering an object.
-     */
-    private static class LogUnregisterObject extends LogRecord {
-
-	private static final long serialVersionUID = 6269824097396935501L;
-	private UID uid;
-
-	LogUnregisterObject(UID uid) {
-	    this.uid = uid;
-	}
-	
-	Object apply(Object state) {
-	    try {
-		((Activation) state).getGroupEntry(uid).unregisterObject(
-								  uid, false);
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-
-    /**
-     * Log record for registering a group.
-     */
-    private static class LogRegisterGroup extends LogRecord {
-
-	private static final long serialVersionUID = -1966827458515403625L;
-	private ActivationGroupID id;
-	private ActivationGroupDesc desc;
-
-	LogRegisterGroup(ActivationGroupID id, ActivationGroupDesc desc) {
-	    this.id = id;
-	    this.desc = desc;
-	}
-
-	Object apply(Object state) {
-	    // modify state directly
-	    // can't ask a nonexistent GroupEntry to register itself
-	    ((Activation)state).groupTable.put(id, ((Activation) state).new
-					       GroupEntry(id, desc));
-	    return state;
-	}
-    }
-
-    /**
-     * Log record for updating an activation desc
-     */
-    private static class LogUpdateDesc extends LogRecord {
-
-	private static final long serialVersionUID = 545511539051179885L;
-
-	private UID uid;
-	private ActivationDesc desc;
-
-	LogUpdateDesc(UID uid, ActivationDesc desc) {
-	    this.uid = uid;
-	    this.desc = desc;
-	}
-	
-	Object apply(Object state) {
-	    try {
-		((Activation) state).getGroupEntry(uid).
-		    setActivationDesc(uid, desc, false);
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-    
-    /**
-     * Log record for unregistering a group.
-     */
-    private static class LogUpdateGroupDesc extends LogRecord {
-
-	private static final long serialVersionUID = -1271300989218424337L;
-	private ActivationGroupID id;
-	private ActivationGroupDesc desc;
-
-	LogUpdateGroupDesc(ActivationGroupID id, ActivationGroupDesc desc) {
-	    this.id = id;
-	    this.desc = desc;
-	}
-	
-	Object apply(Object state) {
-	    try {
-		((Activation) state).getGroupEntry(id).
-		    setActivationGroupDesc(id, desc, false);
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-    
-    /**
-     * Log record for unregistering a group.
-     */
-    private static class LogUnregisterGroup extends LogRecord {
-
-	private static final long serialVersionUID = -3356306586522147344L;
-	private ActivationGroupID id;
-
-	LogUnregisterGroup(ActivationGroupID id) {
-	    this.id = id;
-	}
-	
-	Object apply(Object state) {
-	    GroupEntry entry = (GroupEntry)
-		((Activation) state).groupTable.remove(id);
-	    try {
-	    	entry.unregisterGroup(false);
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-
-    /**
-     * Log record for an active group incarnation
-     */
-    private static class LogGroupIncarnation extends LogRecord {
-
-	private static final long serialVersionUID = 4146872747377631897L;
-	private ActivationGroupID id;
-	private long inc;
-
-	LogGroupIncarnation(ActivationGroupID id, long inc) {
-	    this.id = id;
-	    this.inc = inc;
-	}
-
-	Object apply(Object state) {
-	    try {
-		GroupEntry entry = ((Activation) state).getGroupEntry(id);
-                synchronized (entry){
-                    entry.incarnation = inc;
-                }
-	    } catch (Exception e) {
-		logger.log(Level.WARNING, "log recovery throws", e);
-	    }
-	    return state;
-	}
-    }
-    
-    private static void usage() {
- 	System.err.println(
-		    MessageFormat.format(getTextResource("phoenix.usage"),
-		    new String[] {Activation.class.getName()}));
-	System.exit(1);
-    }
-
-    private static void bomb(String error) {
-	System.err.println("phoenix: " + error); // $NON-NLS$
-	usage();
-    }
-
-    private static void bomb(String res, String val) {
-	bomb(MessageFormat.format(getTextResource(res), new String[] {val}));
-    }
-
-    /**
-     * Starts phoenix. See the
-     * <a href="package-summary.html#package_description">package
-     * documentation</a> for details.
-     *
-     * @param args command line options
-     */
-    public static void main(String[] args) {
-	if (System.getSecurityManager() == null) {
-	    System.setSecurityManager(new SecurityManager());
-	}
-	boolean stop = false;
-	if (args.length > 0 && args[0].equals("-stop")) {
-	    stop = true;
-	    String[] nargs = new String[args.length - 1];
-	    System.arraycopy(args, 1, nargs, 0, nargs.length);
-	    args = nargs;
-	} else if (args.length == 1 && args[0].equals("-help")) {
-	    usage();
-	}
-	try {
-	    main(args, stop, null);
-	} catch (Exception e) {
-	    System.err.println(MessageFormat.format(
-	                       getTextResource("phoenix.unexpected.exception"),
-			       new Object[]{e.getMessage()}));
-	    e.printStackTrace();
-	    System.exit(1);
-	}
-    }
-
-    /**
-     * Returns the ActivationSystem proxy (used by the PhoenixStarter).
-     */
-    ActivationSystem getActivationSystemProxy() {
-	return systemStub;
-    }
-
-    /**
-     * Starts phoenix and returns a reference to the recovered
-     * <code>Activation</code> instance.
-     *
-     * @param configOptions the configuration options for the configuration
-     * @param stop if <code>true</code>, initiates shutdown of the
-     * activation system on the "registryHost" and "registryPort" obtained
-     * from the configuration
-     * @param starter the <code>PhoenixStarter</code> instance, or
-     * <code>null</code> 
-     **/
-    static Activation main(final String[] configOptions,
-			   final boolean stop,
-			   final PhoenixStarter starter)
-	throws Exception
-    {
-	final Configuration config = getConfig(configOptions, starter);
-	final LoginContext login =
-	    (LoginContext) config.getEntry(PHOENIX, "loginContext",
-					   LoginContext.class, null);
-	if (login != null) {
-	    login.login();
-	}
-	PrivilegedExceptionAction<Activation> action = new PrivilegedExceptionAction<Activation>() {
-	    public Activation run() throws Exception {
-		if (stop) {
-		    assert starter == null;
-		    shutdown(config);
-		    System.exit(0);
-		}
-		String logName = null;
-		try {
-		    logName = (String) config.getEntry(
-			PHOENIX, "persistenceDirectory", String.class);
-		} catch (NoSuchEntryException e) {
-		    if (starter == null) {
-			bomb("phoenix.missing.log", null);
-		    } else {
-			throw e;
-		    }
-		}
-		ActLogHandler handler = new ActLogHandler();
-		ReliableLog log = new ReliableLog(logName, handler);
-		log.recover();
-		Activation state = handler.getState();
-		if (state == null) {
-		    log.snapshot();
-		    state = handler.getState();
-		}
-		state.init(log, login, config, configOptions, starter);
-		if (starter == null) {
-		    // prevent exit
-		    while (true) {
-			try {
-			    Thread.sleep(Long.MAX_VALUE);
-			} catch (InterruptedException e) {
-			}
-		    }
-		} else {
-		    return state;
-		}
-	    }
-	};
-	if (login != null) {
-	    return Subject.doAsPrivileged(login.getSubject(), action, null);
-	} else {
-	    return action.run();
-	}
-    }
-
-    /**
-     * Shut down an activation system daemon, using the specified
-     * configuration location to obtain the host and port of the daemon's
-     * registry, the client constraints for the remote call, and the
-     * permissions to grant to the system proxy.
-     */
-    private static void shutdown(Configuration config) throws Exception {
-	String host = (String) config.getEntry(PHOENIX, "registryHost",
-					       String.class, null);
-	int port = getInt(config, "registryPort",
-			  ActivationSystem.SYSTEM_PORT);
-	Registry reg = LocateRegistry.getRegistry(host, port);
-	ActivationSystem sys =
-	    (ActivationSystem) reg.lookup(ActivationSystem.class.getName());
-	ProxyPreparer sysPreparer = getPreparer(config, "systemPreparer");
-	sys = (ActivationSystem) sysPreparer.prepareProxy(sys);
-	sys.shutdown();
-    }
-
-    /**
-     * Retrieves text resources from the locale-specific properties file.
-     */
-    static String getTextResource(String key) {
-        if (resources == null) return "[missing resource file: " + key + "]";
-       
-	try {
-	    return resources.getString(key);
-	} catch (MissingResourceException mre) {
-	    return "[missing resource: " + key + "]";
-	}
-    }
-
-    /*
-     * Dijkstra semaphore operations to limit the number of subprocesses
-     * phoenix attempts to make at once.
-     */
-
-    /**
-     * Acquire the group semaphore and return a group name.  Each
-     * Pstartgroup must be followed by a Vstartgroup.  The calling thread
-     * will wait until there are fewer than <code>N</code> other threads
-     * holding the group semaphore.  The calling thread will then acquire
-     * the semaphore and return.
-     */
-    private synchronized String Pstartgroup() throws ActivationException {
-	while (true) {
-	    checkShutdown();
-	    // Wait until positive, then decrement.
-	    if (groupSemaphore > 0) {
-		groupSemaphore--;
-		return "Group-" + groupCounter++;
-	    }
-	    try {
-		wait();
-	    } catch (InterruptedException e) {
-	    }
-	}
-    }
-
-    /**
-     * Release the group semaphore.  Every P operation must be
-     * followed by a V operation.  This may cause another thread to
-     * wake up and return from its P operation.
-     */
-    private synchronized void Vstartgroup() {
-	// Increment and notify a waiter (not necessarily FIFO).
-	groupSemaphore++;
-	notifyAll();
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.sun.jini.phoenix;
+
+import com.sun.jini.proxy.BasicProxyTrustVerifier;
+import com.sun.jini.proxy.MarshalledWrapper;
+import com.sun.jini.reliableLog.LogHandler;
+import com.sun.jini.reliableLog.ReliableLog;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.lang.Process;
+import java.net.URL;
+import java.rmi.*;
+import java.rmi.activation.*;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UID;
+import java.security.CodeSource;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.text.MessageFormat;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import net.jini.config.Configuration;
+import net.jini.config.ConfigurationException;
+import net.jini.config.ConfigurationNotFoundException;
+import net.jini.config.ConfigurationProvider;
+import net.jini.config.NoSuchEntryException;
+import net.jini.core.constraint.RemoteMethodControl;
+import net.jini.export.Exporter;
+import net.jini.io.MarshalInputStream;
+import net.jini.io.MarshalOutputStream;
+import net.jini.io.MarshalledInstance;
+import net.jini.jeri.BasicILFactory;
+import net.jini.jeri.BasicJeriExporter;
+import net.jini.jeri.ServerEndpoint;
+import net.jini.jeri.tcp.TcpServerEndpoint;
+import net.jini.security.BasicProxyPreparer;
+import net.jini.security.ProxyPreparer;
+import net.jini.security.TrustVerifier;
+import net.jini.security.proxytrust.ServerProxyTrust;
+
+/**
+ * Phoenix main class.
+ *
+ * @author Sun Microsystems, Inc.
+ * 
+ * @since 2.0
+ */
+class Activation implements Serializable {
+    private static final long serialVersionUID = -6825932652725866242L;
+    private static final String PHOENIX = "com.sun.jini.phoenix";
+    private static final ResourceBundle resources;
+    private static final Logger logger = Logger.getLogger(PHOENIX);
+    private static final int PHOENIX_PORT = 1198;
+    private static final Object logLock = new Object();
+    
+    static {
+        ResourceBundle res = null;
+        try {
+            res = ResourceBundle.getBundle(
+                "com.sun.jini.phoenix.resources.phoenix");
+        } catch (MissingResourceException mre) {
+            logger.log( Level.CONFIG, "[missing resource file]", mre);
+            res = null;
+        }
+        resources = res;
+    }
+
+    /** maps activation id uid to its respective group id */
+    private final Map<UID,ActivationGroupID> idTable = new HashMap<UID,ActivationGroupID>();
+    /** maps group id to its GroupEntry groups */
+    private final Map<ActivationGroupID,GroupEntry> groupTable = new HashMap<ActivationGroupID,GroupEntry>();
+    
+    /** login context */
+    private transient LoginContext login;
+    /** number of simultaneous group exec's */
+    private transient int groupSemaphore;
+    /** counter for numbering groups */
+    private transient int groupCounter = 0;
+    /** persistent store */
+    private transient ReliableLog log;
+    /** number of log updates since last log snapshot */
+    private transient int numUpdates = 0;
+    /** take log snapshot after this many updates */
+    private transient int snapshotInterval;
+    /** the default java command for groups */
+    private transient String[] command;
+    /** timeout on wait for child process to be created or destroyed */
+    private transient long groupTimeout;
+    /** timeout on wait for unexport to succeed */
+    private transient long unexportTimeout;
+    /** timeout on wait between unexport attempts */
+    private transient long unexportWait;
+    /** ActivatorImpl instance */
+    private transient Activator activator;
+    /** exporter for activator */
+    private transient Exporter activatorExporter;
+    /** stub for activator */
+    private transient Activator activatorStub;
+    /** SystemImpl instance */
+    private transient ActivationSystem system;
+    /** exporter for system */
+    private transient Exporter systemExporter;
+    /** stub for system */
+    private transient ActivationSystem systemStub;
+    /** MonitorImpl instance */
+    private transient ActivationMonitor monitor;
+    /** exporter for monitor */
+    private transient Exporter monitorExporter;
+    /** stub for monitor */
+    private transient ActivationMonitor monitorStub;
+    /** RegistryImpl instance */
+    private transient Registry registry;
+    /** exporter for registry */
+    private transient Exporter registryExporter;
+    /** stub for registry */
+    private transient Registry registryStub;
+    /** MarshalledObject(ActivationGroupData) or null */
+    private transient MarshalledObject groupData;
+    /** Location of ActivationGroupImpl or null */
+    private transient String groupLocation;
+    /** preparer for ActivationInstantiators */
+    private transient ProxyPreparer groupPreparer;
+    private transient GroupOutputHandler outputHandler;
+    /** true if shutdown has been called */
+    private volatile boolean shuttingDown = false;
+    /** Runtime shutdown hook */
+    private transient Thread shutdownHook;
+    /** Non-null if phoenix was started by the service starter */
+    private transient PhoenixStarter starter;
+
+    /**
+     * Create an uninitialized instance of Activation that can be
+     * populated with log data.  This is only called when the initial
+     * snapshot is taken during the first incarnation of phoenix.
+     */
+    private Activation() {}
+
+    private void init(ReliableLog log,
+		      LoginContext login,
+		      Configuration config,
+		      String[] configOptions,
+		      PhoenixStarter starter)
+	throws Exception
+    {
+	this.log = log;
+	this.login = login;
+	this.starter = starter;
+	groupSemaphore = getInt(config, "groupThrottle", 3);
+	snapshotInterval = getInt(config, "persistenceSnapshotThreshold", 200);
+	groupTimeout = getInt(config, "groupTimeout", 60000);
+	unexportTimeout = getInt(config, "unexportTimeout", 60000);
+	unexportWait = getInt(config, "unexportWait", 10);
+	String[] opts = (String[]) config.getEntry(
+		PHOENIX, "groupOptions", String[].class, new String[0]);
+	command = new String[opts.length + 2];
+	command[0] = (System.getProperty("java.home") +
+		      File.separator + "bin" + File.separator + "java");
+	System.arraycopy(opts, 0, command, 1, opts.length);
+	command[command.length - 1] =
+	    "com.sun.jini.phoenix.ActivationGroupInit";
+	shutdownHook = new ShutdownHook();
+	Runtime.getRuntime().addShutdownHook(shutdownHook);
+	groupPreparer = getPreparer(config, "instantiatorPreparer");
+	groupData = new MarshalledInstance(
+            new ActivationGroupData((String[]) config.getEntry(
+                    PHOENIX, 
+                    "groupConfig",
+                    String[].class,
+                    configOptions
+            ))).convertToMarshalledObject();
+	outputHandler = (GroupOutputHandler) config.getEntry(
+		PHOENIX, "groupOutputHandler", GroupOutputHandler.class,
+		new GroupOutputHandler() {
+                    @Override
+		    public void handleOutput(ActivationGroupID id,
+					     ActivationGroupDesc desc,
+					     long incarnation,
+					     String groupName,
+					     InputStream out,
+					     InputStream err)
+		    {
+  			PipeWriter.plugTogetherPair(
+				groupName, out, System.out, err, System.err);
+		    }
+		});
+	
+	groupLocation = (String)
+	    config.getEntry(PHOENIX, "groupLocation",
+			    String.class, getDefaultGroupLocation());
+	
+	ActivationGroupID[] gids = (ActivationGroupID[])
+	    groupTable.keySet().toArray(
+				    new ActivationGroupID[groupTable.size()]);
+	activator = new ActivatorImpl();
+	ServerEndpoint se = TcpServerEndpoint.getInstance(PHOENIX_PORT);
+	activatorExporter =
+	    getExporter(config, "activatorExporter",
+			new BasicJeriExporter(se, new BasicILFactory(),
+					      false, true,
+					     PhoenixConstants.ACTIVATOR_UUID));
+	system = new SystemImpl();
+	systemExporter =
+	    getExporter(config, "systemExporter",
+			new BasicJeriExporter(se, new SystemAccessILFactory(),
+					      false, true,
+			             PhoenixConstants.ACTIVATION_SYSTEM_UUID));
+	monitor = new MonitorImpl();
+	monitorExporter =
+	    getExporter(config, "monitorExporter",
+			new BasicJeriExporter(se, new AccessILFactory()));
+	registry = new RegistryImpl();
+	registryExporter =
+	    getExporter(config, "registryExporter", new RegistrySunExporter());
+	monitorStub = (ActivationMonitor) monitorExporter.export(monitor);
+	synchronized (activatorExporter) {
+	    systemStub = (ActivationSystem) systemExporter.export(system);
+	    activatorStub = (Activator) activatorExporter.export(activator);
+            activatorExporter.notifyAll();
+	}
+	registryStub = (Registry) registryExporter.export(registry);
+	logger.info(getTextResource("phoenix.daemon.started"));
+	for (int i = gids.length; --i >= 0; ) {
+	    try {
+		getGroupEntry(gids[i]).restartServices();
+	    } catch (UnknownGroupException e) {
+	    }
+	}
+    }
+
+    private static String getDefaultGroupLocation() {
+	ProtectionDomain pd = Activation.class.getProtectionDomain();
+	CodeSource cs = pd.getCodeSource();
+	URL location = null;
+	if (cs != null) {
+	    location = cs.getLocation();
+	}
+	if (location != null) {
+	    String loc = location.toString();
+	    if (loc.endsWith(".jar")) {
+		return loc.substring(0, loc.length() - 4) + "-group.jar";
+	    }
+	}
+	return null;
+    }
+
+    /**
+     * Return a configuration for the specified options.
+     */
+    private static Configuration getConfig(String[] configOptions,
+					   PhoenixStarter starter)
+	throws ConfigurationException
+    {
+	try {
+	    return ConfigurationProvider.getInstance(
+		configOptions, Activation.class.getClassLoader());
+	    
+	} catch (ConfigurationNotFoundException e) {
+	    if (starter == null) {
+		bomb("phoenix.missing.config",
+		     Arrays.asList(configOptions).toString());
+	    }
+	    throw e;
+	}
+    }
+
+    /**
+     * Return an int configuration entry.
+     */
+    private static int getInt(Configuration config, String name, int defValue)
+	throws ConfigurationException
+    {
+	return ((Integer) config.getEntry(PHOENIX, name, int.class,
+					  Integer.valueOf(defValue))).intValue();
+    }
+
+    /**
+     * Return the exporter with the specified name from the specified
+     * configuration.
+     */
+    private static Exporter getExporter(Configuration config,
+					String name,
+					Exporter defaultExporter)
+	throws ConfigurationException
+    {
+	return (Exporter)
+	    config.getEntry(PHOENIX, name, Exporter.class, defaultExporter);
+    }
+
+    /**
+     * Return a ProxyPreparer configuration entry.
+     */
+    private static ProxyPreparer getPreparer(Configuration config, String name)
+	throws ConfigurationException
+    {
+	return (ProxyPreparer) config.getEntry(PHOENIX, name,
+					       ProxyPreparer.class,
+					       new BasicProxyPreparer());
+    }
+
+    class ActivatorImpl extends AbstractActivator implements ServerProxyTrust {
+	ActivatorImpl() {
+	}
+    
+        @Override
+	public MarshalledWrapper activate(ActivationID id, boolean force)
+    	    throws ActivationException
+	{
+	    UID uid = getUID(id);
+	    return getGroupEntry(uid).activate(uid, force);
+	}
+
+        @Override
+	public TrustVerifier getProxyVerifier() {
+	    return new ConstrainableAID.Verifier(activatorStub);
+	}
+    }
+
+    class MonitorImpl extends AbstractMonitor implements ServerProxyTrust {
+	MonitorImpl() {
+	}
+	
+        @Override
+	public void inactiveObject(ActivationID id)
+	    throws UnknownObjectException
+	{
+	    UID uid = getUID(id);
+	    getGroupEntry(uid).inactiveObject(uid);
+	}
+    
+        @Override
+	public void activeObject(ActivationID id, MarshalledObject mobj)
+    	    throws UnknownObjectException
+	{
+	    UID uid = getUID(id);
+	    getGroupEntry(uid).activeObject(uid, mobj);
+	}
+	
+        @Override
+	public void inactiveGroup(ActivationGroupID id,
+				  long incarnation)
+	    throws UnknownGroupException
+	{
+	    getGroupEntry(id).inactiveGroup(incarnation, false);
+	}
+	
+        @Override
+	public TrustVerifier getProxyVerifier() {
+	    return new BasicProxyTrustVerifier(monitorStub);
+	}
+    }
+    
+    class SystemImpl extends AbstractSystem implements ServerProxyTrust {
+	SystemImpl() {
+	}
+	
+	/** returns a ConstrainableAID */
+        @Override
+	public ActivationID registerObject(ActivationDesc desc)
+	    throws ActivationException
+	{
+	    UID uid = new UID();
+	    ActivationGroupID groupID = desc.getGroupID();
+	    getGroupEntry(groupID).registerObject(uid, desc, true);
+	    synchronized (activatorExporter) {
+		return getAID(uid);
+	    }
+	}
+
+        @Override
+	public void unregisterObject(ActivationID id)
+	    throws ActivationException
+	{
+	    UID uid = getUID(id);
+	    getGroupEntry(getGroupID(uid)).unregisterObject(uid, true);
+	}
+	
+        @Override
+	public ActivationGroupID registerGroup(ActivationGroupDesc desc)
+	    throws ActivationException
+	{
+	    ActivationGroupID id = new ActivationGroupID(systemStub);
+	    synchronized (logLock) {
+		addLogRecord(new LogRegisterGroup(id, desc));
+		GroupEntry entry = new GroupEntry(id, desc);
+		synchronized (groupTable) {
+		    groupTable.put(id, entry);
+		}
+	    }
+	    return id;
+	}
+	
+        @Override
+	public ActivationMonitor activeGroup(ActivationGroupID id,
+					     ActivationInstantiator group,
+					     long incarnation)
+	    throws ActivationException, RemoteException
+	{
+	    group = (ActivationInstantiator) groupPreparer.prepareProxy(group);
+	    getGroupEntry(id).activeGroup(group, incarnation);
+	    return monitorStub;
+	}
+	
+        @Override
+	public void unregisterGroup(ActivationGroupID id)
+	    throws ActivationException
+	{
+	    GroupEntry groupEntry;
+	    synchronized (groupTable) {
+		groupEntry = getGroupEntry(id);		
+		groupTable.remove(id);
+	    }
+	    groupEntry.unregisterGroup(true);
+	}
+
+        @Override
+	public ActivationDesc setActivationDesc(ActivationID id,
+						ActivationDesc desc)
+	    throws ActivationException
+	{
+	    UID uid = getUID(id);
+	    if (!getGroupID(uid).equals(desc.getGroupID())) {
+		throw new ActivationException(
+				       "ActivationDesc contains wrong group");
+	    }
+	    return getGroupEntry(uid).setActivationDesc(uid, desc, true);
+	}
+
+        @Override
+	public ActivationGroupDesc setActivationGroupDesc(
+						     ActivationGroupID id,
+						     ActivationGroupDesc desc)
+	    throws ActivationException
+	{
+	    return getGroupEntry(id).setActivationGroupDesc(id, desc, true);
+	}
+
+        @Override
+	public ActivationDesc getActivationDesc(ActivationID id)
+	    throws UnknownObjectException
+	{
+	    UID uid = getUID(id);
+	    return getGroupEntry(uid).getActivationDesc(uid);
+	}
+	      
+        @Override
+	public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID id)
+	    throws UnknownGroupException
+	{
+	    GroupEntry entry = getGroupEntry(id);
+            synchronized (entry){
+                return entry.desc;
+            }
+	}
+	
+        @Override
+	public void shutdown() {
+	    synchronized (Activation.this) {
+		if (!shuttingDown) {
+		    shuttingDown = true;
+		    new Shutdown().start();
+		}
+	    }
+	}
+
+        @Override
+	public Map<ActivationGroupID,ActivationGroupDesc> getActivationGroups() {
+            Collection<GroupEntry> entries;
+            synchronized (groupTable){
+                entries = new ArrayList<GroupEntry>(groupTable.values());
+            }
+            // release lock on groupTable before obtaining lock on entry.
+            Map<ActivationGroupID,ActivationGroupDesc> map 
+                    = new HashMap<ActivationGroupID,ActivationGroupDesc>(entries.size());
+            for (Iterator<GroupEntry> iter = entries.iterator();
+                 iter.hasNext(); )
+            {
+                GroupEntry entry = iter.next();
+                synchronized (entry){
+                    if (!entry.removed) {
+                        map.put(entry.groupID, entry.desc);
+                    }
+                }
+            }
+            return map;
+	    
+	}
+
+        @Override
+	public Map<ActivationID,ActivationDesc> getActivatableObjects(ActivationGroupID id)
+	    throws UnknownGroupException
+	{
+	    synchronized (activatorExporter) {
+                // to wait for it to be exported
+                while (activatorStub == null){
+                    try {
+                        activatorExporter.wait();
+                    } catch (InterruptedException ex) {
+                        // restore interrupt.
+                        Thread.currentThread().interrupt();
+                    }
+                }
+	    }
+	    return getGroupEntry(id).getActivatableObjects();
+	}
+	
+        @Override
+	public TrustVerifier getProxyVerifier() {
+	    return new BasicProxyTrustVerifier(systemStub);
+	}
+    }
+
+    /**
+     * A read-only registry containing a single entry for the system.
+     */
+    class RegistryImpl extends AbstractRegistry {
+	/** The name of the single entry */
+	private final String NAME = ActivationSystem.class.getName();
+
+	RegistryImpl() {
+	}
+
+	/**
+	 * Returns the single object if the specified name matches the single
+	 * name, otherwise throws NotBoundException.
+	 */
+        @Override
+	public Remote lookup(String name) throws NotBoundException {
+	    if (name.equals(NAME)) {
+		return systemStub;
+	    }
+	    throw new NotBoundException(name);
+	}
+
+	/** Always throws SecurityException. */
+        @Override
+	public void bind(String name, Remote obj) {
+	    throw new SecurityException("read-only registry");
+	}
+
+	/** Always throws SecurityException. */
+        @Override
+	public void unbind(String name) {
+	    throw new SecurityException("read-only registry");
+	}
+
+	/** Always throws SecurityException. */
+        @Override
+	public void rebind(String name, Remote obj) {
+	    throw new SecurityException("read-only registry");
+	}
+
+	/** Returns a list containing the single name. */
+        @Override
+	public String[] list() {
+	    return new String[]{NAME};
+	}
+    }
+
+    /**
+     * If shutting down, throw an ActivationException.
+     */
+    private void checkShutdown() throws ActivationException {
+	if (shuttingDown) {
+	    throw new ActivationException(
+				       "activation system is shutting down");
+	}
+    }
+
+    /**
+     * Thread to shutdown phoenix.
+     */
+    private class Shutdown extends Thread {
+	Shutdown() {
+	    super("Shutdown");
+	}
+
+        @Override
+	public void run() {
+	    try {
+		long stop = System.currentTimeMillis() + unexportTimeout;
+		boolean force = false;
+		while (!registryExporter.unexport(force) ||
+		       !activatorExporter.unexport(force) ||
+		       !systemExporter.unexport(force) ||
+		       !monitorExporter.unexport(force))
+		{
+		    long rem = stop - System.currentTimeMillis();
+		    if (rem <= 0) {
+			force = true;
+		    } else {
+			try {
+			    Thread.sleep(Math.min(rem, unexportWait));
+			} catch (InterruptedException e) {
+			}
+		    }
+		}
+		// destroy all child processes (groups)
+		GroupEntry[] groupEntries;
+		synchronized (groupTable) {
+		    groupEntries = groupTable.values().toArray(new GroupEntry[groupTable.size()]);
+		}
+		for (int i = 0; i < groupEntries.length; i++) {
+		    groupEntries[i].shutdown();
+		}
+		
+		Runtime.getRuntime().removeShutdownHook(shutdownHook);
+		try {
+		    synchronized (logLock) {
+			log.close();
+		    }
+		} catch (IOException e) {
+		}
+		try {
+		    if (login != null) {
+			login.logout();
+		    }
+		} catch (LoginException e) {
+		}
+	    } catch (Throwable t) {
+		logger.log(Level.WARNING, "exception during shutdown", t);
+	    } finally {
+		logger.info(getTextResource("phoenix.daemon.shutdown"));
+		if (starter == null) {
+		    System.exit(0);
+		} else {
+		    starter.unregister();
+		}
+	    }
+	}
+    }
+    
+    /** Thread to destroy children in the event of abnormal termination. */
+    private class ShutdownHook extends Thread {
+	ShutdownHook() {
+	    super("Phoenix Shutdown Hook");
+	}
+
+        @Override
+	public void run() {
+	    synchronized (Activation.this) {
+		shuttingDown = true;
+	    }
+	    // destroy all child processes (groups) quickly
+	    synchronized (groupTable) {
+		for (Iterator<GroupEntry> iter = groupTable.values().iterator();
+		     iter.hasNext(); )
+		{
+		    iter.next().shutdownFast();
+		}
+	    }
+	}
+    }
+
+    private ActivationID getAID(UID uid) {
+	if (activatorStub instanceof RemoteMethodControl) {
+	    return new ConstrainableAID(activatorStub, uid);
+	} else {
+	    return new AID(activatorStub, uid);
+	}
+    }
+
+    private static UID getUID(ActivationID id) throws UnknownObjectException {
+	Class c = id.getClass();
+	if (c == AID.class || c == ConstrainableAID.class) {
+	    return ((AID) id).getUID();
+	}
+	throw new UnknownObjectException("object unknown");
+    }
+
+    /**
+     * Returns the groupID for a given id of an object in the group.
+     * Throws UnknownObjectException if the object is not registered.
+     */
+    private ActivationGroupID getGroupID(UID uid)
+	throws UnknownObjectException
+    {
+	synchronized (idTable) {
+	    ActivationGroupID groupID = idTable.get(uid);
+	    if (groupID != null) {
+		return groupID;
+	    }
+	}
+	throw new UnknownObjectException("object unknown");
+    }
+    
+    /**
+     * Returns the group entry for the group id. Throws
+     * UnknownGroupException if the group is not registered.
+     */
+    private GroupEntry getGroupEntry(ActivationGroupID id)
+	throws UnknownGroupException
+    {
+	if (id.getClass() == ActivationGroupID.class) {
+            GroupEntry entry;
+	    synchronized (groupTable) {
+		entry = groupTable.get(id);
+	    }
+            // groupTable lock is released before obtaining lock on entry.
+            if (entry != null){
+                synchronized (entry){
+                    if (!entry.removed) {
+                        return entry;
+                    }
+                }
+            }
+	}
+	throw new UnknownGroupException("group unknown");
+    }
+
+    /**
+     * Returns the group entry for the object's id. Throws
+     * UnknownObjectException if the object is not registered.
+     */
+    private GroupEntry getGroupEntry(UID uid) throws UnknownObjectException {
+	ActivationGroupID gid = getGroupID(uid);
+	synchronized (groupTable) {
+	    GroupEntry entry = groupTable.get(gid);
+	    if (entry != null) {
+		return entry;
+	    }
+	}
+	throw new UnknownObjectException("object's group removed");
+    }
+
+    /**
+     * Container for group information: group's descriptor, group's
+     * instantiator, flag to indicate pending group creation, and
+     * table of the group's active objects.
+     *
+     * WARNING: GroupEntry objects should not be written into log file
+     * updates.  GroupEntrys are inner classes of Activation and they
+     * can not be serialized independent of this class.  If the
+     * complete Activation system is written out as a log update, the
+     * point of having updates is nullified.  
+     */
+    private class GroupEntry implements Serializable {
+	
+	private static final long serialVersionUID = 7222464070032993304L;
+	private static final int MAX_TRIES = 2;
+	private static final int NORMAL = 0;
+	private static final int CREATING = 1;
+	private static final int TERMINATE = 2;
+	private static final int TERMINATING = 3;
+	
+        /* 15th May 2013 - Peter Firmstone.
+         * 
+         * I suspect the original designer made a
+         * compromise with visibility to avoid deadlock, lock
+         * ordering should be revisited.
+         * 
+         * Current lock order:
+         * 1. sync (this)
+         * 2. sync (logLock)
+         * 3. sync (idTable)
+         * 
+         * This lock order appears to be deliberately avoided:
+         * 1. sync (groupTable)
+         * 2. sync (this)
+         * 
+         * Instead of avoiding locking, I've released each lock before obtaining
+         * the next:
+         * 1. sync (groupTable)
+         * 2. end sync.
+         * 3. sync (this)
+         * 4. end sync.
+         *
+         * Although this is not atomic, it guarantees visiblity, which is 
+         * better than no locking at all.
+         */
+        
+        
+	ActivationGroupDesc desc;
+	ActivationGroupID groupID;
+	long incarnation = 0;
+	Map<UID,ObjectEntry> objects = new HashMap<UID,ObjectEntry>(11);
+	HashSet<UID> restartSet = new HashSet<UID>();
+	
+	transient ActivationInstantiator group = null;
+	transient int status = NORMAL;
+	transient long waitTime = 0;
+	transient String groupName = null;
+	transient Process child = null;
+	transient boolean removed = false;
+	transient Watchdog watchdog = null;
+	
+	GroupEntry(ActivationGroupID groupID, ActivationGroupDesc desc) {
+	    this.groupID = groupID;
+	    this.desc = desc;
+	}
+
+        
+	void restartServices() {
+	    Iterator<UID> iter = null;
+	    
+	    synchronized (this) {
+		if (restartSet.isEmpty()) {
+		    return;
+		}
+
+		/*
+		 * Clone the restartSet so the set does not have to be locked
+		 * during iteration. Locking the restartSet could cause
+		 * deadlock if an object we are restarting caused another
+		 * object in this group to be activated.
+		 */
+                
+		iter = new HashSet<UID>(restartSet).iterator();
+	    }
+	    
+	    while (iter.hasNext()) {
+		UID uid = iter.next();
+		try {
+		    activate(uid, true);
+		} catch (Exception e) {
+		    if (shuttingDown) {
+			return;
+		    }
+		    logger.log(Level.WARNING, "unable to restart service", e);
+		}
+	    }
+	}
+	
+	synchronized void activeGroup(ActivationInstantiator inst,
+				      long instIncarnation)
+	    throws ActivationException
+	{
+	    if (group != null && group.equals(inst) &&
+		incarnation == instIncarnation)
+	    {
+		return;
+	    } else if (child != null && status != CREATING) {
+		throw new ActivationException("group not being created");
+	    } else if (incarnation != instIncarnation) {
+		throw new ActivationException("invalid incarnation");
+	    } else if (group != null) {
+		throw new ActivationException("group already active");
+	    }
+	    
+	    group = inst;
+	    status = NORMAL;
+	    notifyAll();
+	}
+
+	private void checkRemoved() throws UnknownGroupException {
+	    if (removed) {
+		throw new UnknownGroupException("group removed");
+	    }
+	}
+
+	private ObjectEntry getObjectEntry(UID uid)
+	    throws UnknownObjectException
+	{
+	    if (removed) {
+		throw new UnknownObjectException("object's group removed");
+	    }
+	    ObjectEntry objEntry = objects.get(uid);
+	    if (objEntry == null) {
+		throw new UnknownObjectException("object unknown");
+	    }
+	    return objEntry;
+	}
+
+	synchronized void registerObject(UID uid, 
+					 ActivationDesc desc,
+					 boolean addRecord)
+    	    throws ActivationException
+	{
+	    checkRemoved();
+	    synchronized (logLock) {
+		if (addRecord) {
+		    addLogRecord(new LogRegisterObject(uid, desc));
+		}
+		objects.put(uid, new ObjectEntry(desc));
+		if (desc.getRestartMode()) {
+		    restartSet.add(uid);
+		}	    
+		synchronized (idTable) {
+		    idTable.put(uid, groupID);
+		}
+	    }
+	}
+
+	synchronized void unregisterObject(UID uid, boolean addRecord)
+    	    throws ActivationException
+	{
+	    ObjectEntry objEntry = getObjectEntry(uid);
+	    synchronized (logLock) {
+		if (addRecord) {
+		    addLogRecord(new LogUnregisterObject(uid));
+		}
+		objEntry.removed();
+		objects.remove(uid);
+		if (objEntry.desc.getRestartMode()) {
+		    restartSet.remove(uid);
+		}
+		synchronized (idTable) {
+		    idTable.remove(uid);
+		}
+	    }
+	}
+	
+	synchronized Map<ActivationID,ActivationDesc> getActivatableObjects() {
+	    Map<ActivationID,ActivationDesc> map = new HashMap<ActivationID,ActivationDesc>(objects.size());
+	    for (Iterator<Map.Entry<UID,ObjectEntry>> iter = objects.entrySet().iterator();
+		 iter.hasNext(); )
+	    {
+		Map.Entry<UID,ObjectEntry> ent = iter.next();
+		map.put(getAID(ent.getKey()),ent.getValue().desc);
+	    }
+	    return map;
+	}
+
+	synchronized void unregisterGroup(boolean addRecord)
+    	   throws ActivationException
+	{
+	    checkRemoved();
+	    
+	    synchronized (logLock) {
+		if (addRecord) {
+		    addLogRecord(new LogUnregisterGroup(groupID));
+		}
+		removed = true;
+		for (Iterator<Map.Entry<UID,ObjectEntry>> iter = objects.entrySet().iterator();
+		     iter.hasNext(); )
+		{
+		    Map.Entry<UID,ObjectEntry> ent = iter.next();
+		    UID uid = ent.getKey();
+		    synchronized (idTable) {
+			idTable.remove(uid);
+		    }
+		    ObjectEntry objEntry = ent.getValue();
+		    objEntry.removed();
+		}
+		objects.clear();
+		restartSet.clear();
+		reset();
+		childGone();
+	    }
+	}
+
+	synchronized ActivationDesc setActivationDesc(UID uid,
+						      ActivationDesc desc,
+						      boolean addRecord)
+	    throws ActivationException
+	{
+	    ObjectEntry objEntry = getObjectEntry(uid);
+	    synchronized (logLock) {
+		if (addRecord) {
+		    addLogRecord(new LogUpdateDesc(uid, desc));
+		}
+		ActivationDesc oldDesc = objEntry.desc;
+		objEntry.desc = desc;
+		if (desc.getRestartMode()) {
+		    restartSet.add(uid);
+		} else {
+		    restartSet.remove(uid);
+		}
+		return oldDesc;
+	    }
+	}
+
+	synchronized ActivationDesc getActivationDesc(UID uid)
+	    throws UnknownObjectException
+	{
+	    return getObjectEntry(uid).desc;
+	}
+
+	synchronized ActivationGroupDesc setActivationGroupDesc(
+		ActivationGroupID id,
+		ActivationGroupDesc desc,
+		boolean addRecord)
+    	    throws ActivationException
+	{
+	    checkRemoved();
+	    synchronized (logLock) {
+		if (addRecord) {
+		    addLogRecord(new LogUpdateGroupDesc(id, desc));
+		}
+		ActivationGroupDesc oldDesc = this.desc;
+		this.desc = desc;
+		return oldDesc;
+	    }
+	}
+
+	synchronized void inactiveGroup(long incarnation, boolean failure)
+	    throws UnknownGroupException
+	{
+	    checkRemoved();
+	    if (this.incarnation != incarnation) {
+		throw new UnknownGroupException("invalid incarnation");
+	    }
+	    reset();
+	    if (failure) {
+		terminate();
+	    } else if (child != null && status == NORMAL) {
+		status = TERMINATE;
+		watchdog.noRestart();
+	    }
+	}
+
+	synchronized void activeObject(UID uid, MarshalledObject mobj)
+	    throws UnknownObjectException
+	{
+	    getObjectEntry(uid).stub =
+		new MarshalledWrapper(new MarshalledInstance(mobj));
+	}
+
+	synchronized void inactiveObject(UID uid)
+    	    throws UnknownObjectException
+	{
+	    getObjectEntry(uid).reset();
+	}
+
+	private void reset() {
+	    group = null;
+	    for (Iterator<ObjectEntry> iter = objects.values().iterator(); iter.hasNext(); )
+	    {
+		iter.next().reset();
+	    }
+	}
+	
+	private void childGone() {
+	    if (child != null) {
+		child = null;
+		watchdog.dispose();
+		watchdog = null;
+		status = NORMAL;
+		notifyAll();
+	    }
+	}
+
+	private void terminate() {
+	    if (child != null && status != TERMINATING) {
+		child.destroy();
+		status = TERMINATING;
+		waitTime = System.currentTimeMillis() + groupTimeout;
+		notifyAll();
+	    }
+	}
+
+	private void await() {
+	    while (true) {
+		switch (status) {
+		case NORMAL:
+		    return;
+		case TERMINATE:
+		    terminate();
+		case TERMINATING:
+		    try {
+			child.exitValue();
+		    } catch (IllegalThreadStateException e) {
+			long now = System.currentTimeMillis();
+			if (waitTime > now) {
+			    try {
+				wait(waitTime - now);
+			    } catch (InterruptedException ee) {
+			    }
+			    continue;
+			}
+			logger.log(Level.WARNING,
+				   "group did not terminate: {0}", groupName);
+		    }
+		    childGone();
+		    return;
+		case CREATING:
+		    try {
+			wait();
+		    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+		    }
+		}
+	    }
+	}
+
+	// no synchronization to avoid delay wrt getInstantiator
+	void shutdownFast() {
+	    Process p = child;
+	    if (p != null) {
+		p.destroy();
+	    }
+	}
+
+	synchronized void shutdown() {
+	    reset();
+	    terminate();
+	    await();
+	}
+
+	MarshalledWrapper activate(UID uid, boolean force)

[... 807 lines stripped ...]