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">•
106 * <th scope="col" align="left" colspan="2"><code>
107 * loginContext</code>
108 * <tr valign="top"> <td> <th scope="row" align="right">
109 * Type: <td> <code>{@link javax.security.auth.login.LoginContext}</code>
110 * <tr valign="top"> <td> <th scope="row" align="right">
111 * Default: <td> <code>null</code>
112 * <tr valign="top"> <td> <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">•
120 * <th scope="col" align="left" colspan="2"><code>
121 * inheritGroupSubject</code>
122 * <tr valign="top"> <td> <th scope="row" align="right">
123 * Type: <td> <code>boolean</code>
124 * <tr valign="top"> <td> <th scope="row" align="right">
125 * Default: <td> <code>false</code>
126 * <tr valign="top"> <td> <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">•
135 * <th scope="col" align="left" colspan="2"><code>
136 * instantiatorExporter</code>
137 * <tr valign="top"> <td> <th scope="row" align="right">
138 * Type: <td> <code>{@link net.jini.export.Exporter}</code>
139 * <tr valign="top"> <td> <th scope="row" align="right">
140 * Default: <td> retains existing JRMP export of instantiator
141 * <tr valign="top"> <td> <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">•
150 * <th scope="col" align="left" colspan="2"><code>
151 * monitorPreparer</code>
152 * <tr valign="top"> <td> <th scope="row" align="right">
153 * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
154 * <tr valign="top"> <td> <th scope="row" align="right">
155 * Default: <td> <code>new {@link
156 * net.jini.security.BasicProxyPreparer}()</code>
157 * <tr valign="top"> <td> <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">•
166 * <th scope="col" align="left" colspan="2"> <code>
167 * systemPreparer</code>
168 * <tr valign="top"> <td> <th scope="row" align="right">
169 * Type: <td> <code>{@link net.jini.security.ProxyPreparer}</code>
170 * <tr valign="top"> <td> <th scope="row" align="right">
171 * Default: <td> <code>new {@link
172 * net.jini.security.BasicProxyPreparer}()</code>
173 * <tr valign="top"> <td> <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">•
182 * <th scope="col" align="left" colspan="2"><code>
183 * unexportTimeout</code>
184 * <tr valign="top"> <td> <th scope="row" align="right">
185 * Type: <td> <code>int</code>
186 * <tr valign="top"> <td> <th scope="row" align="right">
187 * Default: <td> <code>60000</code>
188 * <tr valign="top"> <td> <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">•
198 * <th scope="col" align="left" colspan="2"> <code>
199 * unexportWait</code>
200 * <tr valign="top"> <td> <th scope="row" align="right">
201 * Type: <td> <code>int</code>
202 * <tr valign="top"> <td> <th scope="row" align="right">
203 * Default: <td> <code>10</code>
204 * <tr valign="top"> <td> <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 }