History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/rmi/server/Activation.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/rmi/server/Activation.java+2502−0
@@ -0,0 +1,2502 @@
1+/*
2+ * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.rmi.server;
27+
28+import java.io.ByteArrayOutputStream;
29+import java.io.File;
30+import java.io.FileOutputStream;
31+import java.io.IOException;
32+import java.io.InputStream;
33+import java.io.ObjectInputStream;
34+import java.io.OutputStream;
35+import java.io.PrintStream;
36+import java.io.PrintWriter;
37+import java.io.Serializable;
38+import java.lang.Process;
39+import java.lang.reflect.InvocationTargetException;
40+import java.lang.reflect.Method;
41+import java.net.InetAddress;
42+import java.net.ServerSocket;
43+import java.net.Socket;
44+import java.net.SocketAddress;
45+import java.net.SocketException;
46+import java.nio.channels.Channel;
47+import java.nio.channels.ServerSocketChannel;
48+import java.rmi.AccessException;
49+import java.rmi.AlreadyBoundException;
50+import java.rmi.ConnectException;
51+import java.rmi.ConnectIOException;
52+import java.rmi.MarshalledObject;
53+import java.rmi.NoSuchObjectException;
54+import java.rmi.NotBoundException;
55+import java.rmi.Remote;
56+import java.rmi.RemoteException;
57+import java.rmi.activation.ActivationDesc;
58+import java.rmi.activation.ActivationException;
59+import java.rmi.activation.ActivationGroupDesc;
60+import java.rmi.activation.ActivationGroup;
61+import java.rmi.activation.ActivationGroupID;
62+import java.rmi.activation.ActivationID;
63+import java.rmi.activation.ActivationInstantiator;
64+import java.rmi.activation.ActivationMonitor;
65+import java.rmi.activation.ActivationSystem;
66+import java.rmi.activation.Activator;
67+import java.rmi.activation.UnknownGroupException;
68+import java.rmi.activation.UnknownObjectException;
69+import java.rmi.registry.Registry;
70+import java.rmi.server.ObjID;
71+import java.rmi.server.RMIClassLoader;
72+import java.rmi.server.RMIClientSocketFactory;
73+import java.rmi.server.RMIServerSocketFactory;
74+import java.rmi.server.RemoteObject;
75+import java.rmi.server.RemoteServer;
76+import java.rmi.server.UnicastRemoteObject;
77+import java.security.AccessControlException;
78+import java.security.AccessController;
79+import java.security.AllPermission;
80+import java.security.CodeSource;
81+import java.security.Permission;
82+import java.security.PermissionCollection;
83+import java.security.Permissions;
84+import java.security.Policy;
85+import java.security.PrivilegedAction;
86+import java.security.PrivilegedExceptionAction;
87+import java.security.cert.Certificate;
88+import java.text.MessageFormat;
89+import java.util.ArrayList;
90+import java.util.Arrays;
91+import java.util.Date;
92+import java.util.Enumeration;
93+import java.util.HashMap;
94+import java.util.HashSet;
95+import java.util.Iterator;
96+import java.util.List;
97+import java.util.Map;
98+import java.util.MissingResourceException;
99+import java.util.Properties;
100+import java.util.ResourceBundle;
101+import java.util.Set;
102+import java.util.concurrent.ConcurrentHashMap;
103+import sun.rmi.log.LogHandler;
104+import sun.rmi.log.ReliableLog;
105+import sun.rmi.registry.RegistryImpl;
106+import sun.rmi.runtime.NewThreadAction;
107+import sun.rmi.server.UnicastServerRef;
108+import sun.rmi.transport.LiveRef;
109+import sun.security.action.GetBooleanAction;
110+import sun.security.action.GetIntegerAction;
111+import sun.security.action.GetPropertyAction;
112+import sun.security.provider.PolicyFile;
113+import com.sun.rmi.rmid.ExecPermission;
114+import com.sun.rmi.rmid.ExecOptionPermission;
115+
116+/**
117+ * The Activator facilitates remote object activation. A "faulting"
118+ * remote reference calls the activator's <code>activate</code> method
119+ * to obtain a "live" reference to a activatable remote object. Upon
120+ * receiving a request for activation, the activator looks up the
121+ * activation descriptor for the activation identifier, id, determines
122+ * the group in which the object should be activated and invokes the
123+ * activate method on the object's activation group (described by the
124+ * remote interface <code>ActivationInstantiator</code>). The
125+ * activator initiates the execution of activation groups as
126+ * necessary. For example, if an activation group for a specific group
127+ * identifier is not already executing, the activator will spawn a
128+ * child process for the activation group. <p>
129+ *
130+ * The activator is responsible for monitoring and detecting when
131+ * activation groups fail so that it can remove stale remote references
132+ * from its internal tables. <p>
133+ *
134+ * @author Ann Wollrath
135+ * @since 1.2
136+ */
137+public class Activation implements Serializable {
138+
139+ /** indicate compatibility with JDK 1.2 version of class */
140+ private static final long serialVersionUID = 2921265612698155191L;
141+
142+ private static final byte MAJOR_VERSION = 1;
143+ private static final byte MINOR_VERSION = 0;
144+
145+ /** exec policy object */
146+ private static Object execPolicy;
147+ private static Method execPolicyMethod;
148+ private static boolean debugExec;
149+
150+ /** maps activation id to its respective group id */
151+ private Map<ActivationID,ActivationGroupID> idTable =
152+ new ConcurrentHashMap<>();
153+ /** maps group id to its GroupEntry groups */
154+ private Map<ActivationGroupID,GroupEntry> groupTable =
155+ new ConcurrentHashMap<>();
156+
157+ private byte majorVersion = MAJOR_VERSION;
158+ private byte minorVersion = MINOR_VERSION;
159+
160+ /** number of simultaneous group exec's */
161+ private transient int groupSemaphore;
162+ /** counter for numbering groups */
163+ private transient int groupCounter;
164+ /** reliable log to hold descriptor table */
165+ private transient ReliableLog log;
166+ /** number of updates since last snapshot */
167+ private transient int numUpdates;
168+
169+ /** the java command */
170+ // accessed by GroupEntry
171+ private transient String[] command;
172+ /** timeout on wait for child process to be created or destroyed */
173+ private static final long groupTimeout =
174+ getInt("sun.rmi.activation.groupTimeout", 60000);
175+ /** take snapshot after this many updates */
176+ private static final int snapshotInterval =
177+ getInt("sun.rmi.activation.snapshotInterval", 200);
178+ /** timeout on wait for child process to be created */
179+ private static final long execTimeout =
180+ getInt("sun.rmi.activation.execTimeout", 30000);
181+
182+ private static final Object initLock = new Object();
183+ private static boolean initDone = false;
184+
185+ // this should be a *private* method since it is privileged
186+ private static int getInt(String name, int def) {
187+ return AccessController.doPrivileged(new GetIntegerAction(name, def));
188+ }
189+
190+ private transient Activator activator;
191+ private transient Activator activatorStub;
192+ private transient ActivationSystem system;
193+ private transient ActivationSystem systemStub;
194+ private transient ActivationMonitor monitor;
195+ private transient Registry registry;
196+ private transient volatile boolean shuttingDown = false;
197+ private transient volatile Object startupLock;
198+ private transient Thread shutdownHook;
199+
200+ private static ResourceBundle resources = null;
201+
202+ /**
203+ * Create an uninitialized instance of Activation that can be
204+ * populated with log data. This is only called when the initial
205+ * snapshot is taken during the first incarnation of rmid.
206+ */
207+ private Activation() {}
208+
209+ /**
210+ * Recover activation state from the reliable log and initialize
211+ * activation services.
212+ */
213+ private static void startActivation(int port,
214+ RMIServerSocketFactory ssf,
215+ String logName,
216+ String[] childArgs)
217+ throws Exception
218+ {
219+ ReliableLog log = new ReliableLog(logName, new ActLogHandler());
220+ Activation state = (Activation) log.recover();
221+ state.init(port, ssf, log, childArgs);
222+ }
223+
224+ /**
225+ * Initialize the Activation instantiation; start activation
226+ * services.
227+ */
228+ private void init(int port,
229+ RMIServerSocketFactory ssf,
230+ ReliableLog log,
231+ String[] childArgs)
232+ throws Exception
233+ {
234+ // initialize
235+ this.log = log;
236+ numUpdates = 0;
237+ shutdownHook = new ShutdownHook();
238+ groupSemaphore = getInt("sun.rmi.activation.groupThrottle", 3);
239+ groupCounter = 0;
240+ Runtime.getRuntime().addShutdownHook(shutdownHook);
241+
242+ // Use array size of 0, since the value from calling size()
243+ // may be out of date by the time toArray() is called.
244+ ActivationGroupID[] gids =
245+ groupTable.keySet().toArray(new ActivationGroupID[0]);
246+
247+ synchronized (startupLock = new Object()) {
248+ // all the remote methods briefly synchronize on startupLock
249+ // (via checkShutdown) to make sure they don't happen in the
250+ // middle of this block. This block must not cause any such
251+ // incoming remote calls to happen, or deadlock would result!
252+ activator = new ActivatorImpl(port, ssf);
253+ activatorStub = (Activator) RemoteObject.toStub(activator);
254+ system = new ActivationSystemImpl(port, ssf);
255+ systemStub = (ActivationSystem) RemoteObject.toStub(system);
256+ monitor = new ActivationMonitorImpl(port, ssf);
257+ initCommand(childArgs);
258+ registry = new SystemRegistryImpl(port, null, ssf, systemStub);
259+
260+ if (ssf != null) {
261+ synchronized (initLock) {
262+ initDone = true;
263+ initLock.notifyAll();
264+ }
265+ }
266+ }
267+ startupLock = null;
268+
269+ // restart services
270+ for (int i = gids.length; --i >= 0; ) {
271+ try {
272+ getGroupEntry(gids[i]).restartServices();
273+ } catch (UnknownGroupException e) {
274+ System.err.println(
275+ getTextResource("rmid.restart.group.warning"));
276+ e.printStackTrace();
277+ }
278+ }
279+ }
280+
281+ /**
282+ * Previous versions used HashMap instead of ConcurrentHashMap.
283+ * Replace any HashMaps found during deserialization with
284+ * ConcurrentHashMaps.
285+ */
286+ private void readObject(ObjectInputStream ois)
287+ throws IOException, ClassNotFoundException
288+ {
289+ ois.defaultReadObject();
290+ if (! (groupTable instanceof ConcurrentHashMap)) {
291+ groupTable = new ConcurrentHashMap<>(groupTable);
292+ }
293+ if (! (idTable instanceof ConcurrentHashMap)) {
294+ idTable = new ConcurrentHashMap<>(idTable);
295+ }
296+ }
297+
298+ private static class SystemRegistryImpl extends RegistryImpl {
299+
300+ private static final String NAME = ActivationSystem.class.getName();
301+ private final ActivationSystem systemStub;
302+
303+ SystemRegistryImpl(int port,
304+ RMIClientSocketFactory csf,
305+ RMIServerSocketFactory ssf,
306+ ActivationSystem systemStub)
307+ throws RemoteException
308+ {
309+ super(port, csf, ssf);
310+ this.systemStub = systemStub;
311+ }
312+
313+ /**
314+ * Returns the activation system stub if the specified name
315+ * matches the activation system's class name, otherwise
316+ * returns the result of invoking super.lookup with the specified
317+ * name.
318+ */
319+ public Remote lookup(String name)
320+ throws RemoteException, NotBoundException
321+ {
322+ if (name.equals(NAME)) {
323+ return systemStub;
324+ } else {
325+ return super.lookup(name);
326+ }
327+ }
328+
329+ public String[] list() throws RemoteException {
330+ String[] list1 = super.list();
331+ int length = list1.length;
332+ String[] list2 = new String[length + 1];
333+ if (length > 0) {
334+ System.arraycopy(list1, 0, list2, 0, length);
335+ }
336+ list2[length] = NAME;
337+ return list2;
338+ }
339+
340+ public void bind(String name, Remote obj)
341+ throws RemoteException, AlreadyBoundException, AccessException
342+ {
343+ if (name.equals(NAME)) {
344+ throw new AccessException(
345+ "binding ActivationSystem is disallowed");
346+ } else {
347+ super.bind(name, obj);
348+ }
349+ }
350+
351+ public void unbind(String name)
352+ throws RemoteException, NotBoundException, AccessException
353+ {
354+ if (name.equals(NAME)) {
355+ throw new AccessException(
356+ "unbinding ActivationSystem is disallowed");
357+ } else {
358+ super.unbind(name);
359+ }
360+ }
361+
362+
363+ public void rebind(String name, Remote obj)
364+ throws RemoteException, AccessException
365+ {
366+ if (name.equals(NAME)) {
367+ throw new AccessException(
368+ "binding ActivationSystem is disallowed");
369+ } else {
370+ super.rebind(name, obj);
371+ }
372+ }
373+ }
374+
375+
376+ class ActivatorImpl extends RemoteServer implements Activator {
377+ // Because ActivatorImpl has a fixed ObjID, it can be
378+ // called by clients holding stale remote references. Each of
379+ // its remote methods, then, must check startupLock (calling
380+ // checkShutdown() is easiest).
381+
382+ private static final long serialVersionUID = -3654244726254566136L;
383+
384+ /**
385+ * Construct a new Activator on a specified port.
386+ */
387+ ActivatorImpl(int port, RMIServerSocketFactory ssf)
388+ throws RemoteException
389+ {
390+ /* Server ref must be created and assigned before remote object
391+ * 'this' can be exported.
392+ */
393+ LiveRef lref =
394+ new LiveRef(new ObjID(ObjID.ACTIVATOR_ID), port, null, ssf);
395+ UnicastServerRef uref = new UnicastServerRef(lref);
396+ ref = uref;
397+ uref.exportObject(this, null, false);
398+ }
399+
400+ public MarshalledObject<? extends Remote> activate(ActivationID id,
401+ boolean force)
402+ throws ActivationException, UnknownObjectException, RemoteException
403+ {
404+ checkShutdown();
405+ return getGroupEntry(id).activate(id, force);
406+ }
407+ }
408+
409+ class ActivationMonitorImpl extends UnicastRemoteObject
410+ implements ActivationMonitor
411+ {
412+ private static final long serialVersionUID = -6214940464757948867L;
413+
414+ ActivationMonitorImpl(int port, RMIServerSocketFactory ssf)
415+ throws RemoteException
416+ {
417+ super(port, null, ssf);
418+ }
419+
420+ public void inactiveObject(ActivationID id)
421+ throws UnknownObjectException, RemoteException
422+ {
423+ try {
424+ checkShutdown();
425+ } catch (ActivationException e) {
426+ return;
427+ }
428+ RegistryImpl.checkAccess("Activator.inactiveObject");
429+ getGroupEntry(id).inactiveObject(id);
430+ }
431+
432+ public void activeObject(ActivationID id,
433+ MarshalledObject<? extends Remote> mobj)
434+ throws UnknownObjectException, RemoteException
435+ {
436+ try {
437+ checkShutdown();
438+ } catch (ActivationException e) {
439+ return;
440+ }
441+ RegistryImpl.checkAccess("ActivationSystem.activeObject");
442+ getGroupEntry(id).activeObject(id, mobj);
443+ }
444+
445+ public void inactiveGroup(ActivationGroupID id,
446+ long incarnation)
447+ throws UnknownGroupException, RemoteException
448+ {
449+ try {
450+ checkShutdown();
451+ } catch (ActivationException e) {
452+ return;
453+ }
454+ RegistryImpl.checkAccess("ActivationMonitor.inactiveGroup");
455+ getGroupEntry(id).inactiveGroup(incarnation, false);
456+ }
457+ }
458+
459+
460+ class ActivationSystemImpl
461+ extends RemoteServer
462+ implements ActivationSystem
463+ {
464+ private static final long serialVersionUID = 9100152600327688967L;
465+
466+ // Because ActivationSystemImpl has a fixed ObjID, it can be
467+ // called by clients holding stale remote references. Each of
468+ // its remote methods, then, must check startupLock (calling
469+ // checkShutdown() is easiest).
470+ ActivationSystemImpl(int port, RMIServerSocketFactory ssf)
471+ throws RemoteException
472+ {
473+ /* Server ref must be created and assigned before remote object
474+ * 'this' can be exported.
475+ */
476+ LiveRef lref = new LiveRef(new ObjID(4), port, null, ssf);
477+ UnicastServerRef uref = new UnicastServerRef(lref);
478+ ref = uref;
479+ uref.exportObject(this, null);
480+ }
481+
482+ public ActivationID registerObject(ActivationDesc desc)
483+ throws ActivationException, UnknownGroupException, RemoteException
484+ {
485+ checkShutdown();
486+ RegistryImpl.checkAccess("ActivationSystem.registerObject");
487+
488+ ActivationGroupID groupID = desc.getGroupID();
489+ ActivationID id = new ActivationID(activatorStub);
490+ getGroupEntry(groupID).registerObject(id, desc, true);
491+ return id;
492+ }
493+
494+ public void unregisterObject(ActivationID id)
495+ throws ActivationException, UnknownObjectException, RemoteException
496+ {
497+ checkShutdown();
498+ RegistryImpl.checkAccess("ActivationSystem.unregisterObject");
499+ getGroupEntry(id).unregisterObject(id, true);
500+ }
501+
502+ public ActivationGroupID registerGroup(ActivationGroupDesc desc)
503+ throws ActivationException, RemoteException
504+ {
505+ checkShutdown();
506+ RegistryImpl.checkAccess("ActivationSystem.registerGroup");
507+ checkArgs(desc, null);
508+
509+ ActivationGroupID id = new ActivationGroupID(systemStub);
510+ GroupEntry entry = new GroupEntry(id, desc);
511+ // table insertion must take place before log update
512+ groupTable.put(id, entry);
513+ addLogRecord(new LogRegisterGroup(id, desc));
514+ return id;
515+ }
516+
517+ public ActivationMonitor activeGroup(ActivationGroupID id,
518+ ActivationInstantiator group,
519+ long incarnation)
520+ throws ActivationException, UnknownGroupException, RemoteException
521+ {
522+ checkShutdown();
523+ RegistryImpl.checkAccess("ActivationSystem.activeGroup");
524+
525+ getGroupEntry(id).activeGroup(group, incarnation);
526+ return monitor;
527+ }
528+
529+ public void unregisterGroup(ActivationGroupID id)
530+ throws ActivationException, UnknownGroupException, RemoteException
531+ {
532+ checkShutdown();
533+ RegistryImpl.checkAccess("ActivationSystem.unregisterGroup");
534+
535+ // remove entry before unregister so state is updated before
536+ // logged
537+ removeGroupEntry(id).unregisterGroup(true);
538+ }
539+
540+ public ActivationDesc setActivationDesc(ActivationID id,
541+ ActivationDesc desc)
542+ throws ActivationException, UnknownObjectException, RemoteException
543+ {
544+ checkShutdown();
545+ RegistryImpl.checkAccess("ActivationSystem.setActivationDesc");
546+
547+ if (!getGroupID(id).equals(desc.getGroupID())) {
548+ throw new ActivationException(
549+ "ActivationDesc contains wrong group");
550+ }
551+ return getGroupEntry(id).setActivationDesc(id, desc, true);
552+ }
553+
554+ public ActivationGroupDesc setActivationGroupDesc(ActivationGroupID id,
555+ ActivationGroupDesc desc)
556+ throws ActivationException, UnknownGroupException, RemoteException
557+ {
558+ checkShutdown();
559+ RegistryImpl.checkAccess(
560+ "ActivationSystem.setActivationGroupDesc");
561+
562+ checkArgs(desc, null);
563+ return getGroupEntry(id).setActivationGroupDesc(id, desc, true);
564+ }
565+
566+ public ActivationDesc getActivationDesc(ActivationID id)
567+ throws ActivationException, UnknownObjectException, RemoteException
568+ {
569+ checkShutdown();
570+ RegistryImpl.checkAccess("ActivationSystem.getActivationDesc");
571+
572+ return getGroupEntry(id).getActivationDesc(id);
573+ }
574+
575+ public ActivationGroupDesc getActivationGroupDesc(ActivationGroupID id)
576+ throws ActivationException, UnknownGroupException, RemoteException
577+ {
578+ checkShutdown();
579+ RegistryImpl.checkAccess
580+ ("ActivationSystem.getActivationGroupDesc");
581+
582+ return getGroupEntry(id).desc;
583+ }
584+
585+ /**
586+ * Shutdown the activation system. Destroys all groups spawned by
587+ * the activation daemon and exits the activation daemon.
588+ */
589+ public void shutdown() throws AccessException {
590+ RegistryImpl.checkAccess("ActivationSystem.shutdown");
591+
592+ Object lock = startupLock;
593+ if (lock != null) {
594+ synchronized (lock) {
595+ // nothing
596+ }
597+ }
598+
599+ synchronized (Activation.this) {
600+ if (!shuttingDown) {
601+ shuttingDown = true;
602+ (new Shutdown()).start();
603+ }
604+ }
605+ }
606+ }
607+
608+ private void checkShutdown() throws ActivationException {
609+ // if the startup critical section is running, wait until it
610+ // completes/fails before continuing with the remote call.
611+ Object lock = startupLock;
612+ if (lock != null) {
613+ synchronized (lock) {
614+ // nothing
615+ }
616+ }
617+
618+ if (shuttingDown == true) {
619+ throw new ActivationException(
620+ "activation system shutting down");
621+ }
622+ }
623+
624+ private static void unexport(Remote obj) {
625+ for (;;) {
626+ try {
627+ if (UnicastRemoteObject.unexportObject(obj, false) == true) {
628+ break;
629+ } else {
630+ Thread.sleep(100);
631+ }
632+ } catch (Exception e) {
633+ continue;
634+ }
635+ }
636+ }
637+
638+ /**
639+ * Thread to shutdown rmid.
640+ */
641+ private class Shutdown extends Thread {
642+ Shutdown() {
643+ super("rmid Shutdown");
644+ }
645+
646+ public void run() {
647+ try {
648+ /*
649+ * Unexport activation system services
650+ */
651+ unexport(activator);
652+ unexport(system);
653+
654+ // destroy all child processes (groups)
655+ for (GroupEntry groupEntry : groupTable.values()) {
656+ groupEntry.shutdown();
657+ }
658+
659+ Runtime.getRuntime().removeShutdownHook(shutdownHook);
660+
661+ /*
662+ * Unexport monitor safely since all processes are destroyed.
663+ */
664+ unexport(monitor);
665+
666+ /*
667+ * Close log file, fix for 4243264: rmid shutdown thread
668+ * interferes with remote calls in progress. Make sure
669+ * the log file is only closed when it is impossible for
670+ * its closure to interfere with any pending remote calls.
671+ * We close the log when all objects in the rmid VM are
672+ * unexported.
673+ */
674+ try {
675+ synchronized (log) {
676+ log.close();
677+ }
678+ } catch (IOException e) {
679+ }
680+
681+ } finally {
682+ /*
683+ * Now exit... A System.exit should only be done if
684+ * the RMI activation system daemon was started up
685+ * by the main method below (in which should always
686+ * be the case since the Activation contructor is private).
687+ */
688+ System.err.println(getTextResource("rmid.daemon.shutdown"));
689+ System.exit(0);
690+ }
691+ }
692+ }
693+
694+ /** Thread to destroy children in the event of abnormal termination. */
695+ private class ShutdownHook extends Thread {
696+ ShutdownHook() {
697+ super("rmid ShutdownHook");
698+ }
699+
700+ public void run() {
701+ synchronized (Activation.this) {
702+ shuttingDown = true;
703+ }
704+
705+ // destroy all child processes (groups) quickly
706+ for (GroupEntry groupEntry : groupTable.values()) {
707+ groupEntry.shutdownFast();
708+ }
709+ }
710+ }
711+
712+ /**
713+ * Returns the groupID for a given id of an object in the group.
714+ * Throws UnknownObjectException if the object is not registered.
715+ */
716+ private ActivationGroupID getGroupID(ActivationID id)
717+ throws UnknownObjectException
718+ {
719+ ActivationGroupID groupID = idTable.get(id);
720+ if (groupID != null) {
721+ return groupID;
722+ }
723+ throw new UnknownObjectException("unknown object: " + id);
724+ }
725+
726+ /**
727+ * Returns the group entry for the group id, optionally removing it.
728+ * Throws UnknownGroupException if the group is not registered.
729+ */
730+ private GroupEntry getGroupEntry(ActivationGroupID id, boolean rm)
731+ throws UnknownGroupException
732+ {
733+ if (id.getClass() == ActivationGroupID.class) {
734+ GroupEntry entry;
735+ if (rm) {
736+ entry = groupTable.remove(id);
737+ } else {
738+ entry = groupTable.get(id);
739+ }
740+ if (entry != null && !entry.removed) {
741+ return entry;
742+ }
743+ }
744+ throw new UnknownGroupException("group unknown");
745+ }
746+
747+ /**
748+ * Returns the group entry for the group id. Throws
749+ * UnknownGroupException if the group is not registered.
750+ */
751+ private GroupEntry getGroupEntry(ActivationGroupID id)
752+ throws UnknownGroupException
753+ {
754+ return getGroupEntry(id, false);
755+ }
756+
757+ /**
758+ * Removes and returns the group entry for the group id. Throws
759+ * UnknownGroupException if the group is not registered.
760+ */
761+ private GroupEntry removeGroupEntry(ActivationGroupID id)
762+ throws UnknownGroupException
763+ {
764+ return getGroupEntry(id, true);
765+ }
766+
767+ /**
768+ * Returns the group entry for the object's id. Throws
769+ * UnknownObjectException if the object is not registered or the
770+ * object's group is not registered.
771+ */
772+ private GroupEntry getGroupEntry(ActivationID id)
773+ throws UnknownObjectException
774+ {
775+ ActivationGroupID gid = getGroupID(id);
776+ GroupEntry entry = groupTable.get(gid);
777+ if (entry != null && !entry.removed) {
778+ return entry;
779+ }
780+ throw new UnknownObjectException("object's group removed");
781+ }
782+
783+ /**
784+ * Container for group information: group's descriptor, group's
785+ * instantiator, flag to indicate pending group creation, and
786+ * table of the group's actived objects.
787+ *
788+ * WARNING: GroupEntry objects should not be written into log file
789+ * updates. GroupEntrys are inner classes of Activation and they
790+ * can not be serialized independent of this class. If the
791+ * complete Activation system is written out as a log update, the
792+ * point of having updates is nullified.
793+ */
794+ private class GroupEntry implements Serializable {
795+
796+ /** indicate compatibility with JDK 1.2 version of class */
797+ private static final long serialVersionUID = 7222464070032993304L;
798+ private static final int MAX_TRIES = 2;
799+ private static final int NORMAL = 0;
800+ private static final int CREATING = 1;
801+ private static final int TERMINATE = 2;
802+ private static final int TERMINATING = 3;
803+
804+ ActivationGroupDesc desc = null;
805+ ActivationGroupID groupID = null;
806+ long incarnation = 0;
807+ Map<ActivationID,ObjectEntry> objects =
808+ new HashMap<ActivationID,ObjectEntry>();
809+ Set<ActivationID> restartSet = new HashSet<ActivationID>();
810+
811+ transient ActivationInstantiator group = null;
812+ transient int status = NORMAL;
813+ transient long waitTime = 0;
814+ transient String groupName = null;
815+ transient Process child = null;
816+ transient boolean removed = false;
817+ transient Watchdog watchdog = null;
818+
819+ GroupEntry(ActivationGroupID groupID, ActivationGroupDesc desc) {
820+ this.groupID = groupID;
821+ this.desc = desc;
822+ }
823+
824+ void restartServices() {
825+ Iterator<ActivationID> iter = null;
826+
827+ synchronized (this) {
828+ if (restartSet.isEmpty()) {
829+ return;
830+ }
831+
832+ /*
833+ * Clone the restartSet so the set does not have to be locked
834+ * during iteration. Locking the restartSet could cause
835+ * deadlock if an object we are restarting caused another
836+ * object in this group to be activated.
837+ */
838+ iter = (new HashSet<ActivationID>(restartSet)).iterator();
839+ }
840+
841+ while (iter.hasNext()) {
842+ ActivationID id = iter.next();
843+ try {
844+ activate(id, true);
845+ } catch (Exception e) {
846+ if (shuttingDown) {
847+ return;
848+ }
849+ System.err.println(
850+ getTextResource("rmid.restart.service.warning"));
851+ e.printStackTrace();
852+ }
853+ }
854+ }
855+
856+ synchronized void activeGroup(ActivationInstantiator inst,
857+ long instIncarnation)
858+ throws ActivationException, UnknownGroupException
859+ {
860+ if (incarnation != instIncarnation) {
861+ throw new ActivationException("invalid incarnation");
862+ }
863+
864+ if (group != null) {
865+ if (group.equals(inst)) {
866+ return;
867+ } else {
868+ throw new ActivationException("group already active");
869+ }
870+ }
871+
872+ if (child != null && status != CREATING) {
873+ throw new ActivationException("group not being created");
874+ }
875+
876+ group = inst;
877+ status = NORMAL;
878+ notifyAll();
879+ }
880+
881+ private void checkRemoved() throws UnknownGroupException {
882+ if (removed) {
883+ throw new UnknownGroupException("group removed");
884+ }
885+ }
886+
887+ private ObjectEntry getObjectEntry(ActivationID id)
888+ throws UnknownObjectException
889+ {
890+ if (removed) {
891+ throw new UnknownObjectException("object's group removed");
892+ }
893+ ObjectEntry objEntry = objects.get(id);
894+ if (objEntry == null) {
895+ throw new UnknownObjectException("object unknown");
896+ }
897+ return objEntry;
898+ }
899+
900+ synchronized void registerObject(ActivationID id,
901+ ActivationDesc desc,
902+ boolean addRecord)
903+ throws UnknownGroupException, ActivationException
904+ {
905+ checkRemoved();
906+ objects.put(id, new ObjectEntry(desc));
907+ if (desc.getRestartMode() == true) {
908+ restartSet.add(id);
909+ }
910+
911+ // table insertion must take place before log update
912+ idTable.put(id, groupID);
913+
914+ if (addRecord) {
915+ addLogRecord(new LogRegisterObject(id, desc));
916+ }
917+ }
918+
919+ synchronized void unregisterObject(ActivationID id, boolean addRecord)
920+ throws UnknownGroupException, ActivationException
921+ {
922+ ObjectEntry objEntry = getObjectEntry(id);
923+ objEntry.removed = true;
924+ objects.remove(id);
925+ if (objEntry.desc.getRestartMode() == true) {
926+ restartSet.remove(id);
927+ }
928+
929+ // table removal must take place before log update
930+ idTable.remove(id);
931+ if (addRecord) {
932+ addLogRecord(new LogUnregisterObject(id));
933+ }
934+ }
935+
936+ synchronized void unregisterGroup(boolean addRecord)
937+ throws UnknownGroupException, ActivationException
938+ {
939+ checkRemoved();
940+ removed = true;
941+ for (Map.Entry<ActivationID,ObjectEntry> entry :
942+ objects.entrySet())
943+ {
944+ ActivationID id = entry.getKey();
945+ idTable.remove(id);
946+ ObjectEntry objEntry = entry.getValue();
947+ objEntry.removed = true;
948+ }
949+ objects.clear();
950+ restartSet.clear();
951+ reset();
952+ childGone();
953+
954+ // removal should be recorded before log update
955+ if (addRecord) {
956+ addLogRecord(new LogUnregisterGroup(groupID));
957+ }
958+ }
959+
960+ synchronized ActivationDesc setActivationDesc(ActivationID id,
961+ ActivationDesc desc,
962+ boolean addRecord)
963+ throws UnknownObjectException, UnknownGroupException,
964+ ActivationException
965+ {
966+ ObjectEntry objEntry = getObjectEntry(id);
967+ ActivationDesc oldDesc = objEntry.desc;
968+ objEntry.desc = desc;
969+ if (desc.getRestartMode() == true) {
970+ restartSet.add(id);
971+ } else {
972+ restartSet.remove(id);
973+ }
974+ // restart information should be recorded before log update
975+ if (addRecord) {
976+ addLogRecord(new LogUpdateDesc(id, desc));
977+ }
978+
979+ return oldDesc;
980+ }
981+
982+ synchronized ActivationDesc getActivationDesc(ActivationID id)
983+ throws UnknownObjectException, UnknownGroupException
984+ {
985+ return getObjectEntry(id).desc;
986+ }
987+
988+ synchronized ActivationGroupDesc setActivationGroupDesc(
989+ ActivationGroupID id,
990+ ActivationGroupDesc desc,
991+ boolean addRecord)
992+ throws UnknownGroupException, ActivationException
993+ {
994+ checkRemoved();
995+ ActivationGroupDesc oldDesc = this.desc;
996+ this.desc = desc;
997+ // state update should occur before log update
998+ if (addRecord) {
999+ addLogRecord(new LogUpdateGroupDesc(id, desc));
1000+ }
1001+ return oldDesc;
1002+ }
1003+
1004+ synchronized void inactiveGroup(long incarnation, boolean failure)
1005+ throws UnknownGroupException
1006+ {
1007+ checkRemoved();
1008+ if (this.incarnation != incarnation) {
1009+ throw new UnknownGroupException("invalid incarnation");
1010+ }
1011+
1012+ reset();
1013+ if (failure) {
1014+ terminate();
1015+ } else if (child != null && status == NORMAL) {
1016+ status = TERMINATE;
1017+ watchdog.noRestart();
1018+ }
1019+ }
1020+
1021+ synchronized void activeObject(ActivationID id,
1022+ MarshalledObject<? extends Remote> mobj)
1023+ throws UnknownObjectException
1024+ {
1025+ getObjectEntry(id).stub = mobj;
1026+ }
1027+
1028+ synchronized void inactiveObject(ActivationID id)
1029+ throws UnknownObjectException
1030+ {
1031+ getObjectEntry(id).reset();
1032+ }
1033+
1034+ private synchronized void reset() {
1035+ group = null;
1036+ for (ObjectEntry objectEntry : objects.values()) {
1037+ objectEntry.reset();
1038+ }
1039+ }
1040+
1041+ private void childGone() {
1042+ if (child != null) {
1043+ child = null;
1044+ watchdog.dispose();
1045+ watchdog = null;
1046+ status = NORMAL;
1047+ notifyAll();
1048+ }
1049+ }
1050+
1051+ private void terminate() {
1052+ if (child != null && status != TERMINATING) {
1053+ child.destroy();
1054+ status = TERMINATING;
1055+ waitTime = System.currentTimeMillis() + groupTimeout;
1056+ notifyAll();
1057+ }
1058+ }
1059+
1060+ private void await() {
1061+ while (true) {
1062+ switch (status) {
1063+ case NORMAL:
1064+ return;
1065+ case TERMINATE:
1066+ terminate();
1067+ case TERMINATING:
1068+ try {
1069+ child.exitValue();
1070+ } catch (IllegalThreadStateException e) {
1071+ long now = System.currentTimeMillis();
1072+ if (waitTime > now) {
1073+ try {
1074+ wait(waitTime - now);
1075+ } catch (InterruptedException ee) {
1076+ }
1077+ continue;
1078+ }
1079+ // REMIND: print message that group did not terminate?
1080+ }
1081+ childGone();
1082+ return;
1083+ case CREATING:
1084+ try {
1085+ wait();
1086+ } catch (InterruptedException e) {
1087+ }
1088+ }
1089+ }
1090+ }
1091+
1092+ // no synchronization to avoid delay wrt getInstantiator
1093+ void shutdownFast() {
1094+ Process p = child;
1095+ if (p != null) {
1096+ p.destroy();
1097+ }
1098+ }
1099+
1100+ synchronized void shutdown() {
1101+ reset();
1102+ terminate();
1103+ await();
1104+ }
1105+
1106+ MarshalledObject<? extends Remote> activate(ActivationID id,
1107+ boolean force)
1108+ throws ActivationException
1109+ {
1110+ Exception detail = null;
1111+
1112+ /*
1113+ * Attempt to activate object and reattempt (several times)
1114+ * if activation fails due to communication problems.
1115+ */
1116+ for (int tries = MAX_TRIES; tries > 0; tries--) {
1117+ ActivationInstantiator inst;
1118+ long currentIncarnation;
1119+
1120+ // look up object to activate
1121+ ObjectEntry objEntry;
1122+ synchronized (this) {
1123+ objEntry = getObjectEntry(id);
1124+ // if not forcing activation, return cached stub
1125+ if (!force && objEntry.stub != null) {
1126+ return objEntry.stub;
1127+ }
1128+ inst = getInstantiator(groupID);
1129+ currentIncarnation = incarnation;
1130+ }
1131+
1132+ boolean groupInactive = false;
1133+ boolean failure = false;
1134+ // activate object
1135+ try {
1136+ return objEntry.activate(id, force, inst);
1137+ } catch (NoSuchObjectException e) {
1138+ groupInactive = true;
1139+ detail = e;
1140+ } catch (ConnectException e) {
1141+ groupInactive = true;
1142+ failure = true;
1143+ detail = e;
1144+ } catch (ConnectIOException e) {
1145+ groupInactive = true;
1146+ failure = true;
1147+ detail = e;
1148+ } catch (InactiveGroupException e) {
1149+ groupInactive = true;
1150+ detail = e;
1151+ } catch (RemoteException e) {
1152+ // REMIND: wait some here before continuing?
1153+ if (detail == null) {
1154+ detail = e;
1155+ }
1156+ }
1157+
1158+ if (groupInactive) {
1159+ // group has failed or is inactive; mark inactive
1160+ try {
1161+ System.err.println(
1162+ MessageFormat.format(
1163+ getTextResource("rmid.group.inactive"),
1164+ detail.toString()));
1165+ detail.printStackTrace();
1166+ getGroupEntry(groupID).
1167+ inactiveGroup(currentIncarnation, failure);
1168+ } catch (UnknownGroupException e) {
1169+ // not a problem
1170+ }
1171+ }
1172+ }
1173+
1174+ /**
1175+ * signal that group activation failed, nested exception
1176+ * specifies what exception occurred when the group did not
1177+ * activate
1178+ */
1179+ throw new ActivationException("object activation failed after " +
1180+ MAX_TRIES + " tries", detail);
1181+ }
1182+
1183+ /**
1184+ * Returns the instantiator for the group specified by id and
1185+ * entry. If the group is currently inactive, exec some
1186+ * bootstrap code to create the group.
1187+ */
1188+ private ActivationInstantiator getInstantiator(ActivationGroupID id)
1189+ throws ActivationException
1190+ {
1191+ assert Thread.holdsLock(this);
1192+
1193+ await();
1194+ if (group != null) {
1195+ return group;
1196+ }
1197+ checkRemoved();
1198+ boolean acquired = false;
1199+
1200+ try {
1201+ groupName = Pstartgroup();
1202+ acquired = true;
1203+ String[] argv = activationArgs(desc);
1204+ checkArgs(desc, argv);
1205+
1206+ if (debugExec) {
1207+ StringBuffer sb = new StringBuffer(argv[0]);
1208+ int j;
1209+ for (j = 1; j < argv.length; j++) {
1210+ sb.append(' ');
1211+ sb.append(argv[j]);
1212+ }
1213+ System.err.println(
1214+ MessageFormat.format(
1215+ getTextResource("rmid.exec.command"),
1216+ sb.toString()));
1217+ }
1218+
1219+ try {
1220+ child = Runtime.getRuntime().exec(argv);
1221+ status = CREATING;
1222+ ++incarnation;
1223+ watchdog = new Watchdog();
1224+ watchdog.start();
1225+ addLogRecord(new LogGroupIncarnation(id, incarnation));
1226+
1227+ // handle child I/O streams before writing to child
1228+ PipeWriter.plugTogetherPair
1229+ (child.getInputStream(), System.out,
1230+ child.getErrorStream(), System.err);
1231+
1232+ MarshalOutputStream out =
1233+ new MarshalOutputStream(child.getOutputStream());
1234+ out.writeObject(id);
1235+ out.writeObject(desc);
1236+ out.writeLong(incarnation);
1237+ out.flush();
1238+ out.close();
1239+
1240+
1241+ } catch (IOException e) {
1242+ terminate();
1243+ throw new ActivationException(
1244+ "unable to create activation group", e);
1245+ }
1246+
1247+ try {
1248+ long now = System.currentTimeMillis();
1249+ long stop = now + execTimeout;
1250+ do {
1251+ wait(stop - now);
1252+ if (group != null) {
1253+ return group;
1254+ }
1255+ now = System.currentTimeMillis();
1256+ } while (status == CREATING && now < stop);
1257+ } catch (InterruptedException e) {
1258+ }
1259+
1260+ terminate();
1261+ throw new ActivationException(
1262+ (removed ?
1263+ "activation group unregistered" :
1264+ "timeout creating child process"));
1265+ } finally {
1266+ if (acquired) {
1267+ Vstartgroup();
1268+ }
1269+ }
1270+ }
1271+
1272+ /**
1273+ * Waits for process termination and then restarts services.
1274+ */
1275+ private class Watchdog extends Thread {
1276+ private final Process groupProcess = child;
1277+ private final long groupIncarnation = incarnation;
1278+ private boolean canInterrupt = true;
1279+ private boolean shouldQuit = false;
1280+ private boolean shouldRestart = true;
1281+
1282+ Watchdog() {
1283+ super("WatchDog-" + groupName + "-" + incarnation);
1284+ setDaemon(true);
1285+ }
1286+
1287+ public void run() {
1288+
1289+ if (shouldQuit) {
1290+ return;
1291+ }
1292+
1293+ /*
1294+ * Wait for the group to crash or exit.
1295+ */
1296+ try {
1297+ groupProcess.waitFor();
1298+ } catch (InterruptedException exit) {
1299+ return;
1300+ }
1301+
1302+ boolean restart = false;
1303+ synchronized (GroupEntry.this) {
1304+ if (shouldQuit) {
1305+ return;
1306+ }
1307+ canInterrupt = false;
1308+ interrupted(); // clear interrupt bit
1309+ /*
1310+ * Since the group crashed, we should
1311+ * reset the entry before activating objects
1312+ */
1313+ if (groupIncarnation == incarnation) {
1314+ restart = shouldRestart && !shuttingDown;
1315+ reset();
1316+ childGone();
1317+ }
1318+ }
1319+
1320+ /*
1321+ * Activate those objects that require restarting
1322+ * after a crash.
1323+ */
1324+ if (restart) {
1325+ restartServices();
1326+ }
1327+ }
1328+
1329+ /**
1330+ * Marks this thread as one that is no longer needed.
1331+ * If the thread is in a state in which it can be interrupted,
1332+ * then the thread is interrupted.
1333+ */
1334+ void dispose() {
1335+ shouldQuit = true;
1336+ if (canInterrupt) {
1337+ interrupt();
1338+ }
1339+ }
1340+
1341+ /**
1342+ * Marks this thread as no longer needing to restart objects.
1343+ */
1344+ void noRestart() {
1345+ shouldRestart = false;
1346+ }
1347+ }
1348+ }
1349+
1350+ private String[] activationArgs(ActivationGroupDesc desc) {
1351+ ActivationGroupDesc.CommandEnvironment cmdenv;
1352+ cmdenv = desc.getCommandEnvironment();
1353+
1354+ // argv is the literal command to exec
1355+ List<String> argv = new ArrayList<String>();
1356+
1357+ // Command name/path
1358+ argv.add((cmdenv != null && cmdenv.getCommandPath() != null)
1359+ ? cmdenv.getCommandPath()
1360+ : command[0]);
1361+
1362+ // Group-specific command options
1363+ if (cmdenv != null && cmdenv.getCommandOptions() != null) {
1364+ argv.addAll(Arrays.asList(cmdenv.getCommandOptions()));
1365+ }
1366+
1367+ // Properties become -D parameters
1368+ Properties props = desc.getPropertyOverrides();
1369+ if (props != null) {
1370+ for (Enumeration<?> p = props.propertyNames();
1371+ p.hasMoreElements();)
1372+ {
1373+ String name = (String) p.nextElement();
1374+ /* Note on quoting: it would be wrong
1375+ * here, since argv will be passed to
1376+ * Runtime.exec, which should not parse
1377+ * arguments or split on whitespace.
1378+ */
1379+ argv.add("-D" + name + "=" + props.getProperty(name));
1380+ }
1381+ }
1382+
1383+ /* Finally, rmid-global command options (e.g. -C options)
1384+ * and the classname
1385+ */
1386+ for (int i = 1; i < command.length; i++) {
1387+ argv.add(command[i]);
1388+ }
1389+
1390+ String[] realArgv = new String[argv.size()];
1391+ System.arraycopy(argv.toArray(), 0, realArgv, 0, realArgv.length);
1392+
1393+ return realArgv;
1394+ }
1395+
1396+ private void checkArgs(ActivationGroupDesc desc, String[] cmd)
1397+ throws SecurityException, ActivationException
1398+ {
1399+ /*
1400+ * Check exec command using execPolicy object
1401+ */
1402+ if (execPolicyMethod != null) {
1403+ if (cmd == null) {
1404+ cmd = activationArgs(desc);
1405+ }
1406+ try {
1407+ execPolicyMethod.invoke(execPolicy, desc, cmd);
1408+ } catch (InvocationTargetException e) {
1409+ Throwable targetException = e.getTargetException();
1410+ if (targetException instanceof SecurityException) {
1411+ throw (SecurityException) targetException;
1412+ } else {
1413+ throw new ActivationException(
1414+ execPolicyMethod.getName() + ": unexpected exception",
1415+ e);
1416+ }
1417+ } catch (Exception e) {
1418+ throw new ActivationException(
1419+ execPolicyMethod.getName() + ": unexpected exception", e);
1420+ }
1421+ }
1422+ }
1423+
1424+ private static class ObjectEntry implements Serializable {
1425+
1426+ private static final long serialVersionUID = -5500114225321357856L;
1427+
1428+ /** descriptor for object */
1429+ ActivationDesc desc;
1430+ /** the stub (if active) */
1431+ volatile transient MarshalledObject<? extends Remote> stub = null;
1432+ volatile transient boolean removed = false;
1433+
1434+ ObjectEntry(ActivationDesc desc) {
1435+ this.desc = desc;
1436+ }
1437+
1438+ synchronized MarshalledObject<? extends Remote>
1439+ activate(ActivationID id,
1440+ boolean force,
1441+ ActivationInstantiator inst)
1442+ throws RemoteException, ActivationException
1443+ {
1444+ MarshalledObject<? extends Remote> nstub = stub;
1445+ if (removed) {
1446+ throw new UnknownObjectException("object removed");
1447+ } else if (!force && nstub != null) {
1448+ return nstub;
1449+ }
1450+
1451+ nstub = inst.newInstance(id, desc);
1452+ stub = nstub;
1453+ /*
1454+ * stub could be set to null by a group reset, so return
1455+ * the newstub here to prevent returning null.
1456+ */
1457+ return nstub;
1458+ }
1459+
1460+ void reset() {
1461+ stub = null;
1462+ }
1463+ }
1464+
1465+ /**
1466+ * Add a record to the activation log. If the number of updates
1467+ * passes a predetermined threshold, record a snapshot.
1468+ */
1469+ private void addLogRecord(LogRecord rec) throws ActivationException {
1470+ synchronized (log) {
1471+ checkShutdown();
1472+ try {
1473+ log.update(rec, true);
1474+ } catch (Exception e) {
1475+ numUpdates = snapshotInterval;
1476+ System.err.println(getTextResource("rmid.log.update.warning"));
1477+ e.printStackTrace();
1478+ }
1479+ if (++numUpdates < snapshotInterval) {
1480+ return;
1481+ }
1482+ try {
1483+ log.snapshot(this);
1484+ numUpdates = 0;
1485+ } catch (Exception e) {
1486+ System.err.println(
1487+ getTextResource("rmid.log.snapshot.warning"));
1488+ e.printStackTrace();
1489+ try {
1490+ // shutdown activation system because snapshot failed
1491+ system.shutdown();
1492+ } catch (RemoteException ignore) {
1493+ // can't happen
1494+ }
1495+ // warn the client of the original update problem
1496+ throw new ActivationException("log snapshot failed", e);
1497+ }
1498+ }
1499+ }
1500+
1501+ /**
1502+ * Handler for the log that knows how to take the initial snapshot
1503+ * and apply an update (a LogRecord) to the current state.
1504+ */
1505+ private static class ActLogHandler extends LogHandler {
1506+
1507+ ActLogHandler() {
1508+ }
1509+
1510+ public Object initialSnapshot()
1511+ {
1512+ /**
1513+ * Return an empty Activation object. Log will update
1514+ * this object with recovered state.
1515+ */
1516+ return new Activation();
1517+ }
1518+
1519+ public Object applyUpdate(Object update, Object state)
1520+ throws Exception
1521+ {
1522+ return ((LogRecord) update).apply(state);
1523+ }
1524+
1525+ }
1526+
1527+ /**
1528+ * Abstract class for all log records. The subclass contains
1529+ * specific update information and implements the apply method
1530+ * that applys the update information contained in the record
1531+ * to the current state.
1532+ */
1533+ private static abstract class LogRecord implements Serializable {
1534+ /** indicate compatibility with JDK 1.2 version of class */
1535+ private static final long serialVersionUID = 8395140512322687529L;
1536+ abstract Object apply(Object state) throws Exception;
1537+ }
1538+
1539+ /**
1540+ * Log record for registering an object.
1541+ */
1542+ private static class LogRegisterObject extends LogRecord {
1543+ /** indicate compatibility with JDK 1.2 version of class */
1544+ private static final long serialVersionUID = -6280336276146085143L;
1545+ private ActivationID id;
1546+ private ActivationDesc desc;
1547+
1548+ LogRegisterObject(ActivationID id, ActivationDesc desc) {
1549+ this.id = id;
1550+ this.desc = desc;
1551+ }
1552+
1553+ Object apply(Object state) {
1554+ try {
1555+ ((Activation) state).getGroupEntry(desc.getGroupID()).
1556+ registerObject(id, desc, false);
1557+ } catch (Exception ignore) {
1558+ System.err.println(
1559+ MessageFormat.format(
1560+ getTextResource("rmid.log.recover.warning"),
1561+ "LogRegisterObject"));
1562+ ignore.printStackTrace();
1563+ }
1564+ return state;
1565+ }
1566+ }
1567+
1568+ /**
1569+ * Log record for unregistering an object.
1570+ */
1571+ private static class LogUnregisterObject extends LogRecord {
1572+ /** indicate compatibility with JDK 1.2 version of class */
1573+ private static final long serialVersionUID = 6269824097396935501L;
1574+ private ActivationID id;
1575+
1576+ LogUnregisterObject(ActivationID id) {
1577+ this.id = id;
1578+ }
1579+
1580+ Object apply(Object state) {
1581+ try {
1582+ ((Activation) state).getGroupEntry(id).
1583+ unregisterObject(id, false);
1584+ } catch (Exception ignore) {
1585+ System.err.println(
1586+ MessageFormat.format(
1587+ getTextResource("rmid.log.recover.warning"),
1588+ "LogUnregisterObject"));
1589+ ignore.printStackTrace();
1590+ }
1591+ return state;
1592+ }
1593+ }
1594+
1595+ /**
1596+ * Log record for registering a group.
1597+ */
1598+ private static class LogRegisterGroup extends LogRecord {
1599+ /** indicate compatibility with JDK 1.2 version of class */
1600+ private static final long serialVersionUID = -1966827458515403625L;
1601+ private ActivationGroupID id;
1602+ private ActivationGroupDesc desc;
1603+
1604+ LogRegisterGroup(ActivationGroupID id, ActivationGroupDesc desc) {
1605+ this.id = id;
1606+ this.desc = desc;
1607+ }
1608+
1609+ Object apply(Object state) {
1610+ // modify state directly; cant ask a nonexistent GroupEntry
1611+ // to register itself.
1612+ ((Activation) state).groupTable.put(id, ((Activation) state).new
1613+ GroupEntry(id, desc));
1614+ return state;
1615+ }
1616+ }
1617+
1618+ /**
1619+ * Log record for udpating an activation desc
1620+ */
1621+ private static class LogUpdateDesc extends LogRecord {
1622+ /** indicate compatibility with JDK 1.2 version of class */
1623+ private static final long serialVersionUID = 545511539051179885L;
1624+
1625+ private ActivationID id;
1626+ private ActivationDesc desc;
1627+
1628+ LogUpdateDesc(ActivationID id, ActivationDesc desc) {
1629+ this.id = id;
1630+ this.desc = desc;
1631+ }
1632+
1633+ Object apply(Object state) {
1634+ try {
1635+ ((Activation) state).getGroupEntry(id).
1636+ setActivationDesc(id, desc, false);
1637+ } catch (Exception ignore) {
1638+ System.err.println(
1639+ MessageFormat.format(
1640+ getTextResource("rmid.log.recover.warning"),
1641+ "LogUpdateDesc"));
1642+ ignore.printStackTrace();
1643+ }
1644+ return state;
1645+ }
1646+ }
1647+
1648+ /**
1649+ * Log record for unregistering a group.
1650+ */
1651+ private static class LogUpdateGroupDesc extends LogRecord {
1652+ /** indicate compatibility with JDK 1.2 version of class */
1653+ private static final long serialVersionUID = -1271300989218424337L;
1654+ private ActivationGroupID id;
1655+ private ActivationGroupDesc desc;
1656+
1657+ LogUpdateGroupDesc(ActivationGroupID id, ActivationGroupDesc desc) {
1658+ this.id = id;
1659+ this.desc = desc;
1660+ }
1661+
1662+ Object apply(Object state) {
1663+ try {
1664+ ((Activation) state).getGroupEntry(id).
1665+ setActivationGroupDesc(id, desc, false);
1666+ } catch (Exception ignore) {
1667+ System.err.println(
1668+ MessageFormat.format(
1669+ getTextResource("rmid.log.recover.warning"),
1670+ "LogUpdateGroupDesc"));
1671+ ignore.printStackTrace();
1672+ }
1673+ return state;
1674+ }
1675+ }
1676+
1677+ /**
1678+ * Log record for unregistering a group.
1679+ */
1680+ private static class LogUnregisterGroup extends LogRecord {
1681+ /** indicate compatibility with JDK 1.2 version of class */
1682+ private static final long serialVersionUID = -3356306586522147344L;
1683+ private ActivationGroupID id;
1684+
1685+ LogUnregisterGroup(ActivationGroupID id) {
1686+ this.id = id;
1687+ }
1688+
1689+ Object apply(Object state) {
1690+ GroupEntry entry = ((Activation) state).groupTable.remove(id);
1691+ try {
1692+ entry.unregisterGroup(false);
1693+ } catch (Exception ignore) {
1694+ System.err.println(
1695+ MessageFormat.format(
1696+ getTextResource("rmid.log.recover.warning"),
1697+ "LogUnregisterGroup"));
1698+ ignore.printStackTrace();
1699+ }
1700+ return state;
1701+ }
1702+ }
1703+
1704+ /**
1705+ * Log record for an active group incarnation
1706+ */
1707+ private static class LogGroupIncarnation extends LogRecord {
1708+ /** indicate compatibility with JDK 1.2 version of class */
1709+ private static final long serialVersionUID = 4146872747377631897L;
1710+ private ActivationGroupID id;
1711+ private long inc;
1712+
1713+ LogGroupIncarnation(ActivationGroupID id, long inc) {
1714+ this.id = id;
1715+ this.inc = inc;
1716+ }
1717+
1718+ Object apply(Object state) {
1719+ try {
1720+ GroupEntry entry = ((Activation) state).getGroupEntry(id);
1721+ entry.incarnation = inc;
1722+ } catch (Exception ignore) {
1723+ System.err.println(
1724+ MessageFormat.format(
1725+ getTextResource("rmid.log.recover.warning"),
1726+ "LogGroupIncarnation"));
1727+ ignore.printStackTrace();
1728+ }
1729+ return state;
1730+ }
1731+ }
1732+
1733+ /**
1734+ * Initialize command to exec a default group.
1735+ */
1736+ private void initCommand(String[] childArgs) {
1737+ command = new String[childArgs.length + 2];
1738+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
1739+ public Void run() {
1740+ try {
1741+ command[0] = System.getProperty("java.home") +
1742+ File.separator + "bin" + File.separator + "java";
1743+ } catch (Exception e) {
1744+ System.err.println(
1745+ getTextResource("rmid.unfound.java.home.property"));
1746+ command[0] = "java";
1747+ }
1748+ return null;
1749+ }
1750+ });
1751+ System.arraycopy(childArgs, 0, command, 1, childArgs.length);
1752+ command[command.length-1] = "sun.rmi.server.ActivationGroupInit";
1753+ }
1754+
1755+ private static void bomb(String error) {
1756+ System.err.println("rmid: " + error); // $NON-NLS$
1757+ System.err.println(MessageFormat.format(getTextResource("rmid.usage"),
1758+ "rmid"));
1759+ System.exit(1);
1760+ }
1761+
1762+ /**
1763+ * The default policy for checking a command before it is executed
1764+ * makes sure the appropriate com.sun.rmi.rmid.ExecPermission and
1765+ * set of com.sun.rmi.rmid.ExecOptionPermissions have been granted.
1766+ */
1767+ public static class DefaultExecPolicy {
1768+
1769+ public void checkExecCommand(ActivationGroupDesc desc, String[] cmd)
1770+ throws SecurityException
1771+ {
1772+ PermissionCollection perms = getExecPermissions();
1773+
1774+ /*
1775+ * Check properties overrides.
1776+ */
1777+ Properties props = desc.getPropertyOverrides();
1778+ if (props != null) {
1779+ Enumeration<?> p = props.propertyNames();
1780+ while (p.hasMoreElements()) {
1781+ String name = (String) p.nextElement();
1782+ String value = props.getProperty(name);
1783+ String option = "-D" + name + "=" + value;
1784+ try {
1785+ checkPermission(perms,
1786+ new ExecOptionPermission(option));
1787+ } catch (AccessControlException e) {
1788+ if (value.equals("")) {
1789+ checkPermission(perms,
1790+ new ExecOptionPermission("-D" + name));
1791+ } else {
1792+ throw e;
1793+ }
1794+ }
1795+ }
1796+ }
1797+
1798+ /*
1799+ * Check group class name (allow nothing but the default),
1800+ * code location (must be null), and data (must be null).
1801+ */
1802+ String groupClassName = desc.getClassName();
1803+ if ((groupClassName != null &&
1804+ !groupClassName.equals(
1805+ ActivationGroupImpl.class.getName())) ||
1806+ (desc.getLocation() != null) ||
1807+ (desc.getData() != null))
1808+ {
1809+ throw new AccessControlException(
1810+ "access denied (custom group implementation not allowed)");
1811+ }
1812+
1813+ /*
1814+ * If group descriptor has a command environment, check
1815+ * command and options.
1816+ */
1817+ ActivationGroupDesc.CommandEnvironment cmdenv;
1818+ cmdenv = desc.getCommandEnvironment();
1819+ if (cmdenv != null) {
1820+ String path = cmdenv.getCommandPath();
1821+ if (path != null) {
1822+ checkPermission(perms, new ExecPermission(path));
1823+ }
1824+
1825+ String[] options = cmdenv.getCommandOptions();
1826+ if (options != null) {
1827+ for (String option : options) {
1828+ checkPermission(perms,
1829+ new ExecOptionPermission(option));
1830+ }
1831+ }
1832+ }
1833+ }
1834+
1835+ /**
1836+ * Prints warning message if installed Policy is the default Policy
1837+ * implementation and globally granted permissions do not include
1838+ * AllPermission or any ExecPermissions/ExecOptionPermissions.
1839+ */
1840+ static void checkConfiguration() {
1841+ Policy policy =
1842+ AccessController.doPrivileged(new PrivilegedAction<Policy>() {
1843+ public Policy run() {
1844+ return Policy.getPolicy();
1845+ }
1846+ });
1847+ if (!(policy instanceof PolicyFile)) {
1848+ return;
1849+ }
1850+ PermissionCollection perms = getExecPermissions();
1851+ for (Enumeration<Permission> e = perms.elements();
1852+ e.hasMoreElements();)
1853+ {
1854+ Permission p = e.nextElement();
1855+ if (p instanceof AllPermission ||
1856+ p instanceof ExecPermission ||
1857+ p instanceof ExecOptionPermission)
1858+ {
1859+ return;
1860+ }
1861+ }
1862+ System.err.println(getTextResource("rmid.exec.perms.inadequate"));
1863+ }
1864+
1865+ private static PermissionCollection getExecPermissions() {
1866+ /*
1867+ * The approach used here is taken from the similar method
1868+ * getLoaderAccessControlContext() in the class
1869+ * sun.rmi.server.LoaderHandler.
1870+ */
1871+
1872+ // obtain permissions granted to all code in current policy
1873+ PermissionCollection perms = AccessController.doPrivileged(
1874+ new PrivilegedAction<PermissionCollection>() {
1875+ public PermissionCollection run() {
1876+ CodeSource codesource =
1877+ new CodeSource(null, (Certificate[]) null);
1878+ Policy p = Policy.getPolicy();
1879+ if (p != null) {
1880+ return p.getPermissions(codesource);
1881+ } else {
1882+ return new Permissions();
1883+ }
1884+ }
1885+ });
1886+
1887+ return perms;
1888+ }
1889+
1890+ private static void checkPermission(PermissionCollection perms,
1891+ Permission p)
1892+ throws AccessControlException
1893+ {
1894+ if (!perms.implies(p)) {
1895+ throw new AccessControlException(
1896+ "access denied " + p.toString());
1897+ }
1898+ }
1899+ }
1900+
1901+ /**
1902+ * Main program to start the activation system. <br>
1903+ * The usage is as follows: rmid [-port num] [-log dir].
1904+ */
1905+ public static void main(String[] args) {
1906+ boolean stop = false;
1907+
1908+ // Create and install the security manager if one is not installed
1909+ // already.
1910+ if (System.getSecurityManager() == null) {
1911+ System.setSecurityManager(new SecurityManager());
1912+ }
1913+
1914+ try {
1915+ int port = ActivationSystem.SYSTEM_PORT;
1916+ RMIServerSocketFactory ssf = null;
1917+
1918+ /*
1919+ * If rmid has an inherited channel (meaning that it was
1920+ * launched from inetd), set the server socket factory to
1921+ * return the inherited server socket.
1922+ **/
1923+ Channel inheritedChannel = AccessController.doPrivileged(
1924+ new PrivilegedExceptionAction<Channel>() {
1925+ public Channel run() throws IOException {
1926+ return System.inheritedChannel();
1927+ }
1928+ });
1929+
1930+ if (inheritedChannel != null &&
1931+ inheritedChannel instanceof ServerSocketChannel)
1932+ {
1933+ /*
1934+ * Redirect System.err output to a file.
1935+ */
1936+ AccessController.doPrivileged(
1937+ new PrivilegedExceptionAction<Void>() {
1938+ public Void run() throws IOException {
1939+ File file =
1940+ File.createTempFile("rmid-err", null, null);
1941+ PrintStream errStream =
1942+ new PrintStream(new FileOutputStream(file));
1943+ System.setErr(errStream);
1944+ return null;
1945+ }
1946+ });
1947+
1948+ ServerSocket serverSocket =
1949+ ((ServerSocketChannel) inheritedChannel).socket();
1950+ port = serverSocket.getLocalPort();
1951+ ssf = new ActivationServerSocketFactory(serverSocket);
1952+
1953+ System.err.println(new Date());
1954+ System.err.println(getTextResource(
1955+ "rmid.inherited.channel.info") +
1956+ ": " + inheritedChannel);
1957+ }
1958+
1959+ String log = null;
1960+ List<String> childArgs = new ArrayList<String>();
1961+
1962+ /*
1963+ * Parse arguments
1964+ */
1965+ for (int i = 0; i < args.length; i++) {
1966+ if (args[i].equals("-port")) {
1967+ if (ssf != null) {
1968+ bomb(getTextResource("rmid.syntax.port.badarg"));
1969+ }
1970+ if ((i + 1) < args.length) {
1971+ try {
1972+ port = Integer.parseInt(args[++i]);
1973+ } catch (NumberFormatException nfe) {
1974+ bomb(getTextResource("rmid.syntax.port.badnumber"));
1975+ }
1976+ } else {
1977+ bomb(getTextResource("rmid.syntax.port.missing"));
1978+ }
1979+
1980+ } else if (args[i].equals("-log")) {
1981+ if ((i + 1) < args.length) {
1982+ log = args[++i];
1983+ } else {
1984+ bomb(getTextResource("rmid.syntax.log.missing"));
1985+ }
1986+
1987+ } else if (args[i].equals("-stop")) {
1988+ stop = true;
1989+
1990+ } else if (args[i].startsWith("-C")) {
1991+ childArgs.add(args[i].substring(2));
1992+
1993+ } else {
1994+ bomb(MessageFormat.format(
1995+ getTextResource("rmid.syntax.illegal.option"),
1996+ args[i]));
1997+ }
1998+ }
1999+
2000+ if (log == null) {
2001+ if (ssf != null) {
2002+ bomb(getTextResource("rmid.syntax.log.required"));
2003+ } else {
2004+ log = "log";
2005+ }
2006+ }
2007+
2008+ debugExec = AccessController.doPrivileged(
2009+ new GetBooleanAction("sun.rmi.server.activation.debugExec"));
2010+
2011+ /**
2012+ * Determine class name for activation exec policy (if any).
2013+ */
2014+ String execPolicyClassName = AccessController.doPrivileged(
2015+ new GetPropertyAction("sun.rmi.activation.execPolicy", null));
2016+ if (execPolicyClassName == null) {
2017+ if (!stop) {
2018+ DefaultExecPolicy.checkConfiguration();
2019+ }
2020+ execPolicyClassName = "default";
2021+ }
2022+
2023+ /**
2024+ * Initialize method for activation exec policy.
2025+ */
2026+ if (!execPolicyClassName.equals("none")) {
2027+ if (execPolicyClassName.equals("") ||
2028+ execPolicyClassName.equals("default"))
2029+ {
2030+ execPolicyClassName = DefaultExecPolicy.class.getName();
2031+ }
2032+
2033+ try {
2034+ Class<?> execPolicyClass =
2035+ RMIClassLoader.loadClass(execPolicyClassName);
2036+ execPolicy = execPolicyClass.newInstance();
2037+ execPolicyMethod =
2038+ execPolicyClass.getMethod("checkExecCommand",
2039+ ActivationGroupDesc.class,
2040+ String[].class);
2041+ } catch (Exception e) {
2042+ if (debugExec) {
2043+ System.err.println(
2044+ getTextResource("rmid.exec.policy.exception"));
2045+ e.printStackTrace();
2046+ }
2047+ bomb(getTextResource("rmid.exec.policy.invalid"));
2048+ }
2049+ }
2050+
2051+ if (stop == true) {
2052+ final int finalPort = port;
2053+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
2054+ public Void run() {
2055+ System.setProperty("java.rmi.activation.port",
2056+ Integer.toString(finalPort));
2057+ return null;
2058+ }
2059+ });
2060+ ActivationSystem system = ActivationGroup.getSystem();
2061+ system.shutdown();
2062+ System.exit(0);
2063+ }
2064+
2065+ /*
2066+ * Fix for 4173960: Create and initialize activation using
2067+ * a static method, startActivation, which will build the
2068+ * Activation state in two ways: if when rmid is run, no
2069+ * log file is found, the ActLogHandler.recover(...)
2070+ * method will create a new Activation instance.
2071+ * Alternatively, if a logfile is available, a serialized
2072+ * instance of activation will be read from the log's
2073+ * snapshot file. Log updates will be applied to this
2074+ * Activation object until rmid's state has been fully
2075+ * recovered. In either case, only one instance of
2076+ * Activation is created.
2077+ */
2078+ startActivation(port, ssf, log,
2079+ childArgs.toArray(new String[childArgs.size()]));
2080+
2081+ // prevent activator from exiting
2082+ while (true) {
2083+ try {
2084+ Thread.sleep(Long.MAX_VALUE);
2085+ } catch (InterruptedException e) {
2086+ }
2087+ }
2088+ } catch (Exception e) {
2089+ System.err.println(
2090+ MessageFormat.format(
2091+ getTextResource("rmid.unexpected.exception"), e));
2092+ e.printStackTrace();
2093+ }
2094+ System.exit(1);
2095+ }
2096+
2097+ /**
2098+ * Retrieves text resources from the locale-specific properties file.
2099+ */
2100+ private static String getTextResource(String key) {
2101+ if (Activation.resources == null) {
2102+ try {
2103+ Activation.resources = ResourceBundle.getBundle(
2104+ "sun.rmi.server.resources.rmid");
2105+ } catch (MissingResourceException mre) {
2106+ }
2107+ if (Activation.resources == null) {
2108+ // throwing an Error is a bit extreme, methinks
2109+ return ("[missing resource file: " + key + "]");
2110+ }
2111+ }
2112+
2113+ String val = null;
2114+ try {
2115+ val = Activation.resources.getString (key);
2116+ } catch (MissingResourceException mre) {
2117+ }
2118+
2119+ if (val == null) {
2120+ return ("[missing resource: " + key + "]");
2121+ } else {
2122+ return val;
2123+ }
2124+ }
2125+
2126+ /*
2127+ * Dijkstra semaphore operations to limit the number of subprocesses
2128+ * rmid attempts to make at once.
2129+ */
2130+ /**
2131+ * Acquire the group semaphore and return a group name. Each
2132+ * Pstartgroup must be followed by a Vstartgroup. The calling thread
2133+ * will wait until there are fewer than <code>N</code> other threads
2134+ * holding the group semaphore. The calling thread will then acquire
2135+ * the semaphore and return.
2136+ */
2137+ private synchronized String Pstartgroup() throws ActivationException {
2138+ while (true) {
2139+ checkShutdown();
2140+ // Wait until positive, then decrement.
2141+ if (groupSemaphore > 0) {
2142+ groupSemaphore--;
2143+ return "Group-" + groupCounter++;
2144+ }
2145+
2146+ try {
2147+ wait();
2148+ } catch (InterruptedException e) {
2149+ }
2150+ }
2151+ }
2152+
2153+ /**
2154+ * Release the group semaphore. Every P operation must be
2155+ * followed by a V operation. This may cause another thread to
2156+ * wake up and return from its P operation.
2157+ */
2158+ private synchronized void Vstartgroup() {
2159+ // Increment and notify a waiter (not necessarily FIFO).
2160+ groupSemaphore++;
2161+ notifyAll();
2162+ }
2163+
2164+ /**
2165+ * A server socket factory to use when rmid is launched via 'inetd'
2166+ * with 'wait' status. This socket factory's 'createServerSocket'
2167+ * method returns the server socket specified during construction that
2168+ * is specialized to delay accepting requests until the
2169+ * 'initDone' flag is 'true'. The server socket supplied to
2170+ * the constructor should be the server socket obtained from the
2171+ * ServerSocketChannel returned from the 'System.inheritedChannel'
2172+ * method.
2173+ **/
2174+ private static class ActivationServerSocketFactory
2175+ implements RMIServerSocketFactory
2176+ {
2177+ private final ServerSocket serverSocket;
2178+
2179+ /**
2180+ * Constructs an 'ActivationServerSocketFactory' with the specified
2181+ * 'serverSocket'.
2182+ **/
2183+ ActivationServerSocketFactory(ServerSocket serverSocket) {
2184+ this.serverSocket = serverSocket;
2185+ }
2186+
2187+ /**
2188+ * Returns the server socket specified during construction wrapped
2189+ * in a 'DelayedAcceptServerSocket'.
2190+ **/
2191+ public ServerSocket createServerSocket(int port)
2192+ throws IOException
2193+ {
2194+ return new DelayedAcceptServerSocket(serverSocket);
2195+ }
2196+
2197+ }
2198+
2199+ /**
2200+ * A server socket that delegates all public methods to the underlying
2201+ * server socket specified at construction. The accept method is
2202+ * overridden to delay calling accept on the underlying server socket
2203+ * until the 'initDone' flag is 'true'.
2204+ **/
2205+ private static class DelayedAcceptServerSocket extends ServerSocket {
2206+
2207+ private final ServerSocket serverSocket;
2208+
2209+ DelayedAcceptServerSocket(ServerSocket serverSocket)
2210+ throws IOException
2211+ {
2212+ this.serverSocket = serverSocket;
2213+ }
2214+
2215+ public void bind(SocketAddress endpoint) throws IOException {
2216+ serverSocket.bind(endpoint);
2217+ }
2218+
2219+ public void bind(SocketAddress endpoint, int backlog)
2220+ throws IOException
2221+ {
2222+ serverSocket.bind(endpoint, backlog);
2223+ }
2224+
2225+ public InetAddress getInetAddress() {
2226+ return serverSocket.getInetAddress();
2227+ }
2228+
2229+ public int getLocalPort() {
2230+ return serverSocket.getLocalPort();
2231+ }
2232+
2233+ public SocketAddress getLocalSocketAddress() {
2234+ return serverSocket.getLocalSocketAddress();
2235+ }
2236+
2237+ /**
2238+ * Delays calling accept on the underlying server socket until the
2239+ * remote service is bound in the registry.
2240+ **/
2241+ public Socket accept() throws IOException {
2242+ synchronized (initLock) {
2243+ try {
2244+ while (!initDone) {
2245+ initLock.wait();
2246+ }
2247+ } catch (InterruptedException ignore) {
2248+ throw new AssertionError(ignore);
2249+ }
2250+ }
2251+ return serverSocket.accept();
2252+ }
2253+
2254+ public void close() throws IOException {
2255+ serverSocket.close();
2256+ }
2257+
2258+ public ServerSocketChannel getChannel() {
2259+ return serverSocket.getChannel();
2260+ }
2261+
2262+ public boolean isBound() {
2263+ return serverSocket.isBound();
2264+ }
2265+
2266+ public boolean isClosed() {
2267+ return serverSocket.isClosed();
2268+ }
2269+
2270+ public void setSoTimeout(int timeout)
2271+ throws SocketException
2272+ {
2273+ serverSocket.setSoTimeout(timeout);
2274+ }
2275+
2276+ public int getSoTimeout() throws IOException {
2277+ return serverSocket.getSoTimeout();
2278+ }
2279+
2280+ public void setReuseAddress(boolean on) throws SocketException {
2281+ serverSocket.setReuseAddress(on);
2282+ }
2283+
2284+ public boolean getReuseAddress() throws SocketException {
2285+ return serverSocket.getReuseAddress();
2286+ }
2287+
2288+ public String toString() {
2289+ return serverSocket.toString();
2290+ }
2291+
2292+ public void setReceiveBufferSize(int size)
2293+ throws SocketException
2294+ {
2295+ serverSocket.setReceiveBufferSize(size);
2296+ }
2297+
2298+ public int getReceiveBufferSize()
2299+ throws SocketException
2300+ {
2301+ return serverSocket.getReceiveBufferSize();
2302+ }
2303+ }
2304+}
2305+
2306+/**
2307+ * PipeWriter plugs together two pairs of input and output streams by
2308+ * providing readers for input streams and writing through to
2309+ * appropriate output streams. Both output streams are annotated on a
2310+ * per-line basis.
2311+ *
2312+ * @author Laird Dornin, much code borrowed from Peter Jones, Ken
2313+ * Arnold and Ann Wollrath.
2314+ */
2315+class PipeWriter implements Runnable {
2316+
2317+ /** stream used for buffering lines */
2318+ private ByteArrayOutputStream bufOut;
2319+
2320+ /** count since last separator */
2321+ private int cLast;
2322+
2323+ /** current chunk of input being compared to lineSeparator.*/
2324+ private byte[] currSep;
2325+
2326+ private PrintWriter out;
2327+ private InputStream in;
2328+
2329+ private String pipeString;
2330+ private String execString;
2331+
2332+ private static String lineSeparator;
2333+ private static int lineSeparatorLength;
2334+
2335+ private static int numExecs = 0;
2336+
2337+ static {
2338+ lineSeparator = AccessController.doPrivileged(
2339+ new GetPropertyAction("line.separator"));
2340+ lineSeparatorLength = lineSeparator.length();
2341+ }
2342+
2343+ /**
2344+ * Create a new PipeWriter object. All methods of PipeWriter,
2345+ * except plugTogetherPair, are only accesible to PipeWriter
2346+ * itself. Synchronization is unnecessary on functions that will
2347+ * only be used internally in PipeWriter.
2348+ *
2349+ * @param in input stream from which pipe input flows
2350+ * @param out output stream to which log messages will be sent
2351+ * @param dest String which tags output stream as 'out' or 'err'
2352+ * @param nExecs number of execed processes, Activation groups.
2353+ */
2354+ private PipeWriter
2355+ (InputStream in, OutputStream out, String tag, int nExecs) {
2356+
2357+ this.in = in;
2358+ this.out = new PrintWriter(out);
2359+
2360+ bufOut = new ByteArrayOutputStream();
2361+ currSep = new byte[lineSeparatorLength];
2362+
2363+ /* set unique pipe/pair annotations */
2364+ execString = ":ExecGroup-" +
2365+ Integer.toString(nExecs) + ':' + tag + ':';
2366+ }
2367+
2368+ /**
2369+ * Create a thread to listen and read from input stream, in. buffer
2370+ * the data that is read until a marker which equals lineSeparator
2371+ * is read. Once such a string has been discovered; write out an
2372+ * annotation string followed by the buffered data and a line
2373+ * separator.
2374+ */
2375+ public void run() {
2376+ byte[] buf = new byte[256];
2377+ int count;
2378+
2379+ try {
2380+ /* read bytes till there are no more. */
2381+ while ((count = in.read(buf)) != -1) {
2382+ write(buf, 0, count);
2383+ }
2384+
2385+ /* flush internal buffer... may not have ended on a line
2386+ * separator, we also need a last annotation if
2387+ * something was left.
2388+ */
2389+ String lastInBuffer = bufOut.toString();
2390+ bufOut.reset();
2391+ if (lastInBuffer.length() > 0) {
2392+ out.println (createAnnotation() + lastInBuffer);
2393+ out.flush(); // add a line separator
2394+ // to make output nicer
2395+ }
2396+
2397+ } catch (IOException e) {
2398+ }
2399+ }
2400+
2401+ /**
2402+ * Write a subarray of bytes. Pass each through write byte method.
2403+ */
2404+ private void write(byte b[], int off, int len) throws IOException {
2405+
2406+ if (len < 0) {
2407+ throw new ArrayIndexOutOfBoundsException(len);
2408+ }
2409+ for (int i = 0; i < len; ++ i) {
2410+ write(b[off + i]);
2411+ }
2412+ }
2413+
2414+ /**
2415+ * Write a byte of data to the stream. If we have not matched a
2416+ * line separator string, then the byte is appended to the internal
2417+ * buffer. If we have matched a line separator, then the currently
2418+ * buffered line is sent to the output writer with a prepended
2419+ * annotation string.
2420+ */
2421+ private void write(byte b) throws IOException {
2422+ int i = 0;
2423+
2424+ /* shift current to the left */
2425+ for (i = 1 ; i < (currSep.length); i ++) {
2426+ currSep[i-1] = currSep[i];
2427+ }
2428+ currSep[i-1] = b;
2429+ bufOut.write(b);
2430+
2431+ /* enough characters for a separator? */
2432+ if ( (cLast >= (lineSeparatorLength - 1)) &&
2433+ (lineSeparator.equals(new String(currSep))) ) {
2434+
2435+ cLast = 0;
2436+
2437+ /* write prefix through to underlying byte stream */
2438+ out.print(createAnnotation() + bufOut.toString());
2439+ out.flush();
2440+ bufOut.reset();
2441+
2442+ if (out.checkError()) {
2443+ throw new IOException
2444+ ("PipeWriter: IO Exception when"+
2445+ " writing to output stream.");
2446+ }
2447+
2448+ } else {
2449+ cLast++;
2450+ }
2451+ }
2452+
2453+ /**
2454+ * Create an annotation string to be printed out after
2455+ * a new line and end of stream.
2456+ */
2457+ private String createAnnotation() {
2458+
2459+ /* construct prefix for log messages:
2460+ * date/time stamp...
2461+ */
2462+ return ((new Date()).toString() +
2463+ /* ... print pair # ... */
2464+ (execString));
2465+ }
2466+
2467+ /**
2468+ * Allow plugging together two pipes at a time, to associate
2469+ * output from an execed process. This is the only publicly
2470+ * accessible method of this object; this helps ensure that
2471+ * synchronization will not be an issue in the annotation
2472+ * process.
2473+ *
2474+ * @param in input stream from which pipe input comes
2475+ * @param out output stream to which log messages will be sent
2476+ * @param in1 input stream from which pipe input comes
2477+ * @param out1 output stream to which log messages will be sent
2478+ */
2479+ static void plugTogetherPair(InputStream in,
2480+ OutputStream out,
2481+ InputStream in1,
2482+ OutputStream out1) {
2483+ Thread inThread = null;
2484+ Thread outThread = null;
2485+
2486+ int nExecs = getNumExec();
2487+
2488+ /* start RMI threads to read output from child process */
2489+ inThread = AccessController.doPrivileged(
2490+ new NewThreadAction(new PipeWriter(in, out, "out", nExecs),
2491+ "out", true));
2492+ outThread = AccessController.doPrivileged(
2493+ new NewThreadAction(new PipeWriter(in1, out1, "err", nExecs),
2494+ "err", true));
2495+ inThread.start();
2496+ outThread.start();
2497+ }
2498+
2499+ private static synchronized int getNumExec() {
2500+ return numExecs++;
2501+ }
2502+}