View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.river.phoenix.group;
20  
21  import java.io.IOException;
22  import java.io.ObjectStreamClass;
23  import java.io.ObjectStreamField;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  import java.rmi.MarshalledObject;
27  import java.rmi.NoSuchObjectException;
28  import java.rmi.Remote;
29  import java.rmi.RemoteException;
30  import java.rmi.activation.Activatable;
31  import java.rmi.activation.ActivationDesc;
32  import java.rmi.activation.ActivationException;
33  import java.rmi.activation.ActivationGroupDesc;
34  import java.rmi.activation.ActivationGroupID;
35  import java.rmi.activation.ActivationID;
36  import java.rmi.activation.ActivationInstantiator;
37  import java.rmi.activation.ActivationMonitor;
38  import java.rmi.activation.ActivationSystem;
39  import java.rmi.activation.UnknownGroupException;
40  import java.rmi.activation.UnknownObjectException;
41  import java.rmi.server.ExportException;
42  import java.rmi.server.RemoteObject;
43  import java.rmi.server.UnicastRemoteObject;
44  import java.security.AccessController;
45  import java.security.Permission;
46  import java.security.PrivilegedAction;
47  import java.security.PrivilegedActionException;
48  import java.security.PrivilegedExceptionAction;
49  import java.util.ArrayList;
50  import java.util.HashMap;
51  import java.util.List;
52  import java.util.Map;
53  import javax.security.auth.Subject;
54  import javax.security.auth.login.LoginContext;
55  import javax.security.auth.login.LoginException;
56  import net.jini.activation.ActivationGroup;
57  import net.jini.config.Configuration;
58  import net.jini.config.ConfigurationException;
59  import net.jini.config.ConfigurationProvider;
60  import net.jini.export.Exporter;
61  import net.jini.export.ProxyAccessor;
62  import net.jini.io.MarshalledInstance;
63  import net.jini.jeri.AtomicILFactory;
64  import net.jini.jeri.BasicJeriExporter;
65  import net.jini.jeri.tcp.TcpServerEndpoint;
66  import net.jini.loader.ClassLoading;
67  import net.jini.security.BasicProxyPreparer;
68  import net.jini.security.ProxyPreparer;
69  import net.jini.security.Security;
70  import net.jini.security.TrustVerifier;
71  import net.jini.security.proxytrust.ServerProxyTrust;
72  import org.apache.river.api.io.AtomicSerial;
73  import org.apache.river.api.io.AtomicSerial.GetArg;
74  import org.apache.river.phoenix.common.AccessAtomicILFactory;
75  import org.apache.river.proxy.BasicProxyTrustVerifier;
76  import org.apache.river.thread.Executor;
77  import org.apache.river.thread.GetThreadPoolAction;
78  import org.apache.river.phoenix.dl.MonitorPermission;
79  import org.apache.river.phoenix.dl.InactiveGroupException;
80  import org.apache.river.phoenix.common.AccessILFactory;
81  import org.apache.river.phoenix.common.ActivationGroupData;
82  
83  /**
84   * The default activation group implementation for phoenix.  Instances of
85   * this class are configurable through a {@link Configuration}, as detailed
86   * further below, and provide the necessary support to allow exporter-based
87   * remote objects to go inactive.  Instances of this class support the
88   * creation of remote objects through the normal activatable constructor;
89   * an activatable remote object must either implement the {@link
90   * ProxyAccessor} interface to return a suitable proxy for the remote
91   * object, or the remote object must itself be serializable and marshalling
92   * the object must produce a suitable proxy for the remote object.
93   * 
94   * <p>An instance of this class can be configured by specifying an
95   * {@link ActivationGroupData} instance containing configuration options
96   * as the initialization data for the activation group. Typically
97   * this is accomplished indirectly, by setting the
98   * <code>groupConfig</code> configuration entry for
99   * phoenix itself. The following entries are obtained from the configuration,
100  * all for the component named <code>org.apache.river.phoenix</code>:
101  *
102  *  <table summary="Describes the loginContext configuration entry"
103  *         border="0" cellpadding="2">
104  *    <tr valign="top">
105  *      <th scope="col">&#X2022;
106  *      <th scope="col" align="left" colspan="2"><code>
107  *      loginContext</code>
108  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
109  *      Type: <td> <code>{@link javax.security.auth.login.LoginContext}</code>
110  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
111  *      Default: <td> <code>null</code>
112  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
113  *      Description: <td> JAAS login context
114  *  </table>
115  *
116  *  <table summary="Describes the inheritGroupSubject configuration entry"
117  *         border="0" cellpadding="2">
118  *    <tr valign="top">
119  *      <th scope="col">&#X2022;
120  *      <th scope="col" align="left" colspan="2"><code>
121  *      inheritGroupSubject</code>
122  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
123  *      Type: <td> <code>boolean</code>
124  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
125  *      Default: <td> <code>false</code>
126  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
127  *      Description: <td> if <code>true</code>, group subject is inherited
128  *		when an activatable object is created 
129  *  </table>
130  *
131  *  <table summary="Describes the instantiatorExporter configuration entry"
132  *         border="0" cellpadding="2">
133  *    <tr valign="top">
134  *      <th scope="col">&#X2022;
135  *      <th scope="col" align="left" colspan="2"><code>
136  *      instantiatorExporter</code>
137  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
138  *      Type: <td> <code>{@link net.jini.export.Exporter}</code>
139  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
140  *      Default: <td> retains existing JRMP export of instantiator
141  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
142  *      Description: <td> {@link java.rmi.activation.ActivationInstantiator}
143  *		exporter
144  *  </table>
145  *
146  *  <table summary="Describes the monitorPreparer configuration entry"
147  *         border="0" cellpadding="2">
148  *    <tr valign="top">
149  *      <th scope="col">&#X2022;
150  *      <th scope="col" align="left" colspan="2"><code>
151  *      monitorPreparer</code>
152  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
153  *      Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
154  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
155  *      Default: <td> <code>new {@link
156  *		net.jini.security.BasicProxyPreparer}()</code> 
157  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
158  *      Description: <td> {@link java.rmi.activation.ActivationMonitor}
159  *		proxy preparer 
160  *  </table>
161  *
162  *  <table summary="Describes the systemPreparer configuration entry"
163  *         border="0" cellpadding="2">
164  *    <tr valign="top">
165  *      <th scope="col">&#X2022;
166  *      <th scope="col" align="left" colspan="2"> <code>
167  *      systemPreparer</code>
168  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
169  *      Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
170  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
171  *      Default: <td> <code>new {@link
172  *		net.jini.security.BasicProxyPreparer}()</code> 
173  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
174  *      Description: <td> {@link java.rmi.activation.ActivationSystem}
175  *		proxy preparer 
176  *  </table>
177  *
178  *  <table summary="Describes the unexportTimeout configuration entry"
179  *         border="0" cellpadding="2">
180  *    <tr valign="top">
181  *      <th scope="col">&#X2022;
182  *      <th scope="col" align="left" colspan="2"><code>
183  *      unexportTimeout</code>
184  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
185  *      Type: <td> <code>int</code>
186  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
187  *      Default: <td> <code>60000</code>
188  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
189  *      Description: <td> maximum time in milliseconds to wait for
190  *		in-progress calls to finish before forcibly unexporting the
191  *		group when going inactive 
192  *  </table>
193  *
194  *  <table summary="Describes the unexportWait configuration entry"
195  *         border="0" cellpadding="2">
196  *    <tr valign="top">
197  *      <th scope="col">&#X2022;
198  *      <th scope="col" align="left" colspan="2"> <code>
199  *      unexportWait</code>
200  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
201  *      Type: <td> <code>int</code>
202  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
203  *      Default: <td> <code>10</code>
204  *    <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
205  *      Description: <td> milliseconds to wait between unexport attempts
206  *		when going inactive 
207  *  </table>
208  * 
209  * <p>This class depends on its {@link #createGroup createGroup} method being
210  * called to initialize the activation group. As such, this class cannot be
211  * used in conjunction with the standard <code>rmid</code>.
212  *
213  * @author Sun Microsystems, Inc.
214  * 
215  * @since 2.0
216  **/
217 abstract class AbstractActivationGroup extends ActivationGroup
218     implements ServerProxyTrust
219 {
220     private static final long serialVersionUID = 5758693559430427303L;
221 
222     private static final String PHOENIX = "org.apache.river.phoenix";
223     /** instance has not been created */
224     private static final int UNUSED = 0;
225     /** in the middle of createGroup */
226     private static final int CREATING = 1;
227     /** constructor (including activeGroup) has succeeded */
228     private static final int CREATED = 2;
229     /** createGroup has succeeded */
230     private static final int ACTIVE = 3;
231     /** group is inactive */
232     private static final int INACTIVE = 4;
233     /** parameter types for activatable constructor */
234     private final static Class[] paramTypes = {
235 	ActivationID.class, MarshalledObject.class
236     };
237 
238     /* avoid serial field clutter */
239     private static final ObjectStreamField[] serialPersistentFields =
240 						ObjectStreamClass.NO_FIELDS;
241 
242     private static int state = UNUSED;
243     private static long incarnation;
244     /** original unprepared group id */
245     private static ActivationGroupID groupID;
246 
247     /** server LoginContext or null */
248     private static LoginContext login;
249     private static Exporter exporter;
250     /** true if calls should be refused, false otherwise */
251     private static boolean refuseCalls = false;
252     /** monitor proxy preparer */
253     private static ProxyPreparer monPreparer;
254     private static ActivationMonitor monitor;
255     /** timeout on wait for unexport to succeed */
256     private static long unexportTimeout;
257     /** timeout on wait between unexport attempts */
258     private static long unexportWait;
259     /** maps ActivationID to ActiveEntry */
260     private static Map active = new HashMap();
261     /** ActivationIDs with operations in progress */
262     private static List lockedIDs = new ArrayList();
263     /** true if group subject is inherited when creating activatable objects */
264     private static boolean inheritGroupSubject;
265 
266     /** permission to check for monitor's activeObject call */
267     private final static Permission activeObjectPermission =
268         new MonitorPermission(
269 	    "java.rmi.activation.ActivationMonitor.activeObject");
270     
271     /** permission to check for monitor's activeObject call */
272     private static Permission inactiveObjectPermission =
273         new MonitorPermission(
274 	    "java.rmi.activation.ActivationMonitor.inactiveObject");
275     
276     /** proxy for this activation group */
277     private ActivationInstantiator proxy;
278 
279     /**
280      * Creates an {@link java.rmi.activation.ActivationGroup} instance and
281      * returns it. An {@link ActivationGroupData} instance is extracted from
282      * the initialization data, and a {@link Configuration} is obtained by
283      * calling
284      * {@link net.jini.config.ConfigurationProvider#getInstance
285      * Configuration.Provider.getInstance} with the configuration options from
286      * that instance. A {@link LoginContext} is obtained from the
287      * <code>loginContext</code> configuration entry, if one exists; if the
288      * value is not <code>null</code>, a login is performed on that context,
289      * and the resulting {@link Subject} (set to be read-only) is used as the
290      * subject when executing the rest of this method. The subject is also
291      * used for all subsequent remote calls by this class to the
292      * {@link ActivationMonitor}. The {@link ActivationSystem} proxy
293      * (obtained from the <code>ActivationGroupID</code>) is passed to the
294      * {@link ProxyPreparer} given by the <code>systemPreparer</code>
295      * configuration entry, if one exists; a new
296      * <code>ActivationGroupID</code> is constructed with the resulting proxy.
297      * An {@link Exporter} instance is obtained from the
298      * <code>instantiatorExporter</code> configuration entry, if one exists;
299      * this exporter will be used (in the constructor of this class) to export
300      * the group. A <code>ProxyPreparer</code> instance is obtained from the
301      * <code>monitorPreparer</code> configuration entry, if one exists; this
302      * preparer will be used (in the constructor of this class) to prepare the
303      * <code>ActivationMonitor</code>. A call is then made to
304      * {@link ActivationGroup#createGroup ActivationGroup.createGroup} with
305      * the new group identifier, the activation group descriptor, and the
306      * group incarnation number, and the result of that call is returned.
307      *
308      * @param id the activation group identifier
309      * @param desc the activation group descriptor
310      * @param incarnation the group's incarnation number (zero on initial
311      * creation)
312      * @return the created activation group
313      * @throws ActivationException if a group already exists or if an
314      * exception occurs during group creation
315      */
316     public static synchronized
317 	java.rmi.activation.ActivationGroup createGroup(
318 					      final ActivationGroupID id,
319 					      final ActivationGroupDesc desc,
320 					      final long incarnation)
321         throws ActivationException
322     {
323 	if (state != UNUSED) {
324 	    throw new ActivationException("group previously created");
325 	}
326 	try {
327 	    final Configuration config = getConfiguration(desc.getData());
328 	    login = (LoginContext) config.getEntry(
329 			PHOENIX, "loginContext", LoginContext.class, null);
330 	    if (login != null) {
331 		login.login();
332 	    }
333 		
334 	    inheritGroupSubject =
335 		 config.getEntry(
336 		    PHOENIX, "inheritGroupSubject", boolean.class,
337 		    Boolean.FALSE);
338 	    
339 	    return (java.rmi.activation.ActivationGroup) doAction(
340 		new PrivilegedExceptionAction() {
341 		    public Object run() throws Exception {
342 			ProxyPreparer sysPreparer =
343 			    getPreparer(config, "systemPreparer");
344 			monPreparer = getPreparer(config,
345 						  "monitorPreparer");
346 			TcpServerEndpoint se =
347 			    TcpServerEndpoint.getInstance(0);
348 			Exporter defaultExporter =
349 			    new BasicJeriExporter(se, new AtomicILFactory(null, null, this.getClass().getClassLoader()));
350 			exporter = (Exporter) config.getEntry(
351 				PHOENIX, "instantiatorExporter",
352 				Exporter.class, defaultExporter);
353 			if (exporter == null) {
354 			    exporter = new AlreadyExportedExporter();
355 			}
356 			refuseCalls =
357 			    !(exporter instanceof AlreadyExportedExporter);
358 			unexportTimeout = getInt(config, "unexportTimeout",
359 						 60000);
360 			unexportWait = getInt(config, "unexportWait", 10);
361 			ActivationSystem sys = (ActivationSystem)
362 			    sysPreparer.prepareProxy(id.getSystem());
363 			AbstractActivationGroup.incarnation = incarnation;
364 			groupID = id;
365 			state = CREATING;
366 			ActivationGroupID gid = (sys == id.getSystem() ?
367 						 id : new WrappedGID(id, sys));
368 			Object group = ActivationGroup.createGroup(
369 						    gid, desc, incarnation);
370 			state = ACTIVE;
371 			return group;
372 		    }
373 		});
374 	} catch (ActivationException e) {
375 	    throw e;
376 	} catch (Exception e) {
377 	    throw new ActivationException("creation failed", e);
378 	} finally {
379 	    if (state != ACTIVE) {
380 		checkInactiveGroup();
381 	    }
382 	}
383     }
384 
385     /**
386      * Returns the configuration obtained from the specified marshalled
387      * object. 
388      */
389     private static Configuration getConfiguration(MarshalledObject mobj)
390 	throws ConfigurationException, IOException, ClassNotFoundException
391     {
392         /* mobj must be MarshalledObject unmarshalled */
393 	ClassLoader ccl = Thread.currentThread().getContextClassLoader();
394 	ClassLoader cl = AbstractActivationGroup.class.getClassLoader();
395 	Thread.currentThread().setContextClassLoader(cl);
396 	ActivationGroupData data = (ActivationGroupData) new MarshalledInstance(mobj).get(false);
397 	Thread.currentThread().setContextClassLoader(ccl);
398 	if (!covers(cl, ccl)) {
399 	    cl = ccl;
400 	}
401 	return ConfigurationProvider.getInstance(data.getConfig(), cl);
402     }
403     
404     /**
405      * Returns true if the first argument is either equal to, or is a
406      * descendant of, the second argument.  Null is treated as the root of
407      * the tree.
408      */
409     private static boolean covers(ClassLoader sub, ClassLoader sup) {
410 	if (sup == null) {
411 	    return true;
412 	} else if (sub == null) {
413 	    return false;
414 	}
415 	do {
416 	    if (sub == sup) {
417 		return true;
418 	    }
419 	    sub = sub.getParent();
420 	} while (sub != null);
421 	return false;
422     }
423 
424     /**
425      * Return a ProxyPreparer configuration entry.
426      */
427     private static ProxyPreparer getPreparer(Configuration config, String name)
428 	throws ConfigurationException
429     {
430 	return (ProxyPreparer) config.getEntry(PHOENIX, name,
431 					       ProxyPreparer.class,
432 					       new BasicProxyPreparer());
433     }
434 
435     /**
436      * Return an int configuration entry.
437      */
438     private static int getInt(Configuration config, String name, int defValue)
439 	throws ConfigurationException
440     {
441 	return ((Integer) config.getEntry(PHOENIX, name, int.class,
442 					  Integer.valueOf(defValue))).intValue();
443     }
444     private final ActivationGroupID id;
445 
446     /**
447      * ActivationGroupID containing a prepared ActivationSystem proxy and
448      * the original ActivationGroupID (with unprepared ActivationSystem
449      * proxy), that writeReplaces itself to the original.
450      */
451     @AtomicSerial
452     private static class WrappedGID extends ActivationGroupID {
453 	/** Original gid */
454 	private final ActivationGroupID id;
455 	/** Prepared system proxy */
456 	private final ActivationSystem sys;
457 
458 	WrappedGID(ActivationGroupID id, ActivationSystem sys) {
459 	    super(sys);
460 	    this.id = id;
461 	    this.sys = sys;
462 	}
463 	
464 	public WrappedGID(GetArg arg) throws IOException {
465 	    this(arg.get("id", null, ActivationGroupID.class),
466 		    arg.get("sys", null, ActivationSystem.class));
467 	}
468 
469 	/* override */
470 	public ActivationSystem getSystem() {
471 	    return sys;
472 	}
473 
474 	private Object writeReplace() {
475 	    return id;
476 	}
477     }
478 
479     /**
480      * Creates an instance with the specified group identifier and
481      * initialization data. This constructor must be called indirectly,
482      * via {@link #createGroup createGroup}. By default, this instance
483      * automatically exports itself as a {@link UnicastRemoteObject}. (This
484      * is a limitation of the existing activation system design.) If an
485      * {@link Exporter} was obtained by {@link #createGroup createGroup},
486      * then this instance is unexported from the JRMP runtime and re-exported
487      * using that exporter. (Any incoming remote calls received on the
488      * original JRMP export before this instance can be unexported will be
489      * refused with a security exception thrown.) The
490      * {@link ActivationSystem#activeGroup activeGroup} method of the
491      * activation system proxy (in the group identifier) is called to
492      * make the group active. The returned {@link ActivationMonitor} proxy
493      * is passed to the corresponding {@link ProxyPreparer} obtained by
494      * <code>createGroup</code>. Note that after this constructor returns,
495      * {@link ActivationGroup#createGroup ActivationGroup.createGroup} will
496      * also call <code>activeGroup</code> (so the activation system must
497      * accept idempotent calls to that method), but the
498      * <code>ActivationMonitor</code> proxy returned by that call will not be
499      * used.
500      * 
501      * @param id the activation group identifier
502      * @param data group initialization data (ignored)
503      * @throws RemoteException if the group could not be exported or
504      * made active, or proxy preparation fails
505      * @throws ActivationException if the constructor was not called
506      * indirectly from <code>createGroup</code>
507      */
508     AbstractActivationGroup(ActivationGroupID id, MarshalledObject data)
509 	throws ActivationException, RemoteException
510     {
511 	super(id);
512         this.id = id;
513 	synchronized (AbstractActivationGroup.class) {
514 	    if (state != CREATING) {
515 		throw new ActivationException("not called from createGroup");
516 	    }
517 	}
518 	
519     }
520     
521     void export() throws NoSuchObjectException, ExportException, 
522             RemoteException, UnknownGroupException, ActivationException
523     {
524         synchronized (AbstractActivationGroup.class) {
525             if (state != CREATING) {
526 		throw new ActivationException("not called from createGroup");
527 	    }
528             if (refuseCalls) {
529                 unexportObject(this, true);
530                 refuseCalls = false;
531             }
532             proxy = (ActivationInstantiator) exporter.export(this);
533             try {
534                 monitor = (ActivationMonitor) monPreparer.prepareProxy(
535                               id.getSystem().activeGroup(id, proxy, incarnation));
536                 state = CREATED;
537             } finally {
538                 if (state != CREATED) {
539                     exporter.unexport(true);
540                 }
541             }
542             monPreparer = null;
543         }
544     }
545 
546     public TrustVerifier getProxyVerifier() {
547         synchronized (AbstractActivationGroup.class){
548             return new BasicProxyTrustVerifier(proxy);
549         }
550     }
551     
552     /**
553      * Exporter for an object that is already exported to JRMP.
554      */
555     private static class AlreadyExportedExporter implements Exporter {
556 	/**
557 	 * A strong reference to the impl is OK because ActivationGroup
558 	 * also holds a strong reference.
559 	 */
560 	private Remote impl;
561 
562 	AlreadyExportedExporter() {
563 	}
564 
565 	public synchronized Remote export(Remote impl) throws ExportException {
566 	    this.impl = impl;
567 	    try {
568 		return RemoteObject.toStub(impl);
569 	    } catch (NoSuchObjectException e) {
570 		throw new ExportException("no stub found", e);
571 	    }
572 	}
573 
574 	public synchronized boolean unexport(boolean force) {
575 	    try {
576 		if (impl != null &&
577 		    !UnicastRemoteObject.unexportObject(impl, force))
578 		{
579 		    return false;
580 		}
581 	    } catch (NoSuchObjectException e) {
582 	    }
583 	    impl = null;
584 	    return true;
585 	}
586     }
587 
588     /**
589      * Returns the proxy for this remote object. Group creation was designed
590      * to rely on automatic stub replacement (as provided by the JRMP runtime),
591      * which is not supported by all exporters.
592      *
593      * @return the proxy for this remote object
594      */
595     protected Object writeReplace() {
596         synchronized (AbstractActivationGroup.class){
597             return proxy;
598         }
599     }
600 
601     /*
602      * Obtains a lock on the ActivationID id before returning. Allows only one
603      * thread at a time to hold a lock on a particular id.  If the lock for id
604      * is in use, all requests for an equivalent (in the .equals sense)
605      * id will wait for the id to be notified and use the supplied id as the
606      * next lock. The caller of "acquireLock" must execute the "releaseLock"
607      * method" to release the lock and "notifyAll" waiters for the id lock
608      * obtained from this method.  The typical usage pattern is as follows:
609      *
610      * acquireLock(id);
611      * try {
612      *    // do stuff pertaining to id...
613      * } finally {
614      *    releaseLock(id);
615      * }
616      */
617     private void acquireLock(ActivationID id) {
618 	while (true) {
619 	    synchronized (id) {
620 		synchronized (lockedIDs) {
621 		    int index = lockedIDs.indexOf(id);
622 		    if (index < 0) {
623 			lockedIDs.add(id);
624 			return;
625 		    }
626 		    ActivationID lockedID =
627 			(ActivationID) lockedIDs.get(index);
628 		    if (lockedID != id) {
629 			// don't wait on an id that won't be notified
630 			id = lockedID;
631 			continue;
632 		    }
633 		}
634 		try {
635 		    id.wait();
636 		} catch (InterruptedException ignore) {
637 		}
638 	    }
639 	}
640     }
641 
642     /*
643      * Releases the id lock obtained via the "acquireLock" method and then
644      * notifies all threads waiting on the lock.
645      */
646     private void releaseLock(ActivationID id) {
647 	synchronized (lockedIDs) {
648 	    id = (ActivationID) lockedIDs.remove(lockedIDs.indexOf(id));
649 	}
650 	synchronized (id) {
651 	    id.notifyAll();
652 	}
653     }
654 
655     /**
656      * Creates a new instance of an activatable remote object and returns
657      * a marshalled object containing the activated object's proxy.
658      *
659      * <p>If an active object already exists for the specified identifier,
660      * the existing marshalled object for it is returned.
661      *
662      * <p>Otherwise:
663      *
664      * <p>The class for the object is loaded by invoking {@link
665      * ClassLoading#loadClass}
666      * passing the class location (obtained by invoking {@link
667      * ActivationDesc#getLocation getLocation} on the activation
668      * descriptor) and the class name (obtained by invoking {@link
669      * ActivationDesc#getClassName getClassName} on the activation
670      * descriptor).
671      *
672      * <p>The new instance is constructed as follows. If the class defines
673      * a constructor with two parameters of type {@link ActivationID} and
674      * {@link MarshalledObject}, that constructor is called with the
675      * specified activation identifier and the initialization data from the
676      * specified activation descriptor. Otherwise, an
677      * <code>ActivationException</code> is thrown.
678      *
679      * <p>If the class loader of the object's class is a descendant of the
680      * current context class loader, then that class loader is set as the
681      * context class loader when the constructor is called.
682      *
683      * <p>If the <code>inheritGroupSubject</code> configuration entry is
684      * <code>true</code> then the constructor is invoked in an action
685      * passed to the {@link Security#doPrivileged Security.doPrivileged}
686      * method; otherwise the constructor is invoked in an action passed to
687      * the {@link AccessController#doPrivileged
688      * AccessController.doPrivileged} method.
689      *
690      * <p>A proxy for the newly created instance is returned as follows:
691      *
692      * <ul><li>If the newly created instance implements {@link
693      * ProxyAccessor}, a proxy is obtained by invoking the {@link
694      * ProxyAccessor#getProxy getProxy} method on that instance. If the
695      * obtained proxy is not <code>null</code>, that proxy is returned in a
696      * <code>MarshalledObject</code>; otherwise, an
697      * <code>ActivationException</code> is thrown.
698      *
699      * <li>If the newly created instance does not implement
700      * <code>ProxyAccessor</code>, the instance is returned in a
701      * <code>MarshalledObject</code>.  In this case, the instance must be
702      * serializable, and marshalling the instance must produce a suitable
703      * proxy for the remote object (for example, the object implements
704      * {@link java.io.Serializable} and defines a <code>writeReplace</code>
705      * method that returns the object's proxy).
706      * </ul>
707      *
708      * <p>If both the remote object and the activation group are exported
709      * using JRMP, then automatic stub replacement will produce the desired
710      * result, but otherwise the remote object implementation must provide
711      * a means for this group to obtain its proxy as indicated above.
712      *
713      * @throws ActivationException if the object's class could not be
714      * loaded, if the loaded class does not define the appropriate
715      * constructor, or any exception occurs activating the object
716      **/
717     public MarshalledObject newInstance(final ActivationID id,
718 					final ActivationDesc desc)
719 	throws ActivationException
720     {
721 	synchronized (AbstractActivationGroup.class) {
722 	    if (refuseCalls) {
723 		throw new SecurityException("call refused");
724 	    }
725 	}
726 
727 	acquireLock(id);
728 	try {
729 	    ActiveEntry entry;
730 	    synchronized (AbstractActivationGroup.class) {
731 		if (state != ACTIVE) {
732 		    throw new InactiveGroupException("group not active");
733 		}
734 		entry = (ActiveEntry) active.get(id);
735 	    }
736 	    if (entry != null) {
737 		return entry.mobj;
738 	    }
739 
740 	    String className = desc.getClassName();
741 	    final Class cl = ClassLoading.loadClass(desc.getLocation(),
742 						      className, null, false, null);
743 	    final Thread t = Thread.currentThread();
744 	    final ClassLoader savedCcl = t.getContextClassLoader();
745 	    final ClassLoader ccl =
746 		covers(cl.getClassLoader(), savedCcl) ?
747 		cl.getClassLoader() : savedCcl;
748 
749 	    Remote impl = null;
750 
751 	    /*
752 	     * Fix for 4164971: allow non-public activatable class
753 	     * and/or constructor, create the activatable object in a
754 	     * privileged block
755 	     */
756 	    try {
757 		PrivilegedExceptionAction action =
758 		   new PrivilegedExceptionAction() {
759 		      public Object run() throws InstantiationException,
760 			  NoSuchMethodException, IllegalAccessException,
761 			  InvocationTargetException, ActivationException
762 		      {
763 			  Object[] params = new Object[] {id, desc.getData()};
764 			  Constructor constructor =
765 			      cl.getDeclaredConstructor(paramTypes);
766 			  constructor.setAccessible(true);
767 			  try {
768 			      /*
769 			       * Fix for 4289544: make sure to set the
770 			       * context class loader to be the class
771 			       * loader of the impl class before
772 			       * constructing that class.
773 			       */
774 			      t.setContextClassLoader(ccl);
775 			      return constructor.newInstance(params);
776 			  } finally {
777 			      t.setContextClassLoader(savedCcl);
778 			  }
779 		      }
780 		   };
781 
782 		/*
783 		 * The activatable object is created is in a doPrivileged
784 		 * block to protect against user code which might have set
785 		 * a global socket factory (in which case application code
786 		 * would be on the stack).
787 		 */
788 		impl = (Remote) (inheritGroupSubject ?
789 				 Security.doPrivileged(action) :
790 				 AccessController.doPrivileged(action));
791 			
792 	    } catch (PrivilegedActionException pae) {
793 		throw pae.getException();
794 	    }
795 
796 	    entry = new ActiveEntry(impl);
797 	    synchronized (AbstractActivationGroup.class) {
798 		active.put(id, entry);
799 	    }
800 	    return entry.mobj;
801 	} catch (NoSuchMethodException e) {
802 	    /* user forgot to provide activatable constructor? */
803 	    throw new ActivationException(
804 		"activation constructor not defined", e);
805 	} catch (NoSuchMethodError e) {
806 	    /* code recompiled and user forgot to provide
807 	     *  activatable constructor?
808 	     */
809 	    throw new ActivationException(
810 		"activation constructor not defined", e);
811 	} catch (InvocationTargetException e) {
812 	    throw new ActivationException("exception constructing object",
813 					  e.getTargetException());
814 	} catch (ActivationException e) {
815 	    throw e;
816 	} catch (Exception e) {
817 	    throw new ActivationException("unable to activate object", e);
818 	} finally {
819 	    releaseLock(id);
820 	    checkInactiveGroup();
821 	}
822     }
823 
824     /**
825      * Attempts to make the remote object that is associated with the
826      * specified activation identifier, and that was exported as a JRMP
827      * {@link java.rmi.activation.Activatable} object, inactive. This method
828      * calls <code>Activatable.unexportObject</code> with the active remote
829      * object and <code>false</code>, to unexport the object. If that call
830      * returns <code>false</code>, this method returns <code>false</code>.
831      * If that call returns <code>true</code>, the object is marked inactive
832      * in this virtual machine, the superclass <code>inactiveObject</code>
833      * method is called with the same activation identifier, with the
834      * <code>ActivationMonitor</code> constraints (if any) set as
835      * contextual client constraints, and with the group's subject (if any)
836      * set as the executing subject, and this method returns <code>true</code>.
837      *
838      * @param id the activation identifier
839      * @return <code>true</code> if the object was successfully made
840      * inactive; <code>false</code> otherwise
841      * @throws UnknownObjectException if the object is not known to be
842      * active (it may already be inactive)
843      * @throws ActivationException if an activation error occurs
844      * @throws InactiveGroupException if the group is inactive
845      * @throws RemoteException if the remote call to the activation
846      * monitor fails
847      *
848      * @throws SecurityException if a security manager exists and invoking
849      * its {@link SecurityManager#checkPermission checkPermission} method
850      * with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.inactiveObject")</code>
851      * throws a <code>SecurityException</code> 
852      **/
853     @Override
854     public boolean inactiveObject(final ActivationID id)
855 	throws ActivationException, RemoteException
856     {
857 	SecurityManager security = System.getSecurityManager();
858 	if (security != null) {
859 	    security.checkPermission(inactiveObjectPermission);
860 	}
861 	acquireLock(id);
862 	try {
863 	    ActiveEntry entry;
864 	    synchronized (AbstractActivationGroup.class) {
865 		if (state != ACTIVE) {
866 		    throw new InactiveGroupException("group not active");
867 		}
868 		entry = (ActiveEntry) active.get(id);
869 	    }
870 	    if (entry == null) {
871 		// REMIND: should this be silent?
872 		throw new UnknownObjectException("object not active");
873 	    }
874 
875 	    try {
876 		if (!Activatable.unexportObject(entry.impl, false)) {
877 		    return false;
878 		}
879 	    } catch (NoSuchObjectException allowUnexportedObjects) {
880 	    }
881 
882 	    try {
883 		doAction(new PrivilegedExceptionAction() {
884 		    public Object run() throws Exception {
885 			monitor.inactiveObject(id);
886 			return null;
887 		    }
888 		});
889 	    } catch (UnknownObjectException allowUnregisteredObjects) {
890 	    }
891 
892 	    synchronized (AbstractActivationGroup.class) {
893 		active.remove(id);
894 	    }
895 	} finally {
896 	    releaseLock(id);
897 	    checkInactiveGroup();
898 	}
899 
900 	return true;
901     }
902 
903     /**
904      * Attempts to make the remote object that is associated with the
905      * specified activation identifier, and that was exported through the
906      * specified exporter, inactive. The {@link Exporter#unexport unexport}
907      * method of the specified exporter is called with <code>false</code>
908      * as an argument. If that call returns <code>false</code>, this method
909      * returns <code>false</code>. If that call returns <code>true</code>,
910      * the object is marked inactive in this virtual machine, the
911      * superclass <code>inactiveObject</code> method is called with the
912      * activation identifier, with the <code>ActivationMonitor</code>
913      * constraints (if any) set as contextual client constraints, and with
914      * the group's subject (if any) set as the executing subject, and this
915      * method returns <code>true</code>.
916      *
917      * @param id the activation identifier
918      * @param exporter the exporter to use to unexport the object
919      * @return <code>true</code> if the object was successfully made
920      * inactive; <code>false</code> otherwise
921      * @throws UnknownObjectException if the object is not known to be
922      * active (it may already be inactive)
923      * @throws ActivationException if an activation error occurs
924      * @throws InactiveGroupException if the group is inactive
925      * @throws RemoteException if the remote call to the activation monitor
926      * fails
927      * @throws SecurityException if a security manager exists and invoking
928      * its {@link SecurityManager#checkPermission checkPermission} method
929      * with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.inactiveObject")</code>
930      * throws a <code>SecurityException</code> 
931      **/
932     public boolean inactiveObject(final ActivationID id, Exporter exporter)
933 	throws ActivationException, RemoteException
934     {
935 	SecurityManager security = System.getSecurityManager();
936 	if (security != null) {
937 	    security.checkPermission(inactiveObjectPermission);
938 	}
939 	acquireLock(id);
940 	try {
941 	    ActiveEntry entry;
942 	    synchronized (AbstractActivationGroup.class) {
943 		if (state != ACTIVE) {
944 		    throw new InactiveGroupException("group not active");
945 		}
946 		entry = (ActiveEntry) active.get(id);
947 	    }
948 	    if (entry == null) {
949 		// REMIND: should this be silent?
950 		throw new UnknownObjectException("object not active");
951 	    }
952 
953 	    if (!exporter.unexport(false)) {
954 		return false;
955 	    }
956 
957 	    try {
958 		doAction(new PrivilegedExceptionAction() {
959 		    public Object run() throws Exception {
960 			monitor.inactiveObject(id);
961 			return null;
962 		    }
963 		});
964 	    } catch (UnknownObjectException allowUnregisteredObjects) {
965 	    }
966 
967 	    synchronized (AbstractActivationGroup.class) {
968 		active.remove(id);
969 	    }
970 	} finally {
971 	    releaseLock(id);
972 	    checkInactiveGroup();
973 	}
974 
975 	return true;
976     }
977 
978     /*
979      * Determines if the group has become inactive and if so,
980      * marks it as such, notifies the daemon, and unexports the group.
981      */
982     private static void checkInactiveGroup() {
983 	synchronized (AbstractActivationGroup.class) {
984 	    if (state == ACTIVE) {
985 		if (!active.isEmpty() || !lockedIDs.isEmpty()) {
986 		    return;
987 		}
988 		state = INACTIVE;
989 	    } else if (state == INACTIVE) {
990 		return;
991 	    } else if (state == CREATED) {
992 		state = UNUSED;
993 	    } else {
994 		if (login != null) {
995 		    try {
996 			login.logout();
997 		    } catch (LoginException e) {
998 		    }
999 		    login = null;
1000 		}
1001 		state = UNUSED;
1002 		return;
1003 	    }
1004 	}
1005 	try {
1006 	    doAction(new PrivilegedExceptionAction() {
1007 		public Object run() throws Exception {
1008 		    monitor.inactiveGroup(groupID, incarnation);
1009 		    return null;
1010 		}
1011 	    });
1012 	} catch (Exception ignoreDeactivateFailure) {
1013 	}
1014 	Runnable action = new Runnable() {
1015 	    public void run() {
1016 		long stop = System.currentTimeMillis() + unexportTimeout;
1017 		// allow a failing newInstance call to finish first
1018 		boolean force = false;
1019 		while (!exporter.unexport(force)) {
1020 		    long rem = stop - System.currentTimeMillis();
1021 		    if (rem <= 0) {
1022 			force = true;
1023 		    } else {
1024 			try {
1025 			    Thread.sleep(Math.min(rem, unexportWait));
1026 			} catch (InterruptedException e) {
1027 			}
1028 		    }
1029 		}
1030 		if (login != null) {
1031 		    try {
1032 			login.logout();
1033 		    } catch (LoginException e) {
1034 		    }
1035 		}
1036 	    }
1037 	};
1038 	if (state == UNUSED) {
1039 	    action.run();
1040 	} else {
1041 	    Executor systemThreadPool =
1042 		(Executor) AccessController.doPrivileged(
1043 		    (login == null) ?
1044 		    (PrivilegedAction) new GetThreadPoolAction(false) :
1045 		    new PrivilegedAction() {
1046 			public Object run() {
1047 			    return Subject.doAsPrivileged(
1048 					      login.getSubject(),
1049 					      new GetThreadPoolAction(false),
1050 					      null);
1051 			}
1052 		});
1053 	    systemThreadPool.execute(action, "UnexportGroup");
1054 	}
1055     }
1056 
1057     /**
1058      * Marks the object as active in this virtual machine, and calls the
1059      * superclass <code>activeObject</code> method with the same arguments,
1060      * with the <code>ActivationMonitor</code> constraints (if any) set as
1061      * contextual client constraints, and with the group's subject (if any)
1062      * set as the executing subject. Any <code>RemoteException</code>
1063      * thrown by this call is caught and ignored. If the object is already
1064      * marked as active in this virtual machine, this method simply
1065      * returns.
1066      *
1067      * @param id the activation identifier
1068      * @param impl the active remote object
1069      * @throws UnknownObjectException if no object is registered under
1070      * the specified activation identifier
1071      * @throws ActivationException if an activation error occurs
1072      * @throws InactiveGroupException if the group is inactive
1073      * @throws SecurityException if a security manager exists and invoking
1074      * its {@link SecurityManager#checkPermission checkPermission} method
1075      * with the permission <code>{@link MonitorPermission}("java.rmi.activation.ActivationMonitor.activeObject")</code>
1076      * throws a <code>SecurityException</code> 
1077      **/
1078     @Override
1079     public void activeObject(final ActivationID id, Remote impl)
1080 	throws ActivationException
1081     {
1082 	SecurityManager security = System.getSecurityManager();
1083 	if (security != null) {
1084 	    security.checkPermission(activeObjectPermission);
1085 	}
1086 	final ActiveEntry entry = new ActiveEntry(impl);
1087 	acquireLock(id);
1088 	try {
1089 	    synchronized (AbstractActivationGroup.class) {
1090 		if (state != ACTIVE) {
1091 		    throw new InactiveGroupException("group not active");
1092 		}
1093 		if (active.containsKey(id)) {
1094 		    return;
1095 		}
1096 		active.put(id, entry);
1097 	    }
1098 	    // created new entry, so inform monitor of active object
1099 	    try {
1100 		doAction(new PrivilegedExceptionAction<Object>() {
1101 		    @Override
1102 		    public Object run() throws Exception {
1103 			monitor.activeObject(id, entry.mobj);
1104 			return null;
1105 		    }
1106 		});
1107 	    } catch (RemoteException ignore) {
1108 		// daemon can still find it by calling newInstance
1109 	    }
1110 	} finally {
1111 	    releaseLock(id);
1112 	    checkInactiveGroup();
1113 	}
1114     }
1115 
1116     /**
1117      * Execute the specified action on behalf of the server subject without
1118      * requiring the caller to have doAsPrivileged permission.
1119      */
1120     private static Object doAction(final PrivilegedExceptionAction action)
1121 	throws ActivationException, RemoteException
1122     {
1123 	try {
1124 	    if (login == null) {
1125 		return AccessController.doPrivileged(action);
1126 	    } else {
1127 		return AccessController.doPrivileged(
1128 		    new PrivilegedExceptionAction<Object>() {
1129 			@Override
1130 		        public Object run() throws Exception {
1131 			    try {
1132 				return Subject.doAsPrivileged(
1133 					   login.getSubject(), action, null);
1134 			    } catch (PrivilegedActionException e) {
1135 				throw e.getException();
1136 			    }
1137 			}
1138 		});
1139 	    }
1140 	} catch (PrivilegedActionException e) {
1141 	    Exception ex = e.getException();
1142 	    if (ex instanceof RemoteException) {
1143 		throw (RemoteException) ex;
1144 	    } else if (ex instanceof ActivationException) {
1145 		throw (ActivationException) ex;
1146 	    } else {
1147 		throw new ActivationException("unexpected failure", ex);
1148 	    }
1149 	}
1150     }
1151 
1152     /**
1153      * Entry in table for active object.
1154      */
1155     private static class ActiveEntry {
1156 	final Remote impl;
1157 	final MarshalledObject mobj;
1158 
1159 	ActiveEntry(Remote impl) throws ActivationException {
1160 	    this.impl = impl;
1161 	    try {
1162 		Object proxy ;
1163 		if (impl instanceof ProxyAccessor) {
1164 		    proxy = ((ProxyAccessor) impl).getProxy();
1165 		    if (proxy == null) {
1166 			throw new ActivationException(
1167 			    "ProxyAccessor.getProxy returned null");
1168 		    }
1169 		} else {
1170 		    proxy = impl;
1171 		}
1172 		this.mobj = 
1173                     new MarshalledInstance(proxy).convertToMarshalledObject();
1174 	    } catch (IOException e) {
1175 		throw new ActivationException(
1176 		    "failed to marshal remote object", e);
1177 	    }
1178 	}
1179     }
1180 }