KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > util > newjvm > AbstractMasterJVM


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  * END_COPYRIGHT_BLOCK*/

32
33 package edu.rice.cs.util.newjvm;
34
35 import edu.rice.cs.util.Log;
36 import edu.rice.cs.util.UnexpectedException;
37 import edu.rice.cs.drjava.config.FileOption;
38
39 import java.rmi.*;
40 import java.rmi.server.*;
41 import java.io.*;
42 import java.util.Arrays JavaDoc;
43
44 /** An abstract class implementing the logic to invoke and control, via RMI, a second Java virtual
45  * machine. This class is used by subclassing it. (See package documentation for more details.)
46  * This class runs in both the master and the slave JVMs.
47  * @version $Id: AbstractMasterJVM.java 4031 2006-11-15 22:09:06Z rcartwright $
48  */

49 public abstract class AbstractMasterJVM/*<SlaveType extends SlaveRemote>*/
50   implements MasterRemote/*<SlaveType>*/ {
51   
52   public static final Log _log = new Log("MasterSlave.txt", false);
53   
54   /** Name for the thread that waits for the slave to exit. */
55   protected volatile String JavaDoc _waitForQuitThreadName = "Wait for SlaveJVM Exit Thread";
56   
57 // /** Name for the thread that exports the MasterJVM to RMI. */
58
// protected volatile String _exportMasterThreadName = "Export MasterJVM Thread";
59

60   /** Lock for accessing the critical state of this AbstractMasterJVM including _monitorThread. */
61   protected final Object JavaDoc _masterJVMLock = new Object JavaDoc();
62   
63   private static final String JavaDoc RUNNER = SlaveJVMRunner.class.getName();
64   
65   /** The slave JVM remote stub if it's connected; null if not connected. */
66   private volatile SlaveRemote _slave;
67
68   /** Is slave JVM in the process of starting up? INVARIANT: _startupInProgess => _slave == null. */
69   private volatile boolean _startupInProgress = false;
70
71  /** This flag is set when a quit request is issued before the slave has finished starting up.
72    * In that case, immediately after starting up, we quit it. INVARIANT: _quitOnStartUp => _startupInProgress
73    */

74   private volatile boolean _quitOnStartup = false;
75   
76 // /** Lock used in exporting this object to a file and loading it in the slaveJVM; protects stub variables. */
77
// final static Object Lock = new Object();
78

79   /** The current remote stub for this main JVM object. This field is null except between the time the slave
80    * JVM is first invoked and the time the slave registers itself.
81    */

82   private volatile MasterRemote _masterStub = null;
83   
84   /** The file containing the serialized remote stub. This field is null except between the time the slave
85    * JVM is first invoked and the time the slave registers itself.
86    */

87   private volatile File _masterStubFile;
88   
89   /** The fully-qualified name of the slave JVM class. */
90   private final String JavaDoc _slaveClassName;
91   
92   /** The thread monitoring the Slave JVM, waiting for it to terminate. This feature inhibits the creation
93    * of more than one Slave JVM corresponding to "this"
94    */

95   private volatile Thread JavaDoc _monitorThread;
96   
97 // /** The lock used to protect _monitorThread. */
98
// private final Object _monitorLock = new Object();
99

100   /** Sets up the master JVM object, but does not actually invoke the slave JVM.
101    * @param slaveClassName The fully-qualified class name of the class to start up in the second JVM. This
102    * class must implement the interface specified by this class's type parameter, which must be a subclass
103    * of {@link SlaveRemote}.
104    */

105   protected AbstractMasterJVM(String JavaDoc slaveClassName) {
106     _slaveClassName = slaveClassName;
107     _slave = null;
108     _monitorThread = null;
109     
110     _log.log(this + " CREATED");
111     
112     // Make sure RMI doesn't use an IP address that might change
113
System.setProperty("java.rmi.server.hostname", "127.0.0.1");
114   }
115
116   /** Callback for when the slave JVM has connected, and the bidirectional communications link has been
117    * established. During this call, {@link #getSlave} is guaranteed to not return null.
118    */

119   protected abstract void handleSlaveConnected();
120   
121   /** Callback for when the slave JVM has quit. During this call, {@link #getSlave} is guaranteed to return null.
122    * @param status The exit code returned by the slave JVM.
123    */

124   protected abstract void handleSlaveQuit(int status);
125   
126   /** Invokes slave JVM without any JVM arguments.
127    * @throws IllegalStateException if slave JVM already connected or startUp is in progress.
128    */

129   protected final void invokeSlave() throws IOException, RemoteException {
130     invokeSlave(new String JavaDoc[0], FileOption.NULL_FILE);
131   }
132   
133   /** Invokes slave JVM, using the system classpath.
134    * @param jvmArgs Array of arguments to pass to the JVM on startUp
135    * @throws IllegalStateException if slave JVM already connected or startUp is in progress.
136    */

137   protected final void invokeSlave(String JavaDoc[] jvmArgs, File workDir) throws IOException, RemoteException {
138     invokeSlave(jvmArgs, System.getProperty("java.class.path"), workDir);
139   }
140  
141   /** Creates and invokes slave JVM.
142    * @param jvmArgs Array of arguments to pass to the JVM on startUp
143    * @param cp Classpath to use when starting the JVM
144    * @throws IllegalStateException if slave JVM already connected or startUp is in progress.
145    */

146   protected final void invokeSlave(final String JavaDoc[] jvmArgs, final String JavaDoc cp, final File workDir) throws IOException,
147     RemoteException {
148     
149     synchronized(_masterJVMLock) { // synchronization prelude only lets one thread at a time execute the sequel
150

151       try { while (_startupInProgress || _monitorThread != null) _masterJVMLock.wait(); }
152       catch(InterruptedException JavaDoc e) { throw new UnexpectedException(e); }
153       _startupInProgress = true;
154     }
155     
156     _log.log(this + ".invokeSlave(...) called");
157     
158     /******************************************************************************************************
159      * First, we we export ourselves to a file, if it has not already been done on a previous invocation. *
160      *****************************************************************************************************/

161
162     if (_masterStub == null) {
163       try { _masterStub = (MasterRemote) UnicastRemoteObject.exportObject(this); }
164       catch (RemoteException re) {
165         javax.swing.JOptionPane.showMessageDialog(null, edu.rice.cs.util.StringOps.getStackTrace(re));
166         _log.log(this + " threw " + re);
167         throw new UnexpectedException(re); // should never happen
168
}
169       _log.log(this + " EXPORTed Master JVM");
170       
171       _masterStubFile = File.createTempFile("DrJava-remote-stub", ".tmp");
172       _masterStubFile.deleteOnExit();
173       
174       // serialize stub to _masterStubFile
175
FileOutputStream fstream = new FileOutputStream(_masterStubFile);
176       ObjectOutputStream ostream = new ObjectOutputStream(fstream);
177       ostream.writeObject(_masterStub);
178       ostream.flush();
179       fstream.close();
180       ostream.close();
181     }
182     
183     final String JavaDoc[] args = new String JavaDoc[] { _masterStubFile.getAbsolutePath(), _slaveClassName };
184     
185     // Start a thread to create the slave JVM and wait for it to die. When it dies, delegate what to do (restart?)
186
// to subclass
187
_monitorThread = new Thread JavaDoc(_waitForQuitThreadName) {
188       public void run() {
189         try { /* Create the slave JVM. */
190           
191           _log.log(AbstractMasterJVM.this + " is STARTING a Slave JVM with args " + Arrays.asList(args));
192           
193           final Process JavaDoc process = ExecJVM.runJVM(RUNNER, args, cp, jvmArgs, workDir);
194           _log.log(AbstractMasterJVM.this + " CREATED Slave JVM process " + process + " with " + asString());
195           
196           int status = process.waitFor();
197           _log.log(process + " DIED under control of " + asString() + " with status " + status);
198           synchronized(_masterJVMLock) {
199             if (_startupInProgress) {
200               _log.log("Process " + process + " died while starting up");
201               /* If we get here, the process died without registering. One possible cause is the intermittent funky 3 minute
202                * pause in readObject in RUNNER. Other possible causes are errors in the classpath or the absence of a
203                * debug port. Proper behavior in this case is unclear, so we'll let our subclasses decide. */

204               slaveQuitDuringStartup(status);
205             }
206             if (_slave != null) { // Slave JVM quit spontaneously
207
_slave = null;
208             }
209             _monitorThread = null;
210             _masterJVMLock.notifyAll(); // signal that Slave JVM died to any thread waiting for _monitorThread == null
211
}
212             
213 // _log.log(asString() + " calling handleSlaveQuit(" + status + ")");
214
handleSlaveQuit(status);
215         }
216         catch(NoSuchObjectException e) { throw new UnexpectedException(e); }
217         catch(InterruptedException JavaDoc e) { throw new UnexpectedException(e); }
218         catch(IOException e) { throw new UnexpectedException(e); }
219       }
220       private String JavaDoc asString() { return "MonitorThread@" + Integer.toHexString(hashCode()); }
221     };
222 // _log.log(this + " is starting a slave monitor thread to detect when the Slave JVM dies");
223
_monitorThread.start();
224   }
225   
226   /** Waits until no slave JVM is running under control of "this" */
227   public void waitSlaveDone() {
228     try { synchronized(_masterJVMLock) { while (_monitorThread != null) _masterJVMLock.wait(); }}
229     catch(InterruptedException JavaDoc e) { throw new UnexpectedException(e); }
230   }
231     
232   /** Action to take if the slave JVM quits before registering. Assumes _masterJVMLock is held.
233    * @param status Status code of the JVM
234    */

235   protected void slaveQuitDuringStartup(int status) {
236     // Reset Master JVM state (in case invokeSlave is called again on this object)
237
_startupInProgress = false;
238     _quitOnStartup = false;
239     _monitorThread = null;
240   }
241   
242   /** Called if the slave JVM dies before it is able to register.
243    * @param cause The Throwable which caused the slave to die.
244    */

245   public abstract void errorStartingSlave(Throwable JavaDoc cause) throws RemoteException;
246   
247   /** No-op to prove that the master is still alive. */
248   public void checkStillAlive() { }
249   
250   /* Records the identity and status of the Slave JVM in the Master JVM */
251   public void registerSlave(SlaveRemote slave) throws RemoteException {
252     _log.log(this + " registering Slave " + slave);
253     
254     boolean quitSlavePending; // flag used to move quitSlave() call out of synchronized block
255

256     synchronized(_masterJVMLock) {
257       _slave = slave;
258       _startupInProgress = false;
259       
260       _log.log(this + " calling handleSlaveConnected()");
261       
262       handleSlaveConnected();
263       
264       quitSlavePending = _quitOnStartup;
265       if (_quitOnStartup) {
266         // quitSlave was called before the slave registered, so we now act on the deferred quit request.
267
_quitOnStartup = false;
268       }
269     }
270     if (quitSlavePending) {
271       _log.log(this + " Executing deferred quitSlave() that was called during startUp");
272       quitSlave(); // not synchronized; _slave may be null when this code executes
273
}
274   }
275   
276   /** Withdraws RMI exports for this. */
277   public void dispose() throws RemoteException {
278     _log.log(this + ".dispose() called; slaveRemote is " + _slave);
279     if (_startupInProgress) _log.log(this + ".dispose() is KILLing startUp in process; dying slave reference does not yet exist");
280     SlaveRemote dyingSlave;
281     synchronized(_masterJVMLock) {
282       _masterStub = null;
283       if (_monitorThread != null) _monitorThread = null;
284       dyingSlave = _slave; // save value of _slave in case it is not null
285
_slave = null;
286       
287       // Withdraw RMI exports
288
// Slave in process of starting will die because master is inaccessible.
289
_log.log(this + ".dispose() UNEXPORTing " + this);
290       UnicastRemoteObject.unexportObject(this, true);
291     }
292     if (dyingSlave != null) {
293       _log.log(this + ".dispose() QUITing " + dyingSlave);
294       dyingSlave.quit(); // unsynchronized; may hasten the death of dyingSlave
295
}
296   }
297   
298   /** Quits slave JVM. On exit, _slave == null. _quitOnStartup may be true
299    * @throws IllegalStateException if no slave JVM is connected
300    */

301   protected final void quitSlave() throws RemoteException {
302     SlaveRemote dyingSlave;
303     synchronized(_masterJVMLock) {
304       if (isStartupInProgress()) {
305         /* There is a slave to be quit, but _slave == null, so we cannot contact it yet. Instead we set _quitOnStartup
306          * and tell the slave to quit when it registers in registerSlave. */

307         _quitOnStartup = true;
308         return;
309       }
310       else if (_slave == null) {
311         _log.log(this + " called quitSlave() when no slave was running");
312         return;
313       }
314       else {
315         dyingSlave = _slave;
316         _slave = null;
317       }
318     }
319     dyingSlave.quit(); // remote operation is not synchronized!
320
}
321   
322   /** Returns slave remote instance, or null if not connected. */
323   protected final SlaveRemote getSlave() { return _slave; }
324   
325   /** Returns true if the slave is in the process of starting. */
326   protected boolean isStartupInProgress() { return _startupInProgress; }
327 }
328
Popular Tags