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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * loginContext</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>{@link javax.security.auth.login.LoginContext}</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>null</code>
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * inheritGroupSubject</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>boolean</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>false</code>
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * instantiatorExporter</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.export.Exporter}</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> retains existing JRMP export of instantiator
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * monitorPreparer</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>new {@link
+ * net.jini.security.BasicProxyPreparer}()</code>
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * systemPreparer</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>new {@link
+ * net.jini.security.BasicProxyPreparer}()</code>
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * unexportTimeout</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>int</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>60000</code>
+ * <tr valign="top"> <td>   <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">•</font>
+ * <th scope="col" align="left" colspan="2"> <font size="+1"><code>
+ * unexportWait</code></font>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Type: <td> <code>int</code>
+ * <tr valign="top"> <td>   <th scope="row" align="right">
+ * Default: <td> <code>10</code>
+ * <tr valign="top"> <td>   <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 );
+ }
+}