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 [3/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/ActivationGroupImpl.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupImpl.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupImpl.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupImpl.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,1363 @@
+/*
+ * 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.thread.Executor;
+import com.sun.jini.thread.GetThreadPoolAction;
+import java.io.IOException;
+import java.io.ObjectStreamClass;
+import java.io.ObjectStreamField;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.rmi.MarshalledObject;
+import java.rmi.NoSuchObjectException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.Activatable;
+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.UnknownObjectException;
+import java.rmi.server.ExportException;
+import java.rmi.server.RMIClassLoader;
+import java.rmi.server.RemoteObject;
+import java.rmi.server.UnicastRemoteObject;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import net.jini.activation.ActivationGroup;
+import net.jini.config.Configuration;
+import net.jini.config.ConfigurationException;
+import net.jini.config.ConfigurationProvider;
+import net.jini.export.Exporter;
+import net.jini.export.ProxyAccessor;
+import net.jini.export.proxytrust.ServerProxyTrust;
+import net.jini.jeri.BasicJeriExporter;
+import net.jini.jeri.tcp.TcpServerEndpoint;
+import net.jini.security.BasicProxyPreparer;
+import net.jini.security.ProxyPreparer;
+import net.jini.security.Security;
+import net.jini.security.TrustVerifier;
+
+/**
+ * The default activation group implementation for phoenix.  Instances of
+ * this class are configurable through a {@link Configuration}, as detailed
+ * further below, and provide the necessary support to allow exporter-based
+ * remote objects to go inactive.  Instances of this class support the
+ * creation of remote objects through the normal activatable constructor;
+ * an activatable remote object must either implement the {@link
+ * ProxyAccessor} interface to return a suitable proxy for the remote
+ * object, or the remote object must itself be serializable and marshalling
+ * the object must produce a suitable proxy for the remote object.
+ *
+ * <p>An instance of this class can be configured by specifying an
+ * {@link ActivationGroupData} instance containing configuration options
+ * as the initialization data for the activation group. Typically
+ * this is accomplished indirectly, by setting the
+ * <code>groupConfig</code> configuration entry for
+ * phoenix itself. The following entries are obtained from the configuration,
+ * all for the component named <code>com.sun.jini.phoenix</code>:
+ *
+ * <table summary="Describes the loginContext configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * loginContext</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>{@link javax.security.auth.login.LoginContext}</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>null</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> JAAS login context
+ * </table>
+ *
+ * <table summary="Describes the inheritGroupSubject configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * inheritGroupSubject</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>boolean</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>false</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> if <code>true</code>, group subject is inherited
+ * when an activatable object is created
+ * </table>
+ *
+ * <table summary="Describes the instantiatorExporter configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * instantiatorExporter</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.export.Exporter}</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> retains existing JRMP export of instantiator
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> {@link java.rmi.activation.ActivationInstantiator}
+ * exporter
+ * </table>
+ *
+ * <table summary="Describes the monitorPreparer configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * monitorPreparer</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>new {@link
+ * net.jini.security.BasicProxyPreparer}()</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> {@link java.rmi.activation.ActivationMonitor}
+ * proxy preparer
+ * </table>
+ *
+ * <table summary="Describes the systemPreparer configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * systemPreparer</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>new {@link
+ * net.jini.security.BasicProxyPreparer}()</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> {@link java.rmi.activation.ActivationSystem}
+ * proxy preparer
+ * </table>
+ *
+ * <table summary="Describes the unexportTimeout configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * unexportTimeout</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>int</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>60000</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> maximum time in milliseconds to wait for
+ * in-progress calls to finish before forcibly unexporting the
+ * group when going inactive
+ * </table>
+ *
+ * <table summary="Describes the unexportWait configuration entry"
+ * border="0" cellpadding="2">
+ * <tr valign="top">
+ * <th scope="col" summary="layout"> <font size="+1">&#X2022;</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * unexportWait</code></font>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Type: <td> <code>int</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Default: <td> <code>10</code>
+ * <tr valign="top"> <td> &nbsp <th scope="row" align="right">
+ * Description: <td> milliseconds to wait between unexport attempts
+ * when going inactive
+ * </table>
+ *
+ * <p>This class depends on its {@link #createGroup createGroup} method being
+ * called to initialize the activation group. As such, this class cannot be
+ * used in conjunction with the standard <code>rmid</code>.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public class ActivationGroupImpl extends ActivationGroup
+    implements ServerProxyTrust
+{
+    private static final long serialVersionUID = 5758693559430427303L;
+
+    private static final String PHOENIX = "com.sun.jini.phoenix";
+    /**
+     * instance has not been created
+     */
+    private static final int UNUSED = 0;
+    /**
+     * in the middle of createGroup
+     */
+    private static final int CREATING = 1;
+    /**
+     * constructor (including activeGroup) has succeeded
+     */
+    private static final int CREATED = 2;
+    /**
+     * createGroup has succeeded
+     */
+    private static final int ACTIVE = 3;
+    /**
+     * group is inactive
+     */
+    private static final int INACTIVE = 4;
+    /**
+     * parameter types for activatable constructor
+     */
+    private static Class[] paramTypes = {
+        ActivationID.class, MarshalledObject.class
+    };
+
+    /* avoid serial field clutter */
+    private static final ObjectStreamField[] serialPersistentFields =
+        ObjectStreamClass.NO_FIELDS;
+
+    private static int state = UNUSED;
+    private static long incarnation;
+    /**
+     * original unprepared group id
+     */
+    private static ActivationGroupID groupID;
+
+    /**
+     * server LoginContext or null
+     */
+    private static LoginContext login;
+    private static Exporter exporter;
+    /**
+     * true if calls should be refused, false otherwise
+     */
+    private static boolean refuseCalls = false;
+    /**
+     * monitor proxy preparer
+     */
+    private static ProxyPreparer monPreparer;
+    private static ActivationMonitor monitor;
+    /**
+     * timeout on wait for unexport to succeed
+     */
+    private static long unexportTimeout;
+    /**
+     * timeout on wait between unexport attempts
+     */
+    private static long unexportWait;
+    /**
+     * maps ActivationID to ActiveEntry
+     */
+    private static Map active = new HashMap();
+    /**
+     * ActivationIDs with operations in progress
+     */
+    private static List lockedIDs = new ArrayList();
+    /**
+     * true if group subject is inherited when creating activatable objects
+     */
+    private static boolean inheritGroupSubject;
+
+    /**
+     * permission to check for monitor's activeObject call
+     */
+    private final static Permission activeObjectPermission =
+        new MonitorPermission(
+            "java.rmi.activation.ActivationMonitor.activeObject" );
+
+    /**
+     * permission to check for monitor's activeObject call
+     */
+    private static Permission inactiveObjectPermission =
+        new MonitorPermission(
+            "java.rmi.activation.ActivationMonitor.inactiveObject" );
+
+    /**
+     * proxy for this activation group
+     */
+    private ActivationInstantiator proxy;
+
+    /**
+     * Creates an {@link java.rmi.activation.ActivationGroup} instance and
+     * returns it. An {@link ActivationGroupData} instance is extracted from
+     * the initialization data, and a {@link Configuration} is obtained by
+     * calling
+     * {@link net.jini.config.ConfigurationProvider#getInstance
+     * Configuration.Provider.getInstance} with the configuration options from
+     * that instance. A {@link LoginContext} is obtained from the
+     * <code>loginContext</code> configuration entry, if one exists; if the
+     * value is not <code>null</code>, a login is performed on that context,
+     * and the resulting {@link Subject} (set to be read-only) is used as the
+     * subject when executing the rest of this method. The subject is also
+     * used for all subsequent remote calls by this class to the
+     * {@link ActivationMonitor}. The {@link ActivationSystem} proxy
+     * (obtained from the <code>ActivationGroupID</code>) is passed to the
+     * {@link ProxyPreparer} given by the <code>systemPreparer</code>
+     * configuration entry, if one exists; a new
+     * <code>ActivationGroupID</code> is constructed with the resulting proxy.
+     * An {@link Exporter} instance is obtained from the
+     * <code>instantiatorExporter</code> configuration entry, if one exists;
+     * this exporter will be used (in the constructor of this class) to export
+     * the group. A <code>ProxyPreparer</code> instance is obtained from the
+     * <code>monitorPreparer</code> configuration entry, if one exists; this
+     * preparer will be used (in the constructor of this class) to prepare the
+     * <code>ActivationMonitor</code>. A call is then made to
+     * {@link ActivationGroup#createGroup ActivationGroup.createGroup} with
+     * the new group identifier, the activation group descriptor, and the
+     * group incarnation number, and the result of that call is returned.
+     *
+     * @param id          the activation group identifier
+     * @param desc        the activation group descriptor
+     * @param incarnation the group's incarnation number (zero on initial
+     *                    creation)
+     * @return the created activation group
+     * @throws ActivationException if a group already exists or if an
+     *                             exception occurs during group creation
+     */
+    public static synchronized java.rmi.activation.ActivationGroup createGroup(
+        final ActivationGroupID id,
+        final ActivationGroupDesc desc,
+        final long incarnation )
+        throws ActivationException
+    {
+        if( state != UNUSED )
+        {
+            throw new ActivationException( "group previously created" );
+        }
+        try
+        {
+            final Configuration config = getConfiguration( desc.getData() );
+            login = (LoginContext) config.getEntry(
+                PHOENIX, "loginContext", LoginContext.class, null );
+            if( login != null )
+            {
+                login.login();
+            }
+
+            inheritGroupSubject =
+                ( (Boolean) config.getEntry(
+                    PHOENIX, "inheritGroupSubject", boolean.class,
+                    Boolean.FALSE ) ).booleanValue();
+
+            return (java.rmi.activation.ActivationGroup) doAction(
+                new PrivilegedExceptionAction()
+                {
+                    public Object run() throws Exception
+                    {
+                        ProxyPreparer sysPreparer =
+                            getPreparer( config, "systemPreparer" );
+                        monPreparer = getPreparer( config,
+                                                   "monitorPreparer" );
+                        TcpServerEndpoint se =
+                            TcpServerEndpoint.getInstance( 0 );
+                        Exporter defaultExporter =
+                            new BasicJeriExporter( se, new AccessILFactory() );
+                        exporter = (Exporter) config.getEntry(
+                            PHOENIX, "instantiatorExporter",
+                            Exporter.class, defaultExporter );
+                        if( exporter == null )
+                        {
+                            exporter = new AlreadyExportedExporter();
+                        }
+                        refuseCalls =
+                            !( exporter instanceof AlreadyExportedExporter );
+                        unexportTimeout = getInt( config, "unexportTimeout",
+                                                  60000 );
+                        unexportWait = getInt( config, "unexportWait", 10 );
+                        ActivationSystem sys = (ActivationSystem)
+                            sysPreparer.prepareProxy( id.getSystem() );
+                        ActivationGroupImpl.incarnation = incarnation;
+                        groupID = id;
+                        state = CREATING;
+                        ActivationGroupID gid = ( sys == id.getSystem() ?
+                                                  id : new WrappedGID( id, sys ) );
+                        Object group = ActivationGroup.createGroup(
+                            gid, desc, incarnation );
+                        state = ACTIVE;
+                        return group;
+                    }
+                } );
+        }
+        catch( ActivationException e )
+        {
+            throw e;
+        }
+        catch( Exception e )
+        {
+            throw new ActivationException( "creation failed", e );
+        }
+        finally
+        {
+            if( state != ACTIVE )
+            {
+                checkInactiveGroup();
+            }
+        }
+    }
+
+    /**
+     * Returns the configuration obtained from the specified marshalled
+     * object.
+     */
+    private static Configuration getConfiguration( MarshalledObject mobj )
+        throws ConfigurationException, IOException, ClassNotFoundException
+    {
+        ActivationGroupData data = (ActivationGroupData) mobj.get();
+        ClassLoader cl = ActivationGroupImpl.class.getClassLoader();
+        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+        if( !covers( cl, ccl ) )
+        {
+            cl = ccl;
+        }
+        return ConfigurationProvider.getInstance( data.getConfig(), cl );
+    }
+
+    /**
+     * Returns true if the first argument is either equal to, or is a
+     * descendant of, the second argument.  Null is treated as the root of
+     * the tree.
+     */
+    private static boolean covers( ClassLoader sub, ClassLoader sup )
+    {
+        if( sup == null )
+        {
+            return true;
+        }
+        else if( sub == null )
+        {
+            return false;
+        }
+        do
+        {
+            if( sub == sup )
+            {
+                return true;
+            }
+            sub = sub.getParent();
+        }
+        while( sub != null );
+        return false;
+    }
+
+    /**
+     * 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() );
+    }
+
+    /**
+     * 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();
+    }
+
+    /**
+     * ActivationGroupID containing a prepared ActivationSystem proxy and
+     * the original ActivationGroupID (with unprepared ActivationSystem
+     * proxy), that writeReplaces itself to the original.
+     */
+    private static class WrappedGID extends ActivationGroupID
+    {
+        /**
+         * Original gid
+         */
+        private final ActivationGroupID id;
+        /**
+         * Prepared system proxy
+         */
+        private final ActivationSystem sys;
+
+        WrappedGID( ActivationGroupID id, ActivationSystem sys )
+        {
+            super( sys );
+            this.id = id;
+            this.sys = sys;
+        }
+
+        /* override */
+        public ActivationSystem getSystem()
+        {
+            return sys;
+        }
+
+        private Object writeReplace()
+        {
+            return id;
+        }
+    }
+
+    /**
+     * Creates an instance with the specified group identifier and
+     * initialization data. This constructor must be called indirectly,
+     * via {@link #createGroup createGroup}. By default, this instance
+     * automatically exports itself as a {@link UnicastRemoteObject}. (This
+     * is a limitation of the existing activation system design.) If an
+     * {@link Exporter} was obtained by {@link #createGroup createGroup},
+     * then this instance is unexported from the JRMP runtime and re-exported
+     * using that exporter. (Any incoming remote calls received on the
+     * original JRMP export before this instance can be unexported will be
+     * refused with a security exception thrown.) The
+     * {@link ActivationSystem#activeGroup activeGroup} method of the
+     * activation system proxy (in the group identifier) is called to
+     * make the group active. The returned {@link ActivationMonitor} proxy
+     * is passed to the corresponding {@link ProxyPreparer} obtained by
+     * <code>createGroup</code>. Note that after this constructor returns,
+     * {@link ActivationGroup#createGroup ActivationGroup.createGroup} will
+     * also call <code>activeGroup</code> (so the activation system must
+     * accept idempotent calls to that method), but the
+     * <code>ActivationMonitor</code> proxy returned by that call will not be
+     * used.
+     *
+     * @param id   the activation group identifier
+     * @param data group initialization data (ignored)
+     * @throws RemoteException     if the group could not be exported or
+     *                             made active, or proxy preparation fails
+     * @throws ActivationException if the constructor was not called
+     *                             indirectly from <code>createGroup</code>
+     */
+    public ActivationGroupImpl( ActivationGroupID id, MarshalledObject data )
+        throws ActivationException, RemoteException
+    {
+        super( id );
+        synchronized( ActivationGroupImpl.class )
+        {
+            if( state != CREATING )
+            {
+                throw new ActivationException( "not called from createGroup" );
+            }
+        }
+        if( refuseCalls )
+        {
+            unexportObject( this, true );
+            refuseCalls = false;
+        }
+        proxy = (ActivationInstantiator) exporter.export( this );
+        try
+        {
+            monitor = (ActivationMonitor) monPreparer.prepareProxy(
+                id.getSystem().activeGroup( id, proxy, incarnation ) );
+            state = CREATED;
+        }
+        finally
+        {
+            if( state != CREATED )
+            {
+                exporter.unexport( true );
+            }
+        }
+        monPreparer = null;
+    }
+
+    public TrustVerifier getProxyVerifier()
+    {
+        return new BasicProxyTrustVerifier( proxy );
+    }
+
+    /**
+     * Exporter for an object that is already exported to JRMP.
+     */
+    private static class AlreadyExportedExporter implements Exporter
+    {
+        /**
+         * A strong reference to the impl is OK because ActivationGroup
+         * also holds a strong reference.
+         */
+        private Remote impl;
+
+        AlreadyExportedExporter()
+        {
+        }
+
+        public synchronized Remote export( Remote impl ) throws ExportException
+        {
+            this.impl = impl;
+            try
+            {
+                return RemoteObject.toStub( impl );
+            }
+            catch( NoSuchObjectException e )
+            {
+                throw new ExportException( "no stub found", e );
+            }
+        }
+
+        public synchronized boolean unexport( boolean force )
+        {
+            try
+            {
+                if( impl != null &&
+                    !UnicastRemoteObject.unexportObject( impl, force ) )
+                {
+                    return false;
+                }
+            }
+            catch( NoSuchObjectException e )
+            {
+            }
+            impl = null;
+            return true;
+        }
+    }
+
+    /**
+     * Returns the proxy for this remote object. Group creation was designed
+     * to rely on automatic stub replacement (as provided by the JRMP runtime),
+     * which is not supported by all exporters.
+     *
+     * @return the proxy for this remote object
+     */
+    protected Object writeReplace()
+    {
+        return proxy;
+    }
+
+    /*
+     * Obtains a lock on the ActivationID id before returning. Allows only one
+     * thread at a time to hold a lock on a particular id.  If the lock for id
+     * is in use, all requests for an equivalent (in the .equals sense)
+     * id will wait for the id to be notified and use the supplied id as the
+     * next lock. The caller of "acquireLock" must execute the "releaseLock"
+     * method" to release the lock and "notifyAll" waiters for the id lock
+     * obtained from this method.  The typical usage pattern is as follows:
+     *
+     * acquireLock(id);
+     * try {
+     *    // do stuff pertaining to id...
+     * } finally {
+     *    releaseLock(id);
+     * }
+     */
+    private void acquireLock( ActivationID id )
+    {
+        while( true )
+        {
+            synchronized( id )
+            {
+                synchronized( lockedIDs )
+                {
+                    int index = lockedIDs.indexOf( id );
+                    if( index < 0 )
+                    {
+                        lockedIDs.add( id );
+                        return;
+                    }
+                    ActivationID lockedID =
+                        (ActivationID) lockedIDs.get( index );
+                    if( lockedID != id )
+                    {
+                        // don't wait on an id that won't be notified
+                        id = lockedID;
+                        continue;
+                    }
+                }
+                try
+                {
+                    id.wait();
+                }
+                catch( InterruptedException ignore )
+                {
+                }
+            }
+        }
+    }
+
+    /*
+     * Releases the id lock obtained via the "acquireLock" method and then
+     * notifies all threads waiting on the lock.
+     */
+    private void releaseLock( ActivationID id )
+    {
+        synchronized( lockedIDs )
+        {
+            id = (ActivationID) lockedIDs.remove( lockedIDs.indexOf( id ) );
+        }
+        synchronized( id )
+        {
+            id.notifyAll();
+        }
+    }
+
+    /**
+     * Creates a new instance of an activatable remote object and returns
+     * a marshalled object containing the activated object's proxy.
+     *
+     * <p>If an active object already exists for the specified identifier,
+     * the existing marshalled object for it is returned.
+     *
+     * <p>Otherwise:
+     *
+     * <p>The class for the object is loaded by invoking {@link
+     * RMIClassLoader#loadClass(String,String) RMIClassLoader.loadClass}
+     * passing the class location (obtained by invoking {@link
+     * ActivationDesc#getLocation getLocation} on the activation
+     * descriptor) and the class name (obtained by invoking {@link
+     * ActivationDesc#getClassName getClassName} on the activation
+     * descriptor).
+     *
+     * <p>The new instance is constructed as follows. If the class defines
+     * a constructor with two parameters of type {@link ActivationID} and
+     * {@link MarshalledObject}, that constructor is called with the
+     * specified activation identifier and the initialization data from the
+     * specified activation descriptor. Otherwise, an
+     * <code>ActivationException</code> is thrown.
+     *
+     * <p>If the class loader of the object's class is a descendant of the
+     * current context class loader, then that class loader is set as the
+     * context class loader when the constructor is called.
+     *
+     * <p>If the <code>inheritGroupSubject</code> configuration entry is
+     * <code>true</code> then the constructor is invoked in an action
+     * passed to the {@link Security#doPrivileged Security.doPrivileged}
+     * method; otherwise the constructor is invoked in an action passed to
+     * the {@link AccessController#doPrivileged
+     * AccessController.doPrivileged} method.
+     *
+     * <p>A proxy for the newly created instance is returned as follows:
+     *
+     * <ul><li>If the newly created instance implements {@link
+     * ProxyAccessor}, a proxy is obtained by invoking the {@link
+     * ProxyAccessor#getProxy getProxy} method on that instance. If the
+     * obtained proxy is not <code>null</code>, that proxy is returned in a
+     * <code>MarshalledObject</code>; otherwise, an
+     * <code>ActivationException</code> is thrown.
+     *
+     * <li>If the newly created instance does not implement
+     * <code>ProxyAccessor</code>, the instance is returned in a
+     * <code>MarshalledObject</code>.  In this case, the instance must be
+     * serializable, and marshalling the instance must produce a suitable
+     * proxy for the remote object (for example, the object implements
+     * {@link java.io.Serializable} and defines a <code>writeReplace</code>
+     * method that returns the object's proxy).
+     * </ul>
+     *
+     * <p>If both the remote object and the activation group are exported
+     * using JRMP, then automatic stub replacement will produce the desired
+     * result, but otherwise the remote object implementation must provide
+     * a means for this group to obtain its proxy as indicated above.
+     *
+     * @throws ActivationException if the object's class could not be
+     *                             loaded, if the loaded class does not define the appropriate
+     *                             constructor, or any exception occurs activating the object
+     */
+    public MarshalledObject newInstance( final ActivationID id,
+                                         final ActivationDesc desc )
+        throws ActivationException
+    {
+        synchronized( ActivationGroupImpl.class )
+        {
+            if( refuseCalls )
+            {
+                throw new SecurityException( "call refused" );
+            }
+        }
+
+        acquireLock( id );
+        try
+        {
+            ActiveEntry entry;
+            synchronized( ActivationGroupImpl.class )
+            {
+                if( state != ACTIVE )
+                {
+                    throw new InactiveGroupException( "group not active" );
+                }
+                entry = (ActiveEntry) active.get( id );
+            }
+            if( entry != null )
+            {
+                return entry.mobj;
+            }
+
+            String className = desc.getClassName();
+            final Class cl = RMIClassLoader.loadClass( desc.getLocation(),
+                                                       className );
+            final Thread t = Thread.currentThread();
+            final ClassLoader savedCcl = t.getContextClassLoader();
+            final ClassLoader ccl =
+                covers( cl.getClassLoader(), savedCcl ) ?
+                cl.getClassLoader() : savedCcl;
+
+            Remote impl = null;
+
+            /*
+            * Fix for 4164971: allow non-public activatable class
+            * and/or constructor, create the activatable object in a
+            * privileged block
+            */
+            try
+            {
+                PrivilegedExceptionAction action =
+                    new PrivilegedExceptionAction()
+                    {
+                        public Object run() throws InstantiationException,
+                                                   NoSuchMethodException, IllegalAccessException,
+                                                   InvocationTargetException, ActivationException
+                        {
+                            Object[] params = new Object[]{ id, desc.getData() };
+                            Constructor constructor =
+                                cl.getDeclaredConstructor( paramTypes );
+                            constructor.setAccessible( true );
+                            try
+                            {
+                                /*
+                           * Fix for 4289544: make sure to set the
+                           * context class loader to be the class
+                           * loader of the impl class before
+                           * constructing that class.
+                           */
+                                t.setContextClassLoader( ccl );
+                                return constructor.newInstance( params );
+                            }
+                            finally
+                            {
+                                t.setContextClassLoader( savedCcl );
+                            }
+                        }
+                    };
+
+                /*
+             * The activatable object is created is in a doPrivileged
+             * block to protect against user code which might have set
+             * a global socket factory (in which case application code
+             * would be on the stack).
+             */
+                impl = (Remote) ( inheritGroupSubject ?
+                                  Security.doPrivileged( action ) :
+                                  AccessController.doPrivileged( action ) );
+
+            }
+            catch( PrivilegedActionException pae )
+            {
+                throw pae.getException();
+            }
+
+            entry = new ActiveEntry( impl );
+            synchronized( ActivationGroupImpl.class )
+            {
+                active.put( id, entry );
+            }
+            return entry.mobj;
+        }
+        catch( NoSuchMethodException e )
+        {
+            /* user forgot to provide activatable constructor? */
+            throw new ActivationException(
+                "activation constructor not defined", e );
+        }
+        catch( NoSuchMethodError e )
+        {
+            /* code recompiled and user forgot to provide
+            *  activatable constructor?
+            */
+            throw new ActivationException(
+                "activation constructor not defined", e );
+        }
+        catch( InvocationTargetException e )
+        {
+            throw new ActivationException( "exception constructing object",
+                                           e.getTargetException() );
+        }
+        catch( ActivationException e )
+        {
+            throw e;
+        }
+        catch( Exception e )
+        {
+            throw new ActivationException( "unable to activate object", e );
+        }
+        finally
+        {
+            releaseLock( id );
+            checkInactiveGroup();
+        }
+    }
+
+    /**
+     * Attempts to make the remote object that is associated with the
+     * specified activation identifier, and that was exported as a JRMP
+     * {@link java.rmi.activation.Activatable} object, inactive. This method
+     * calls <code>Activatable.unexportObject</code> with the active remote
+     * object and <code>false</code>, to unexport the object. If that call
+     * returns <code>false</code>, this method returns <code>false</code>.
+     * If that call returns <code>true</code>, the object is marked inactive
+     * in this virtual machine, the superclass <code>inactiveObject</code>
+     * method is called with the same activation identifier, with the
+     * <code>ActivationMonitor</code> constraints (if any) set as
+     * contextual client constraints, and with the group's subject (if any)
+     * set as the executing subject, and this method returns <code>true</code>.
+     *
+     * @param id the activation identifier
+     * @return <code>true</code> if the object was successfully made
+     *         inactive; <code>false</code> otherwise
+     * @throws UnknownObjectException if the object is not known to be
+     *                                active (it may already be inactive)
+     * @throws ActivationException    if an activation error occurs
+     * @throws InactiveGroupException if the group is inactive
+     * @throws RemoteException        if the remote call to the activation
+     *                                monitor fails
+     * @throws SecurityException      if a security manager exists and invoking
+     *                                its {@link SecurityManager#checkPermission checkPermission} method
+     *                                with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.inactiveObject")</code>
+     *                                throws a <code>SecurityException</code>
+     */
+    public boolean inactiveObject( final ActivationID id )
+        throws ActivationException, RemoteException
+    {
+        SecurityManager security = System.getSecurityManager();
+        if( security != null )
+        {
+            security.checkPermission( inactiveObjectPermission );
+        }
+        acquireLock( id );
+        try
+        {
+            ActiveEntry entry;
+            synchronized( ActivationGroupImpl.class )
+            {
+                if( state != ACTIVE )
+                {
+                    throw new InactiveGroupException( "group not active" );
+                }
+                entry = (ActiveEntry) active.get( id );
+            }
+            if( entry == null )
+            {
+                // REMIND: should this be silent?
+                throw new UnknownObjectException( "object not active" );
+            }
+
+            try
+            {
+                if( !Activatable.unexportObject( entry.impl, false ) )
+                {
+                    return false;
+                }
+            }
+            catch( NoSuchObjectException allowUnexportedObjects )
+            {
+            }
+
+            try
+            {
+                doAction( new PrivilegedExceptionAction()
+                {
+                    public Object run() throws Exception
+                    {
+                        monitor.inactiveObject( id );
+                        return null;
+                    }
+                } );
+            }
+            catch( UnknownObjectException allowUnregisteredObjects )
+            {
+            }
+
+            synchronized( ActivationGroupImpl.class )
+            {
+                active.remove( id );
+            }
+        }
+        finally
+        {
+            releaseLock( id );
+            checkInactiveGroup();
+        }
+
+        return true;
+    }
+
+    /**
+     * Attempts to make the remote object that is associated with the
+     * specified activation identifier, and that was exported through the
+     * specified exporter, inactive. The {@link Exporter#unexport unexport}
+     * method of the specified exporter is called with <code>false</code>
+     * as an argument. If that call returns <code>false</code>, this method
+     * returns <code>false</code>. If that call returns <code>true</code>,
+     * the object is marked inactive in this virtual machine, the
+     * superclass <code>inactiveObject</code> method is called with the
+     * activation identifier, with the <code>ActivationMonitor</code>
+     * constraints (if any) set as contextual client constraints, and with
+     * the group's subject (if any) set as the executing subject, and this
+     * method returns <code>true</code>.
+     *
+     * @param id       the activation identifier
+     * @param exporter the exporter to use to unexport the object
+     * @return <code>true</code> if the object was successfully made
+     *         inactive; <code>false</code> otherwise
+     * @throws UnknownObjectException if the object is not known to be
+     *                                active (it may already be inactive)
+     * @throws ActivationException    if an activation error occurs
+     * @throws InactiveGroupException if the group is inactive
+     * @throws RemoteException        if the remote call to the activation monitor
+     *                                fails
+     * @throws SecurityException      if a security manager exists and invoking
+     *                                its {@link SecurityManager#checkPermission checkPermission} method
+     *                                with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.inactiveObject")</code>
+     *                                throws a <code>SecurityException</code>
+     */
+    public boolean inactiveObject( final ActivationID id, Exporter exporter )
+        throws ActivationException, RemoteException
+    {
+        SecurityManager security = System.getSecurityManager();
+        if( security != null )
+        {
+            security.checkPermission( inactiveObjectPermission );
+        }
+        acquireLock( id );
+        try
+        {
+            ActiveEntry entry;
+            synchronized( ActivationGroupImpl.class )
+            {
+                if( state != ACTIVE )
+                {
+                    throw new InactiveGroupException( "group not active" );
+                }
+                entry = (ActiveEntry) active.get( id );
+            }
+            if( entry == null )
+            {
+                // REMIND: should this be silent?
+                throw new UnknownObjectException( "object not active" );
+            }
+
+            if( !exporter.unexport( false ) )
+            {
+                return false;
+            }
+
+            try
+            {
+                doAction( new PrivilegedExceptionAction()
+                {
+                    public Object run() throws Exception
+                    {
+                        monitor.inactiveObject( id );
+                        return null;
+                    }
+                } );
+            }
+            catch( UnknownObjectException allowUnregisteredObjects )
+            {
+            }
+
+            synchronized( ActivationGroupImpl.class )
+            {
+                active.remove( id );
+            }
+        }
+        finally
+        {
+            releaseLock( id );
+            checkInactiveGroup();
+        }
+
+        return true;
+    }
+
+    /*
+     * Determines if the group has become inactive and if so,
+     * marks it as such, notifies the daemon, and unexports the group.
+     */
+    private static void checkInactiveGroup()
+    {
+        synchronized( ActivationGroupImpl.class )
+        {
+            if( state == ACTIVE )
+            {
+                if( !active.isEmpty() || !lockedIDs.isEmpty() )
+                {
+                    return;
+                }
+                state = INACTIVE;
+            }
+            else if( state == INACTIVE )
+            {
+                return;
+            }
+            else if( state == CREATED )
+            {
+                state = UNUSED;
+            }
+            else
+            {
+                if( login != null )
+                {
+                    try
+                    {
+                        login.logout();
+                    }
+                    catch( LoginException e )
+                    {
+                    }
+                    login = null;
+                }
+                state = UNUSED;
+                return;
+            }
+        }
+        try
+        {
+            doAction( new PrivilegedExceptionAction()
+            {
+                public Object run() throws Exception
+                {
+                    monitor.inactiveGroup( groupID, incarnation );
+                    return null;
+                }
+            } );
+        }
+        catch( Exception ignoreDeactivateFailure )
+        {
+        }
+        Runnable action = new Runnable()
+        {
+            public void run()
+            {
+                long stop = System.currentTimeMillis() + unexportTimeout;
+                // allow a failing newInstance call to finish first
+                boolean force = false;
+                while( !exporter.unexport( force ) )
+                {
+                    long rem = stop - System.currentTimeMillis();
+                    if( rem <= 0 )
+                    {
+                        force = true;
+                    }
+                    else
+                    {
+                        try
+                        {
+                            Thread.sleep( Math.min( rem, unexportWait ) );
+                        }
+                        catch( InterruptedException e )
+                        {
+                        }
+                    }
+                }
+                if( login != null )
+                {
+                    try
+                    {
+                        login.logout();
+                    }
+                    catch( LoginException e )
+                    {
+                    }
+                }
+            }
+        };
+        if( state == UNUSED )
+        {
+            action.run();
+        }
+        else
+        {
+            Executor systemThreadPool =
+                (Executor) AccessController.doPrivileged(
+                    ( login == null ) ?
+                    (PrivilegedAction) new GetThreadPoolAction( false ) :
+                    new PrivilegedAction()
+                    {
+                        public Object run()
+                        {
+                            return Subject.doAsPrivileged(
+                                login.getSubject(),
+                                new GetThreadPoolAction( false ),
+                                null );
+                        }
+                    } );
+            systemThreadPool.execute( action, "UnexportGroup" );
+        }
+    }
+
+    /**
+     * Marks the object as active in this virtual machine, and calls the
+     * superclass <code>activeObject</code> method with the same arguments,
+     * with the <code>ActivationMonitor</code> constraints (if any) set as
+     * contextual client constraints, and with the group's subject (if any)
+     * set as the executing subject. Any <code>RemoteException</code>
+     * thrown by this call is caught and ignored. If the object is already
+     * marked as active in this virtual machine, this method simply
+     * returns.
+     *
+     * @param id   the activation identifier
+     * @param impl the active remote object
+     * @throws UnknownObjectException if no object is registered under
+     *                                the specified activation identifier
+     * @throws ActivationException    if an activation error occurs
+     * @throws InactiveGroupException if the group is inactive
+     * @throws SecurityException      if a security manager exists and invoking
+     *                                its {@link SecurityManager#checkPermission checkPermission} method
+     *                                with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.activeObject")</code>
+     *                                throws a <code>SecurityException</code>
+     */
+    public void activeObject( final ActivationID id, Remote impl )
+        throws ActivationException
+    {
+        SecurityManager security = System.getSecurityManager();
+        if( security != null )
+        {
+            security.checkPermission( activeObjectPermission );
+        }
+        final ActiveEntry entry = new ActiveEntry( impl );
+        acquireLock( id );
+        try
+        {
+            synchronized( ActivationGroupImpl.class )
+            {
+                if( state != ACTIVE )
+                {
+                    throw new InactiveGroupException( "group not active" );
+                }
+                if( active.containsKey( id ) )
+                {
+                    return;
+                }
+                active.put( id, entry );
+            }
+            // created new entry, so inform monitor of active object
+            try
+            {
+                doAction( new PrivilegedExceptionAction()
+                {
+                    public Object run() throws Exception
+                    {
+                        monitor.activeObject( id, entry.mobj );
+                        return null;
+                    }
+                } );
+            }
+            catch( RemoteException ignore )
+            {
+                // daemon can still find it by calling newInstance
+            }
+        }
+        finally
+        {
+            releaseLock( id );
+            checkInactiveGroup();
+        }
+    }
+
+    /**
+     * Execute the specified action on behalf of the server subject without
+     * requiring the caller to have doAsPrivileged permission.
+     */
+    private static Object doAction( final PrivilegedExceptionAction action )
+        throws ActivationException, RemoteException
+    {
+        try
+        {
+            if( login == null )
+            {
+                return AccessController.doPrivileged( action );
+            }
+            else
+            {
+                return AccessController.doPrivileged(
+                    new PrivilegedExceptionAction()
+                    {
+                        public Object run() throws Exception
+                        {
+                            try
+                            {
+                                return Subject.doAsPrivileged(
+                                    login.getSubject(), action, null );
+                            }
+                            catch( PrivilegedActionException e )
+                            {
+                                throw e.getException();
+                            }
+                        }
+                    } );
+            }
+        }
+        catch( PrivilegedActionException e )
+        {
+            Exception ex = e.getException();
+            if( ex instanceof RemoteException )
+            {
+                throw (RemoteException) ex;
+            }
+            else if( ex instanceof ActivationException )
+            {
+                throw (ActivationException) ex;
+            }
+            else
+            {
+                throw new ActivationException( "unexpected failure", ex );
+            }
+        }
+    }
+
+    /**
+     * Entry in table for active object.
+     */
+    private static class ActiveEntry
+    {
+        Remote impl;
+        MarshalledObject mobj;
+
+        ActiveEntry( Remote impl ) throws ActivationException
+        {
+            this.impl = impl;
+            try
+            {
+                Object proxy;
+                if( impl instanceof ProxyAccessor )
+                {
+                    proxy = ( (ProxyAccessor) impl ).getProxy();
+                    if( proxy == null )
+                    {
+                        throw new ActivationException(
+                            "ProxyAccessor.getProxy returned null" );
+                    }
+                }
+                else
+                {
+                    proxy = impl;
+                }
+                this.mobj = new MarshalledObject( proxy );
+            }
+            catch( IOException e )
+            {
+                throw new ActivationException(
+                    "failed to marshal remote object", e );
+            }
+	}
+    }
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupInit.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupInit.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupInit.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivationGroupInit.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,113 @@
+/*
+ * 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.lang.reflect.Method;
+import java.rmi.activation.ActivationGroup;
+import java.rmi.activation.ActivationGroupDesc;
+import java.rmi.activation.ActivationGroupID;
+import java.rmi.server.RMIClassLoader;
+import java.util.Collections;
+import net.jini.io.MarshalInputStream;
+
+/**
+ * This is the bootstrap code to start a virtual machine (VM) executing an
+ * activation group.
+ *
+ * The activator spawns (as a child process) an activation group as needed
+ * and directs activation requests to the appropriate activation
+ * group. After spawning the VM, the activator passes some
+ * information to the bootstrap code via its stdin: <p>
+ * <ul>
+ * <li> the activation group's id,
+ * <li> the activation group's descriptor (an instance of the class
+ * java.rmi.activation.ActivationGroupDesc) for the group, and
+ * <li> the group's incarnation number.
+ * </ul><p>
+ *
+ * When the bootstrap VM starts executing, it reads group id and
+ * descriptor from its stdin so that it can create the activation
+ * group for the VM.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+class ActivationGroupInit
+{
+    private ActivationGroupInit()
+    {
+    }
+
+    /**
+     * Main program to start a VM for an activation group.
+     */
+    public static void main( String args[] )
+    {
+        try
+        {
+            if( System.getSecurityManager() == null )
+            {
+                System.setSecurityManager( new SecurityManager() );
+            }
+            MarshalInputStream in =
+                new MarshalInputStream(
+                    System.in,
+                    ActivationGroupInit.class.getClassLoader(),
+                    false, null, Collections.EMPTY_LIST );
+            in.useCodebaseAnnotations();
+            ActivationGroupID id = (ActivationGroupID) in.readObject();
+            ActivationGroupDesc desc = (ActivationGroupDesc) in.readObject();
+            long incarnation = in.readLong();
+            Class cl = RMIClassLoader.loadClass( desc.getLocation(),
+                                                 desc.getClassName() );
+            try
+            {
+                Method create =
+                    cl.getMethod( "createGroup",
+                                  new Class[]{ ActivationGroupID.class,
+                                               ActivationGroupDesc.class,
+                                               long.class } );
+                create.invoke( null, new Object[]{ id, desc,
+                                                   new Long( incarnation ) } );
+            }
+            catch( NoSuchMethodException e )
+            {
+                ActivationGroup.createGroup( id, desc, incarnation );
+            }
+        }
+        catch( Exception e )
+        {
+            System.err.println( "Exception in starting ActivationGroupInit:" );
+            e.printStackTrace();
+        }
+        finally
+        {
+            try
+            {
+                System.in.close();
+                // note: system out/err shouldn't be closed
+                // since the parent may want to read them.
+            }
+            catch( Exception ex )
+            {
+                // ignore exceptions
+            }
+        }
+    }
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activator.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activator.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activator.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/Activator.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,59 @@
+/*
+ * 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.MarshalledWrapper;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationException;
+import java.rmi.activation.ActivationID;
+import java.rmi.activation.UnknownObjectException;
+import net.jini.io.MarshalledInstance;
+
+/**
+ * The activator used by {@link AID}.  The {@link #activate activate}
+ * method returns the activated object's proxy as a {@link
+ * MarshalledInstance} wrapped in a {@link MarshalledWrapper} so that
+ * integrity can be verified on the enclosed proxy.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+interface Activator extends Remote
+{
+
+    /**
+     * Returns a marshalled proxy wrapped in a
+     * <code>MarshalledWrapper</code> for the activated object
+     * corresponding to the specified activation identifier.
+     *
+     * @param id    the activation identifier for the remote object
+     * @param force the value to pass to the <code>activate</code> method
+     *              of the activation identifier
+     * @return the remote object's proxy as a <code>MarshalledInstance</code>
+     *         wrapped in a <code>MarshalledWrapper</code>
+     * @throws ActivationException    if object activation fails
+     * @throws UnknownObjectException if object is unknown (not registered)
+     * @throws RemoteException        if remote call fails
+     */
+    MarshalledWrapper activate( ActivationID id, boolean force )
+        throws ActivationException, RemoteException;
+
+
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorPermission.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorPermission.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorPermission.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorPermission.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,80 @@
+/*
+ * 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 net.jini.security.AccessPermission;
+
+/**
+ * Represents permissions that can be used to express the access control
+ * policy for the activator, the remote object handling object activation,
+ * if that remote object is exported with {@link
+ * net.jini.jeri.BasicJeriExporter}. This class can be passed to {@link
+ * net.jini.jeri.BasicInvocationDispatcher}, and then used in security
+ * policy permission grants.
+ *
+ * <p>This permission class can be used for server-side access control of
+ * remote object activation initiated by the client-side {@link
+ * java.rmi.activation.ActivationID#activate ActivationID.activate} method.
+ * The server-side method name for this operation is <code>activate</code>.
+ *
+ * <p>An instance contains a name (also referred to as a "target name") but no
+ * actions list; you either have the named permission or you don't. The
+ * convention is that the target name is the fully qualified name of the
+ * remote method being invoked. Wildcard matches are supported using the
+ * syntax specified by {@link AccessPermission}.
+ *
+ * <p>The possible target names for the activator are:
+ * <table border=1 cellpadding=5>
+ * <tr>
+ * <th>Permission Target Name</th>
+ * <th>What the Permission Allows</th>
+ * <th>Risks of Allowing this Permission</th>
+ * </tr>
+ * <tr>
+ * <td>activate</td>
+ * <td>invoking the activator's <code>activate</code> method</td>
+ * <td>The caller can activate an object and obtain the proxy for it if it
+ * knows the activation identifier.</td>
+ * </tr>
+ * <tr>
+ * <td>net.jini.security.proxytrust.ProxyTrust.getProxyVerifier</td>
+ * <td>invoking
+ * {@link net.jini.export.proxytrust.ProxyTrust#getProxyVerifier
+ * ProxyTrust.getProxyVerifier}</td>
+ * <td>The caller can verify trust in activation identifiers.</td>
+ * </tr>
+ * </table>
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public final class ActivatorPermission extends AccessPermission
+{
+    private static final long serialVersionUID = 1133348041678833146L;
+
+    /**
+     * Creates an instance with the specified name.
+     *
+     * @param name the target name
+     */
+    public ActivatorPermission( String name )
+    {
+        super( name );
+    }
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorSunJrmpExporter.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorSunJrmpExporter.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorSunJrmpExporter.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ActivatorSunJrmpExporter.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,52 @@
+/*
+ * 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.activation.ActivationSystem;
+import java.rmi.server.ObjID;
+
+/**
+ * JRMP exporter to export an <code>Activator</code> using the well-known
+ * activator object identifier. This exporter implementation is only designed
+ * to work with Java(TM) 2 Standard Edition implementations from Sun
+ * Microsystems, Inc.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public class ActivatorSunJrmpExporter extends SunJrmpExporter
+{
+    /**
+     * Creates a JRMP exporter that exports on the standard port (1098).
+     */
+    public ActivatorSunJrmpExporter()
+    {
+        super( ObjID.ACTIVATOR_ID, ActivationSystem.SYSTEM_PORT );
+    }
+
+    /**
+     * Creates a JRMP exporter that exports on the specified port.
+     *
+     * @param port the port (if zero, an anonymous port will be chosen)
+     */
+    public ActivatorSunJrmpExporter( int port )
+    {
+        super( ObjID.ACTIVATOR_ID, port );
+    }
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ConstrainableAID.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ConstrainableAID.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ConstrainableAID.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/ConstrainableAID.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,271 @@
+/*
+ * 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.ConstrainableProxyUtil;
+import java.io.InvalidObjectException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+import java.rmi.activation.ActivationID;
+import java.rmi.server.UID;
+import net.jini.core.constraint.MethodConstraints;
+import net.jini.core.constraint.RemoteMethodControl;
+import net.jini.export.proxytrust.ProxyTrustIterator;
+import net.jini.export.proxytrust.SingletonProxyTrustIterator;
+import net.jini.export.proxytrust.TrustEquivalence;
+import net.jini.security.TrustVerifier;
+
+/**
+ * A subclass of <code>AID</code> that implements the {@link
+ * RemoteMethodControl} interface by delegating to the contained activator.
+ *
+ * <p>This class exists as a convenience for activation system daemon
+ * implementations supporting <code>RemoteMethodControl</code> on proxies,
+ * to avoid requiring all such implementations to make code available for
+ * dynamic download to clients.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+final class ConstrainableAID extends AID
+    implements RemoteMethodControl, TrustEquivalence
+{
+    private static final long serialVersionUID = 2625527831091986783L;
+    private static final Method[] methodMapping = new Method[2];
+
+    static
+    {
+        try
+        {
+            methodMapping[ 0 ] =
+                ActivationID.class.getMethod( "activate",
+                                              new Class[]{ boolean.class } );
+            methodMapping[ 1 ] =
+                Activator.class.getMethod( "activate",
+                                           new Class[]{ ActivationID.class,
+                                                        boolean.class } );
+        }
+        catch( NoSuchMethodException e )
+        {
+            throw new AssertionError( e );
+        }
+    }
+
+    /**
+     * the client constraints
+     */
+    private final MethodConstraints constraints;
+
+    static final class State implements Serializable
+    {
+        private static final long serialVersionUID = 1673734348880788487L;
+        private final Activator activator;
+        private final UID uid;
+        private final MethodConstraints constraints;
+
+        State( Activator activator, UID uid, MethodConstraints constraints )
+        {
+            this.activator = activator;
+            this.uid = uid;
+            this.constraints = constraints;
+        }
+
+        private Object readResolve() throws InvalidObjectException
+        {
+            ConstrainableProxyUtil.verifyConsistentConstraints(
+                constraints, activator, methodMapping );
+            return new ConstrainableAID( activator, uid, constraints );
+        }
+    }
+
+    /**
+     * A <code>ProxyTrust</code> trust verifier for
+     * <code>ConstrainableAID</code> instances.
+     *
+     * @since 2.0
+     */
+    static final class Verifier implements TrustVerifier, Serializable
+    {
+        private static final long serialVersionUID = 570158651966790233L;
+
+        /**
+         * The expected activator.
+         *
+         * @serial
+         */
+        private final RemoteMethodControl activator;
+
+        /**
+         * Creates a verifier for the specified activator.
+         *
+         * @param activator the activator
+         * @throws IllegalArgumentException if the specified activator is
+         *                                  not an instance of <code>RemoteMethodControl</code> or
+         *                                  <code>TrustEquivalence</code>
+         */
+        Verifier( Activator activator )
+        {
+            if( !( activator instanceof RemoteMethodControl ) )
+            {
+                throw new IllegalArgumentException(
+                    "activator not a RemoteMethodControl instance" );
+            }
+            else if( !( activator instanceof TrustEquivalence ) )
+            {
+                throw new IllegalArgumentException(
+                    "activator must implement TrustEquivalence" );
+            }
+            this.activator = (RemoteMethodControl) activator;
+        }
+
+        /**
+         * Verifies trust in a proxy. Returns <code>true</code> if the
+         * proxy passed as an argument is an instance of
+         * <code>ConstrainableAID</code> and that proxy's internal
+         * activator instance is trust equivalent to the activator of this
+         * verifier; returns <code>false</code> otherwise.
+         */
+        public boolean isTrustedObject( Object obj, TrustVerifier.Context ctx )
+            throws RemoteException
+        {
+            if( obj == null || ctx == null )
+            {
+                throw new NullPointerException();
+            }
+            else if( !( obj instanceof ConstrainableAID ) )
+            {
+                return false;
+            }
+            RemoteMethodControl act =
+                (RemoteMethodControl) ( (ConstrainableAID) obj ).activator;
+            MethodConstraints mc = act.getConstraints();
+            TrustEquivalence trusted =
+                (TrustEquivalence) activator.setConstraints( mc );
+            return trusted.checkTrustEquivalence( act );
+        }
+    }
+
+    /**
+     * Creates an activation identifier containing the specified
+     * remote object activator, a new unique identifier, and
+     * <code>null</code> client constraints.
+     *
+     * @param activator the activator
+     * @param uid       the unique identifier
+     * @throws IllegalArgumentException if the specified activator is not
+     *                                  an instance of <code>RemoteMethodControl</code> or
+     *                                  <code>TrustEquivalence</code>
+     */
+    public ConstrainableAID( Activator activator, UID uid )
+    {
+        this( activator, uid, null );
+    }
+
+    /**
+     * Creates an activation identifier containing the specified
+     * remote object activator and a new unique identifier.
+     *
+     * @param activator   the activator
+     * @param uid         the unique identifier
+     * @param constraints the client constraints, or <code>null</code>
+     * @throws IllegalArgumentException if the specified activator is not
+     *                                  an instance of <code>RemoteMethodControl</code> or
+     *                                  <code>TrustEquivalence</code>
+     */
+    private ConstrainableAID( Activator activator,
+                              UID uid,
+                              MethodConstraints constraints )
+    {
+        super( activator, uid );
+        if( !( activator instanceof RemoteMethodControl ) )
+        {
+            throw new IllegalArgumentException(
+                "activator not RemoteMethodControl instance" );
+        }
+        else if( !( activator instanceof TrustEquivalence ) )
+        {
+            throw new IllegalArgumentException(
+                "activator not TrustEquivalence instance" );
+        }
+        this.constraints = constraints;
+    }
+
+    /**
+     * Returns an iterator that yields the activator.
+     */
+    private ProxyTrustIterator getProxyTrustIterator()
+    {
+        return new SingletonProxyTrustIterator( activator );
+    }
+
+    /**
+     * Returns a new copy of this activation identifier containing the same
+     * unique identifier and a copy of the activator with the new specified
+     * constraints.
+     *
+     * @param constraints the client constraints, or <code>null</code>
+     * @return a new copy of this activation identifier containing the same
+     *         unique identifier and a copy of the activator with the new specified
+     *         client constraints
+     * @see #getConstraints
+     */
+    public RemoteMethodControl setConstraints( MethodConstraints constraints )
+    {
+        MethodConstraints actConstraints =
+            ConstrainableProxyUtil.translateConstraints( constraints,
+                                                         methodMapping );
+        RemoteMethodControl act =
+            ( (RemoteMethodControl) activator ).setConstraints( actConstraints );
+        return new ConstrainableAID( (Activator) act, uid, constraints );
+    }
+
+    /**
+     * Returns the client constraints.
+     *
+     * @return the client constraints, or <code>null</code>
+     * @see #setConstraints
+     */
+    public MethodConstraints getConstraints()
+    {
+        return constraints;
+    }
+
+    /**
+     * Returns true if the object is an instance of this class with the same
+     * UID and a trust equivalent activator.
+     */
+    public boolean checkTrustEquivalence( Object obj )
+    {
+        if( !( obj instanceof ConstrainableAID ) )
+        {
+            return false;
+        }
+        ConstrainableAID aid = (ConstrainableAID) obj;
+        return ( uid.equals( aid.uid ) &&
+                 ( (TrustEquivalence) activator ).checkTrustEquivalence(
+                     aid.activator ) );
+    }
+
+    private Object writeReplace()
+    {
+        return new State(activator, uid, constraints);
+    }
+
+}

Added: incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/DefaultGroupPolicy.java
URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/DefaultGroupPolicy.java?rev=724976&view=auto
==============================================================================
--- incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/DefaultGroupPolicy.java (added)
+++ incubator/river/jtsk/skunk/niclas1/services/phoenix/src/main/java/com/sun/jini/phoenix/DefaultGroupPolicy.java Tue Dec  9 21:12:27 2008
@@ -0,0 +1,122 @@
+/*
+ * 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.activation.ActivationGroupDesc;
+import java.security.AccessControlException;
+import java.security.Permission;
+import java.util.Enumeration;
+import java.util.Properties;
+import net.jini.jeri.BasicInvocationDispatcher;
+
+/**
+ * Group policy that requires the appropriate {@link ExecPermission} and
+ * set of {@link ExecOptionPermission} have been granted to the client
+ * subject or the empty protection domain (a domain with all
+ * <code>null</code> elements) if there is no client subject.
+ *
+ * @author Sun Microsystems, Inc.
+ * @since 2.0
+ */
+public class DefaultGroupPolicy implements GroupPolicy
+{
+
+    /**
+     * Checks permissions for the specified group descriptor. If the group
+     * class name in the descriptor is not <code>null</code> and is not
+     * {@link ActivationGroupImpl}, or if the class location or the
+     * initialization data in the descriptor is not <code>null</code>, an
+     * <code>AccessControlException</code> is thrown. For each property in
+     * the descriptor's property overrides, {@link #checkPermission
+     * checkPermission} is called with an {@link ExecOptionPermission}
+     * constructed with a target of the form "-D<i>name</i>=<i>value</i>",
+     * where <i>name</i> is the name of the property and <i>value</i> is the
+     * value of the property. If the command environment in the descriptor is
+     * not <code>null</code>, then additional checks are made as follows. If
+     * the command path is not <code>null</code>, <code>checkPermission</code>
+     * is called with an {@link ExecPermission} constructed with the path as
+     * a target. For each command option, <code>checkPermission</code> is
+     * called with an <code>ExecOptionPermission</code> constructed with the
+     * option as a target.
+     *
+     * @param desc the activation group descriptor
+     * @throws AccessControlException if permission is not granted to create
+     *                                the specified group
+     */
+    public void checkGroup( ActivationGroupDesc desc )
+    {
+        String groupClassName = desc.getClassName();
+        if( ( groupClassName != null &&
+              !groupClassName.equals(
+                  "com.sun.jini.phoenix.ActivationGroupImpl" ) ) ||
+            desc.getLocation() != null ||
+            desc.getData() != null )
+        {
+            throw new AccessControlException(
+                "access denied (custom group implementation not allowed)" );
+        }
+
+        Properties props = desc.getPropertyOverrides();
+        if( props != null )
+        {
+            Enumeration p = props.propertyNames();
+            while( p.hasMoreElements() )
+            {
+                String name = (String) p.nextElement();
+                String option = ( "\"-D" + name + "=" +
+                                  props.getProperty( name ) + "\"" );
+                checkPermission( new ExecOptionPermission( option ) );
+            }
+        }
+
+        ActivationGroupDesc.CommandEnvironment cmdenv;
+        cmdenv = desc.getCommandEnvironment();
+        if( cmdenv != null )
+        {
+            String path = cmdenv.getCommandPath();
+            if( path != null )
+            {
+                checkPermission( new ExecPermission( path ) );
+            }
+
+            String[] options = cmdenv.getCommandOptions();
+            if( options != null )
+            {
+                for( int i = 0; i < options.length; i++ )
+                {
+                    checkPermission( new ExecOptionPermission(
+                        "\"" + options[ i ] + "\"" ) );
+                }
+            }
+        }
+    }
+
+    /**
+     * Calls {@link BasicInvocationDispatcher#checkClientPermission
+     * BasicInvocationDispatcher.checkClientPermission} with the
+     * specified permission.
+     *
+     * @param p the permission being checked
+     * @throws AccessControlException if permission is not granted
+     */
+    protected void checkPermission( Permission p )
+    {
+        BasicInvocationDispatcher.checkClientPermission( p );
+    }
+}