You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by ni...@apache.org on 2008/12/10 06:12:29 UTC
svn commit: r724976 [2/6] - in
/incubator/river/jtsk/skunk/niclas1/services/phoenix: ./ src/ src/main/
src/main/java/ src/main/java/com/ src/main/java/com/sun/
src/main/java/com/sun/jini/ src/main/java/com/sun/jini/phoenix/
src/main/java/com/sun/jini/p...
Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activation.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activation.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activation.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activation.java Tue Dec 9 21:12:27 2008
@@ -0,0 +1,2261 @@
+/*
+ * 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.net.URL;
+import java.rmi.ConnectException;
+import java.rmi.ConnectIOException;
+import java.rmi.MarshalledObject;
+import java.rmi.NoSuchObjectException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationDesc;
+import java.rmi.activation.ActivationException;
+import java.rmi.activation.ActivationGroupDesc;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.activation.ActivationID;
+import java.rmi.activation.ActivationInstantiator;
+import java.rmi.activation.ActivationMonitor;
+import java.rmi.activation.ActivationSystem;
+import java.rmi.activation.UnknownGroupException;
+import java.rmi.activation.UnknownObjectException;
+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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+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.export.proxytrust.ServerProxyTrust;
+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;
+
+/**
+ * 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 = null;
+ private static Logger logger = Logger.getLogger( PHOENIX );
+ private static final int PHOENIX_PORT = 1198;
+ private static final Object logLock = new Object();
+
+ /**
+ * maps activation id uid to its respective group id
+ */
+ private Map idTable = new HashMap();
+ /**
+ * maps group id to its GroupEntry groups
+ */
+ private Map groupTable = new HashMap();
+
+ /**
+ * 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 MarshalledObject( new ActivationGroupData( (String[])
+ config.getEntry( PHOENIX, "groupConfig",
+ String[].class, configOptions ) ) );
+ 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 );
+ }
+ 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,
+ new Integer( 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
+ {
+ return getGroupEntry( id ).desc;
+ }
+
+ public void shutdown()
+ {
+ synchronized( Activation.this )
+ {
+ if( !shuttingDown )
+ {
+ shuttingDown = true;
+ new Shutdown().start();
+ }
+ }
+ }
+
+ public Map getActivationGroups()
+ {
+ synchronized( groupTable )
+ {
+ Map map = new HashMap( groupTable.size() );
+ for( Iterator iter = groupTable.values().iterator();
+ iter.hasNext(); )
+ {
+ GroupEntry entry = (GroupEntry) iter.next();
+ if( !entry.removed )
+ {
+ map.put( entry.groupID, entry.desc );
+ }
+ }
+ return map;
+ }
+ }
+
+ public Map getActivatableObjects( ActivationGroupID id )
+ throws UnknownGroupException
+ {
+ synchronized( activatorExporter )
+ {
+ // to wait for it to be exported
+ }
+ 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 = (GroupEntry[]) 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 iter = groupTable.values().iterator();
+ iter.hasNext(); )
+ {
+ ( (GroupEntry) 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 = (ActivationGroupID) 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 )
+ {
+ synchronized( groupTable )
+ {
+ GroupEntry entry = (GroupEntry) groupTable.get( id );
+ if( entry != null && !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 = (GroupEntry) 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;
+
+ ActivationGroupDesc desc;
+ ActivationGroupID groupID;
+ long incarnation = 0;
+ Map objects = new HashMap( 11 );
+ HashSet restartSet = new HashSet();
+
+ 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 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 = ( (Set) restartSet.clone() ).iterator();
+ }
+
+ while( iter.hasNext() )
+ {
+ UID 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 = (ObjectEntry) 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 getActivatableObjects()
+ {
+ Map map = new HashMap( objects.size() );
+ for( Iterator iter = objects.entrySet().iterator();
+ iter.hasNext(); )
+ {
+ Map.Entry ent = (Map.Entry) iter.next();
+ map.put( getAID( (UID) ent.getKey() ),
+ ( (ObjectEntry) 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 iter = objects.entrySet().iterator();
+ iter.hasNext(); )
+ {
+ Map.Entry ent = (Map.Entry) iter.next();
+ UID uid = (UID) ent.getKey();
+ synchronized( idTable )
+ {
+ idTable.remove( uid );
+ }
+ ObjectEntry objEntry = (ObjectEntry) 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 iter = objects.values().iterator(); iter.hasNext(); )
+ {
+ ( (ObjectEntry) 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 argv = new ArrayList();
+
+ // 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 );
+ 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 action = new PrivilegedExceptionAction()
+ {
+ public Object 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 (Activation)
+ Subject.doAsPrivileged( login.getSubject(), action, null );
+ }
+ else
+ {
+ return (Activation) 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 )
+ {
+ try
+ {
+ resources = ResourceBundle.getBundle(
+ "com.sun.jini.phoenix.resources.phoenix" );
+ }
+ catch( MissingResourceException mre )
+ {
+ 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();
+ }
+}
Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationAdmin.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationAdmin.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationAdmin.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationAdmin.java Tue Dec 9 21:12:27 2008
@@ -0,0 +1,69 @@
+/*
+ * 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 java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.activation.UnknownGroupException;
+import java.util.Map;
+
+/**
+ * An administrative interface for the activation system daemon. This
+ * interface is implemented directly by the same proxy that implements
+ * {@link java.rmi.activation.ActivationSystem}.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public interface ActivationAdmin extends Remote
+{
+ /**
+ * Returns a map from {@link ActivationGroupID} to
+ * {@link java.rmi.activation.ActivationGroupDesc} for all registered
+ * activation groups. The map contains a snapshot of the state at
+ * the time of the call; subsequent state changes are not reflected
+ * in the map, nor do changes in the map cause changes in the state
+ * of the activation system daemon.
+ *
+ * @return a map from <code>ActivationGroupID</code> to
+ * <code>ActivationGroupDesc</code> for all registered activation groups
+ * @throws RemoteException if a communication-related exception occurs
+ */
+ Map getActivationGroups() throws RemoteException;
+
+ /**
+ * Returns a map from {@link java.rmi.activation.ActivationID} to
+ * {@link java.rmi.activation.ActivationDesc} for all activatable objects
+ * registered in the group registered under the specified activation
+ * group identifier. The map contains a snapshot of the state at
+ * the time of the call; subsequent state changes are not reflected
+ * in the map, nor do changes in the map cause changes in the state
+ * of the activation system daemon.
+ *
+ * @param id activation group identifier
+ * @return a map from <code>ActivationID</code> to
+ * <code>ActivationDesc</code> for all activatable objects registered in
+ * the group registered under the specified activation group identifier
+ * @throws UnknownGroupException if no group is registered under the
+ * specified activation group identifier
+ * @throws RemoteException if a communication-related exception occurs
+ */
+ Map getActivatableObjects( ActivationGroupID id )
+ throws UnknownGroupException, RemoteException;
+}
Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupData.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupData.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupData.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupData.java Tue Dec 9 21:12:27 2008
@@ -0,0 +1,62 @@
+/*
+ * 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 java.io.Serializable;
+
+/**
+ * Initialization data for {@link ActivationGroupImpl}, to control the
+ * activation group's configuration. An instance of this is specified
+ * in the <code>ActivationGroupDesc</code> for the group.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public class ActivationGroupData implements Serializable
+{
+ private static final long serialVersionUID = 1135616886784731068L;
+
+ /**
+ * The configuration options.
+ *
+ * @serial
+ */
+ private final String[] config;
+
+ /**
+ * Constructs an instance with the specified
+ * {@link net.jini.config.Configuration} options, if any.
+ *
+ * @param config the configuration options, or <code>null</code>
+ */
+ public ActivationGroupData( String[] config )
+ {
+ this.config = ( config == null ? null : (String[]) config.clone() );
+ }
+
+ /**
+ * Returns the configuration options, or <code>null</code>.
+ *
+ * @return the configuration options, or <code>null</code>
+ */
+ public String[] getConfig()
+ {
+ return config == null ? null : (String[]) config.clone();
+ }
+}