KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > repl > newjvm > MainJVM


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  *
32 END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.repl.newjvm;
35
36 import java.rmi.*;
37 import java.io.*;
38 import java.net.URL JavaDoc;
39 import java.net.MalformedURLException JavaDoc;
40
41 import java.util.List JavaDoc;
42 import java.util.ArrayList JavaDoc;
43
44 // NOTE: Do NOT import/use the config framework in this class!
45
// (It seems to crash Eclipse...)
46
import edu.rice.cs.drjava.DrJava;
47 import edu.rice.cs.drjava.config.OptionConstants;
48 import edu.rice.cs.drjava.model.GlobalModel;
49 import edu.rice.cs.drjava.model.repl.*;
50 import edu.rice.cs.drjava.model.junit.JUnitError;
51 import edu.rice.cs.drjava.model.junit.JUnitModelCallback;
52 import edu.rice.cs.drjava.model.debug.DebugModelCallback;
53
54 import edu.rice.cs.util.ArgumentTokenizer;
55 import edu.rice.cs.util.ClassPathVector;
56 import edu.rice.cs.util.FileOps;
57 import edu.rice.cs.util.Log;
58 import edu.rice.cs.util.StringOps;
59 import edu.rice.cs.util.UnexpectedException;
60 import edu.rice.cs.plt.io.IOUtil;
61
62 import edu.rice.cs.util.newjvm.*;
63 import edu.rice.cs.util.classloader.ClassFileError;
64 import edu.rice.cs.util.swing.Utilities;
65 import edu.rice.cs.util.swing.ScrollableDialog;
66 import koala.dynamicjava.parser.wrapper.*;
67
68 /** Manages a remote JVM.
69  * @version $Id: MainJVM.java 4084 2007-01-23 23:01:23Z dlsmith $
70  */

71 public class MainJVM extends AbstractMasterJVM implements MainJVMRemoteI {
72   /** Name of the class to use in the remote JVM. */
73   private static final String JavaDoc SLAVE_CLASS_NAME = "edu.rice.cs.drjava.model.repl.newjvm.InterpreterJVM";
74   
75   public static final String JavaDoc DEFAULT_INTERPRETER_NAME = "DEFAULT";
76   
77   // _log is inherited from AbstractMasterJVM
78

79   /** Working directory for slave JVM */
80   private volatile File _workDir;
81   
82   /** Listens to interactions-related events. */
83   private volatile InteractionsModelCallback _interactionsModel;
84   
85   /** Listens to JUnit-related events. */
86   private volatile JUnitModelCallback _junitModel;
87   
88   /** Listens to debug-related events */
89   private volatile DebugModelCallback _debugModel;
90   
91   /** Used to protect interpreterJVM setting */
92   private final Object JavaDoc _interpreterLock = new Object JavaDoc();
93   
94   /** Records state of slaveJVM (interpreterJVM); used to suppress restartInteractions on a fresh JVM */
95   private volatile boolean _slaveJVMUsed = false;
96   
97   /** This flag is set to false to inhibit the automatic restart of the JVM. */
98   private volatile boolean _restart = true;
99   
100   /** This flag is set to remember that the JVM is cleanly restarting, so that the replCalledSystemExit method
101    * does not need to be called.
102    */

103   private volatile boolean _cleanlyRestarting = false;
104   
105   /** Instance of inner class to handle interpret result. */
106   private final ResultHandler _handler = new ResultHandler();
107   
108   /** Whether to allow "assert" statements to run in the remote JVM. */
109   private volatile boolean _allowAssertions = false;
110   
111   /** Classpath to use for starting the interpreter JVM */
112   private volatile Iterable JavaDoc<File> _startupClassPath;
113
114   /** A list of user-defined arguments to pass to the interpreter. */
115   private volatile List JavaDoc<String JavaDoc> _optionArgs;
116   
117   /** The name of the current interpreter. */
118   private volatile String JavaDoc _currentInterpreterName = DEFAULT_INTERPRETER_NAME;
119   
120   /** Creates a new MainJVM to interface to another JVM; the MainJVM has a link to the partially initialized
121    * global model. The MainJVM but does not automatically start the Interpreter JVM. Callers must set the
122    * InteractionsModel and JUnitModel and then call startInterpreterJVM().
123    */

124   public MainJVM(File wd) {
125     super(SLAVE_CLASS_NAME);
126 // Utilities.show("Starting the slave JVM");
127
_workDir = wd;
128     _waitForQuitThreadName = "Wait for Interactions to Exit Thread";
129 // _exportMasterThreadName = "Export DrJava to RMI Thread";
130

131     _interactionsModel = new DummyInteractionsModel();
132     _junitModel = new DummyJUnitModel();
133     _debugModel = new DummyDebugModel();
134     _startupClassPath = IOUtil.attemptCanonicalFiles(IOUtil.parsePath(System.getProperty("java.class.path")));
135     _optionArgs = new ArrayList JavaDoc<String JavaDoc>();
136   }
137   
138   public boolean isInterpreterRunning() { return _interpreterJVM() != null; }
139   
140   public boolean slaveJVMUsed() { return _slaveJVMUsed; }
141   
142   /** Provides an object to listen to interactions-related events. */
143   public void setInteractionsModel(InteractionsModelCallback model) { _interactionsModel = model; }
144   
145   /** Provides an object to listen to test-related events.*/
146   public void setJUnitModel(JUnitModelCallback model) { _junitModel = model; }
147   
148   /** Provides an object to listen to debug-related events.
149    * @param model the debug model
150    */

151   public void setDebugModel(DebugModelCallback model) { _debugModel = model; }
152   
153   /** Sets whether the remote JVM will run "assert" statements after the next restart. */
154   public void setAllowAssertions(boolean allow) { _allowAssertions = allow; }
155   
156   /** Sets the extra (optional) arguments to be passed to the interpreter.
157    * @param argString the arguments as they would be typed at the command-line
158    */

159   public void setOptionArgs(String JavaDoc argString) { _optionArgs = ArgumentTokenizer.tokenize(argString); }
160   
161   /** Interprets string s in slave JVM. No masterJVMLock synchronization because reading _restart is the only
162    * access.to master JVM state. */

163   public void interpret(final String JavaDoc s) {
164     // silently fail if disabled. see killInterpreter docs for details.
165
if (! _restart) return;
166     
167     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
168     
169     // Spawn thread on InterpreterJVM side
170
// (will receive result in the interpretResult(...) method)
171
try {
172       _log.log(this + ".interpret(" + s + ")");
173       _slaveJVMUsed = true;
174       _interactionsModel.slaveJVMUsed();
175       slave.interpret(s);
176     }
177     catch (java.rmi.UnmarshalException JavaDoc ume) {
178       // Could not receive result from interpret; system probably exited.
179
// We will silently fail and let the interpreter restart.
180
_log.log(this + ".interpret threw UnmarshalException, so interpreter is dead:\n" + ume);
181     }
182     catch (RemoteException re) { _threwException(re); }
183   }
184   
185   /** Gets the string representation of the value of a variable in the current interpreter.
186    * @param var the name of the variable
187    */

188   public String JavaDoc getVariableToString(String JavaDoc var) {
189     // silently fail if disabled. see killInterpreter docs for details.
190
if (! _restart) return null;
191     
192     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
193     
194     try { return slave.getVariableToString(var); }
195     catch (RemoteException re) {
196       _threwException(re);
197       return null;
198     }
199   }
200   
201   /** Gets the class name of a variable in the current interpreter.
202    * @param var the name of the variable
203    */

204   public String JavaDoc getVariableClassName(String JavaDoc var) {
205     // silently fail if disabled. see killInterpreter docs for details.
206
if (! _restart) return null;
207     
208     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
209       
210     try { return slave.getVariableClassName(var); }
211     catch (RemoteException re) {
212       _threwException(re);
213       return null;
214     }
215   }
216   
217   /** Called when a call to interpret has completed.
218    * @param result The result of the interpretation
219    */

220   public void interpretResult(InterpretResult result) throws RemoteException {
221     try {
222       _log.log(this + ".interpretResult(" + result + ")");
223       
224       result.apply(getResultHandler());
225     }
226     catch (Throwable JavaDoc t) {
227       _log.log(this + "interpretResult threw " + t.toString());
228     }
229   }
230   
231 // /**
232
// * Adds a single path to the Interpreter's class path.
233
// * This method <b>cannot</b> take multiple paths separated by
234
// * a path separator; it must be called separately for each path.
235
// * @param path Path to be added to classpath
236
// */
237
// public void addClassPath(String path) {
238
// // silently fail if disabled. see killInterpreter docs for details.
239
// if (! _restart) return;
240
//
241
// ensureInterpreterConnected();
242
//
243
// try {
244
// // System.err.println("addclasspath to " + _interpreterJVM() + ": " + path);
245
// // System.err.println("full classpath: " + getClasspath());
246
// _interpreterJVM().addClassPath(path);
247
// }
248
// catch (RemoteException re) {
249
// _threwException(re);
250
// }
251
// }
252

253   public void addProjectClassPath(URL JavaDoc path) {
254     if (! _restart) return;
255     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
256     
257     try { slave.addProjectClassPath(path.toString()); }
258     catch(RemoteException re) { _threwException(re); }
259   }
260   
261   public void addBuildDirectoryClassPath(URL JavaDoc path) {
262     if (! _restart) return;
263     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
264     
265     try { slave.addBuildDirectoryClassPath(path.toString()); }
266     catch(RemoteException re) { _threwException(re); }
267   }
268   
269   public void addProjectFilesClassPath(URL JavaDoc path) {
270     if (! _restart) return;
271     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
272     
273     try { slave.addProjectFilesClassPath(path.toString()); }
274     catch(RemoteException re) { _threwException(re); }
275   }
276   
277   public void addExternalFilesClassPath(URL JavaDoc path) {
278     if (! _restart) return;
279     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
280     
281     try { slave.addExternalFilesClassPath(path.toString()); }
282     catch(RemoteException re) { _threwException(re); }
283   }
284   
285   public void addExtraClassPath(URL JavaDoc path) {
286     if (! _restart) return;
287     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
288     
289     try { slave.addExtraClassPath(path.toString()); }
290     catch(RemoteException re) { _threwException(re); }
291   }
292   
293   /** Returns the current classpath of the interpreter as a list of
294    * unique entries. The list is empty if a remote exception occurs.
295    */

296   public ClassPathVector getClassPath() {
297     // silently fail if disabled. see killInterpreter docs for details.
298
if (_restart) {
299       
300       InterpreterJVMRemoteI slave = ensureInterpreterConnected();
301       
302       try {
303         ClassPathVector classPath = slave.getAugmentedClassPath(); // returns fresh copy
304
for (File f : _startupClassPath) {
305           try { classPath.add(FileOps.toURL(f)); }
306           catch (MalformedURLException JavaDoc e) { /* just ignore bad classpath entry */ }
307         }
308         return classPath;
309       }
310       catch (RemoteException re) { _threwException(re); return new ClassPathVector(); }
311     }
312     else { return new ClassPathVector(); }
313   }
314   
315   
316   /** Sets the Interpreter to be in the given package.
317    * @param packageName Name of the package to enter.
318    */

319   public void setPackageScope(String JavaDoc packageName) {
320     // silently fail if disabled. see killInterpreter docs for details.
321
if (! _restart) return;
322     
323     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
324     
325     try { slave.setPackageScope(packageName); }
326     catch (RemoteException re) { _threwException(re); }
327   }
328   
329   /** @param show Whether to show a message if a reset operation fails. */
330   public void setShowMessageOnResetFailure(boolean show) {
331     // silently fail if disabled. see killInterpreter docs for details.
332
if (! _restart) return;
333     
334     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
335
336     try { slave.setShowMessageOnResetFailure(show); }
337     catch (RemoteException re) { _threwException(re); }
338   }
339   
340   /** Forwards a call to System.err from InterpreterJVM to the local InteractionsModel.
341    * @param s String that was printed in the other JVM
342    */

343   public void systemErrPrint(String JavaDoc s) throws RemoteException {
344     _interactionsModel.replSystemErrPrint(s);
345   }
346   
347   /** Forwards a call to System.out from InterpreterJVM to the local InteractionsModel.
348    * @param s String that was printed in the other JVM
349    */

350   public void systemOutPrint(String JavaDoc s) throws RemoteException {
351     _interactionsModel.replSystemOutPrint(s);
352   }
353   
354   /** Sets up a JUnit test suite in the Interpreter JVM and finds which classes are really TestCases
355    * classes (by loading them)
356    * @param classNames the class names to run in a test
357    * @param files the associated file
358    * @return the class names that are actually test cases
359    */

360   public List JavaDoc<String JavaDoc> findTestClasses(List JavaDoc<String JavaDoc> classNames, List JavaDoc<File> files) throws RemoteException {
361     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
362     return slave.findTestClasses(classNames, files);
363   }
364   
365   /** Runs the JUnit test suite already cached in the Interpreter JVM.
366    * @return false if no test suite is cached; true otherwise
367    */

368   public boolean runTestSuite() throws RemoteException {
369     return _interpreterJVM().runTestSuite();
370   }
371   
372   /** Called if JUnit is invoked on a non TestCase class. Forwards from the other JVM to the local JUnit model.
373    * @param isTestAll whether or not it was a use of the test all button
374    */

375   public void nonTestCase(boolean isTestAll) throws RemoteException {
376     _junitModel.nonTestCase(isTestAll);
377   }
378   
379   /** Called if the slave JVM encounters an illegal class file in testing. Forwards from
380    * the other JVM to the local JUnit model.
381    * @param e the ClassFileError describing the error when loading the class file
382    */

383   public void classFileError(ClassFileError e) throws RemoteException {
384 // Utilities.showDebug("classFileError(" + e + ") called in MainJVM");
385
_junitModel.classFileError(e);
386   }
387   /** Called to indicate that a suite of tests has started running.
388    * Forwards from the other JVM to the local JUnit model.
389    * @param numTests The number of tests in the suite to be run.
390    */

391   public void testSuiteStarted(int numTests) throws RemoteException {
392     _slaveJVMUsed = true;
393 // Utilities.show("MainJVM.testSuiteStarted(" + numTests + ") called");
394
_interactionsModel.slaveJVMUsed();
395     _junitModel.testSuiteStarted(numTests);
396   }
397   
398   /** Called when a particular test is started. Forwards from the slave JVM to the local JUnit model.
399    * @param testName The name of the test being started.
400    */

401   public void testStarted(String JavaDoc testName) throws RemoteException {
402 // Utilities.show("MainJVM.testStarted(" + testName + ") called");
403
_slaveJVMUsed = true;
404 // Utilities.show("MainJVM.testStarted(" + testName + ") called");
405
_junitModel.testStarted(testName);
406   }
407   
408   /** Called when a particular test has ended. Forwards from the other JVM to the local JUnit model.
409    * @param testName The name of the test that has ended.
410    * @param wasSuccessful Whether the test passed or not.
411    * @param causedError If not successful, whether the test caused an error or simply failed.
412    */

413   public void testEnded(String JavaDoc testName, boolean wasSuccessful, boolean causedError) throws RemoteException {
414     _junitModel.testEnded(testName, wasSuccessful, causedError);
415   }
416   
417   /** Called when a full suite of tests has finished running. Forwards from the other JVM to the local JUnit model.
418    * @param errors The array of errors from all failed tests in the suite.
419    */

420   public void testSuiteEnded(JUnitError[] errors) throws RemoteException {
421 // Utilities.showDebug("MainJVM.testSuiteEnded() called");
422
_junitModel.testSuiteEnded(errors);
423   }
424   
425   /** Called when the JUnitTestManager wants to open a file that is not currently open.
426    * @param className the name of the class for which we want to find the file
427    * @return the file associated with the given class
428    */

429   public File getFileForClassName(String JavaDoc className) throws RemoteException {
430     return _junitModel.getFileForClassName(className);
431   }
432   
433   /** Notifies the main jvm that an assignment has been made in the given debug interpreter.
434    * Does not notify on declarations.
435    *
436    * This method is not currently necessary, since we don't copy back values in a debug interpreter until the thread
437    * has resumed.
438    *
439    * @param name the name of the debug interpreter
440    *
441    public void notifyDebugInterpreterAssignment(String name) {
442    }*/

443   
444   /**Accessor for the remote interface to the Interpreter JVM. */
445   private InterpreterJVMRemoteI _interpreterJVM() { return (InterpreterJVMRemoteI) getSlave(); }
446   
447 // /** Updates the security manager in slave JVM */
448
// public void enableSecurityManager() throws RemoteException {
449
// _interpreterJVM().enableSecurityManager();
450
// }
451
//
452
// /** Updates the security manager in slave JVM */
453
// public void disableSecurityManager() throws RemoteException{
454
// _interpreterJVM().disableSecurityManager();
455
// }
456

457   
458   /** Adds a named DynamicJavaAdapter to the list of interpreters.
459    * @param name the unique name for the interpreter
460    * @throws IllegalArgumentException if the name is not unique
461    */

462   public void addJavaInterpreter(String JavaDoc name) {
463     // silently fail if disabled. see killInterpreter docs for details.
464
if (! _restart) return;
465     
466     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
467     
468     try { slave.addJavaInterpreter(name); }
469     catch (RemoteException re) { _threwException(re); }
470   }
471   
472   /** Adds a named JavaDebugInterpreter to the list of interpreters.
473    * @param name the unique name for the interpreter
474    * @param className the fully qualified class name of the class the debug interpreter is in
475    * @throws IllegalArgumentException if the name is not unique
476    */

477   public void addDebugInterpreter(String JavaDoc name, String JavaDoc className) {
478     // silently fail if disabled. see killInterpreter docs for details.
479
if (! _restart) return;
480     
481     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
482     
483     try { slave.addDebugInterpreter(name, className); }
484     catch (RemoteException re) { _threwException(re); }
485   }
486   
487   /** Removes the interpreter with the given name, if it exists.
488    * @param name Name of the interpreter to remove
489    */

490   public void removeInterpreter(String JavaDoc name) {
491     // silently fail if disabled. see killInterpreter docs for details.
492
if (!_restart) return;
493     
494     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
495     
496     try {
497       slave.removeInterpreter(name);
498       if (name.equals(_currentInterpreterName)) _currentInterpreterName = null;
499     }
500     catch (RemoteException re) { _threwException(re); }
501   }
502   
503   /** Sets the current interpreter to the one specified by name
504    * @param name the unique name of the interpreter to set active
505    * @return Whether the new interpreter is currently processing an interaction (i.e., whether an interactionEnded
506    * event will be fired)
507    */

508   public boolean setActiveInterpreter(String JavaDoc name) {
509     // silently fail if disabled. see killInterpreter docs for details.
510
if (!_restart) return false;
511     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
512     
513     try {
514       boolean result = slave.setActiveInterpreter(name);
515       _currentInterpreterName = name;
516       return result;
517     }
518     catch (RemoteException re) {
519       _threwException(re);
520       return false;
521     }
522   }
523   
524   /** Sets the default interpreter to be the current one.
525    * @return Whether the new interpreter is currently in progress with an interaction (ie. whether an
526    * interactionEnded event will be fired)
527    */

528   public boolean setToDefaultInterpreter() {
529     // silently fail if disabled. see killInterpreter docs for details.
530
if (! _restart) return false;
531     
532     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
533     
534     try {
535       boolean result = slave.setToDefaultInterpreter();
536       _currentInterpreterName = DEFAULT_INTERPRETER_NAME;
537       return result;
538     }
539     catch (ConnectIOException ce) {
540       _log.log(this + "could not connect to the interpreterJVM after killing it. Threw " + ce);
541       return false;
542     }
543     catch (RemoteException re) {
544       _threwException(re);
545       return false;
546     }
547   }
548
549   /** Accesses the cached current interpreter name. */
550   public String JavaDoc getCurrentInterpreterName() { return _currentInterpreterName; }
551   
552   /** Kills the running interpreter JVM, and restarts with working directory wd if wd != null. If wd == null, the
553    * interpreter is not restarted.
554    * Note: If the interpreter is not restarted, all of the methods that delegate to the interpreter will
555    * silently fail! Therefore, killing without restarting should be used with extreme care and only in
556    * carefully controlled test cases or when DrJava is quitting anyway.
557    */

558
559   public void killInterpreter(File wd) {
560     synchronized(_masterJVMLock) {
561 // Utilities.showDebug("MainJVM: killInterpreter called with working directory = " + wd);
562
_workDir = wd;
563         _restart = (wd != null);
564         _cleanlyRestarting = true;
565         if (_restart) _interactionsModel.interpreterResetting();
566       }
567     /* Dropped lock before making remote call. */
568     try { quitSlave(); } // new slave JVM is restarted by call on startInterpreterJVM on death of current slave
569
catch (RemoteException e) {
570       _log.log(this + "could not connect to the interpreterJVM while trying to kill it. Threw " + e);
571     }
572   }
573   
574   /** Sets the classpath to use for starting the interpreter JVM. Must include the classes for the interpreter.
575    * @param classPath Classpath for the interpreter JVM
576    */

577   public void setStartupClassPath(String JavaDoc classPath) {
578     _startupClassPath = IOUtil.attemptCanonicalFiles(IOUtil.parsePath(classPath));
579   }
580   
581   /** Starts the interpreter if it's not running already. */
582   public void startInterpreterJVM() {
583     _log.log(this + ".startInterpreterJVM() called");
584 // synchronized(_masterJVMLock) { // synch is unnecessary
585
if (isStartupInProgress() || isInterpreterRunning()) return; // These predicates simply check volatile boolean flags
586
// }
587
// Pass assertion and debug port information as JVM arguments
588
ArrayList JavaDoc<String JavaDoc> jvmArgs = new ArrayList JavaDoc<String JavaDoc>();
589     if (allowAssertions()) jvmArgs.add("-ea");
590     int debugPort = getDebugPort();
591     _log.log("Main JVM starting with debug port: " + debugPort);
592     if (debugPort > -1) {
593       jvmArgs.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + debugPort);
594       jvmArgs.add("-Xdebug");
595       jvmArgs.add("-Xnoagent");
596       jvmArgs.add("-Djava.compiler=NONE");
597     }
598     // Cannot do the following line because it causes an error on Macs in the Eclipse plug-in.
599
// By instantiating the config, somehow the Apple JVM tries to start up AWT, which seems
600
// to be prohibited by Eclipse. Badness ensues.
601
// String optionArgString = DrJava.getConfig().getSetting(OptionConstants.JVM_ARGS);
602
// List<String> optionArgs = ArgumentTokenizer.tokenize(optionArgString);
603
jvmArgs.addAll(_optionArgs);
604     String JavaDoc[] jvmArgsArray = new String JavaDoc[jvmArgs.size()];
605     for (int i = 0; i < jvmArgs.size(); i++) { jvmArgsArray[i] = jvmArgs.get(i); }
606     
607     // Create and invoke the Interpreter JVM
608
try {
609      // _startupClasspath is sent in as the interactions classpath
610
// Utilities.show("Calling invokeSlave(" + jvmArgs + ", " + _startupClassPath + ", " + _workDir +")");
611
invokeSlave(jvmArgsArray, IOUtil.pathToString(_startupClassPath), _workDir);
612       _slaveJVMUsed = false;
613     }
614     catch (RemoteException re) { _threwException(re); }
615     catch (IOException ioe) { _threwException(ioe); }
616   }
617   
618   /** React if the slave JVM quits. Restarts the JVM unless _restart is false, and notifies the InteractionsModel
619    * if the quit was unexpected. Called from a thread within AbstractMasterJVM waiting for the death of the process
620    * that starts and runs the slave JVM.
621    * @param status Status returned by the dead process.
622    */

623   protected void handleSlaveQuit(int status) {
624     // Only restart the slave if _restart is true
625
// Utilities.showDebug("MainJVM: slaveJVM has quit with status " + status + " _workDir = " + _workDir +
626
// " _cleanlyRestarting = " + _cleanlyRestarting);
627
if (_restart) {
628       // We have already fired this event if we are cleanly restarting
629
if (! _cleanlyRestarting) _interactionsModel.interpreterResetting();
630 // Utilities.showDebug("MainJVM: calling startInterpreterJVM()");
631
startInterpreterJVM();
632     }
633     
634     if (!_cleanlyRestarting) _interactionsModel.replCalledSystemExit(status);
635     _cleanlyRestarting = false;
636   }
637   
638   /** Action to take if the slave JVM quits before registering. Assumes _masterJVMLock is held.
639    * @param status Status code of the JVM
640    * TODO: revise the unit tests that kill the slave prematurely (by making them wait until the
641    * slave registers) and remove the TEST_MODE escape.
642    */

643   protected void slaveQuitDuringStartup(int status) {
644     super.slaveQuitDuringStartup(status);
645     if (Utilities.TEST_MODE) return; // Some tests kill the slave immediately after it starts.
646

647 // // The slave JVM is not enabled after this.
648
// _restart = false;
649

650     // Signal that an internal error occurred
651
String JavaDoc msg = "Interpreter JVM exited before registering, status: " + status;
652     IllegalStateException JavaDoc e = new IllegalStateException JavaDoc(msg);
653     new edu.rice.cs.drjava.ui.DrJavaErrorHandler().handle(e);
654   }
655   
656   /** Called if the slave JVM dies before it is able to register.
657    * @param cause The Throwable which caused the slave to die.
658    */

659   public void errorStartingSlave(Throwable JavaDoc cause) throws RemoteException {
660     new edu.rice.cs.drjava.ui.DrJavaErrorHandler().handle(cause);
661   }
662   
663   /** This method is called by the interpreter JVM if it cannot be exited.
664    * @param th The Throwable thrown by System.exit
665    */

666   public void quitFailed(Throwable JavaDoc th) throws RemoteException {
667     _interactionsModel.interpreterResetFailed(th);
668     _cleanlyRestarting = false;
669   }
670   
671   /** Returns whether a JVM is currently starting. This override widens the visibility of the method. */
672   public boolean isStartupInProgress() { return super.isStartupInProgress(); }
673   
674   /** Called when Interpreter JVM connects to us after being started. Assumes that _masterJVMLock is already held. */
675   protected void handleSlaveConnected() {
676     // we reset the enabled flag since, unless told otherwise via
677
// killInterpreter(false), we want to automatically respawn
678
// System.out.println("handleSlaveConnected() called in MainJVM"); // DEBUG
679
_restart = true;
680     _cleanlyRestarting = false;
681     
682     Boolean JavaDoc allowAccess = DrJava.getConfig().getSetting(OptionConstants.ALLOW_PRIVATE_ACCESS);
683     setPrivateAccessible(allowAccess.booleanValue());
684     
685 // System.out.println("Calling interpreterReady(" + _workDir + ") called in MainJVM"); // DEBUG
686
_interactionsModel.interpreterReady(_workDir);
687     _junitModel.junitJVMReady();
688     
689     _log.log("Main JVM Thread for slave connection is: " + Thread.currentThread());
690     
691     // notify a thread that is waiting in ensureInterpreterConnected
692
synchronized(_interpreterLock) { _interpreterLock.notify(); }
693   }
694
695   
696   /** Returns the visitor to handle an InterpretResult. */
697   protected InterpretResultVisitor<Object JavaDoc> getResultHandler() { return _handler; }
698   
699   /** Returns the debug port to use, as specified by the model. Returns -1 if no usable port could be found. */
700   protected int getDebugPort() {
701     int port = -1;
702     try { port = _interactionsModel.getDebugPort(); }
703     catch (IOException ioe) {
704       /* Can't find port; don't use debugger */
705     }
706     return port;
707   }
708   
709   /** Return whether to allow assertions in the InterpreterJVM. */
710   protected boolean allowAssertions() {
711     String JavaDoc version = System.getProperty("java.version");
712     return (_allowAssertions && (version != null) && ("1.4.0".compareTo(version) <= 0));
713   }
714   
715   /** Lets the model know if any exceptions occur while communicating with the Interpreter JVM. */
716   private void _threwException(Throwable JavaDoc t) {
717     String JavaDoc shortMsg = null;
718     if ((t instanceof ParseError) && ((ParseError) t).getParseException() != null)
719       shortMsg = ((ParseError) t).getMessage(); // in this case, getMessage is equivalent to getShortMessage
720
_interactionsModel.replThrewException(t.getClass().getName(), t.getMessage(), StringOps.getStackTrace(t), shortMsg); ;
721   }
722   
723   /** Sets the interpreter to allow access to private members. TODO: synchronize? */
724   public void setPrivateAccessible(boolean allow) {
725     // silently fail if disabled. see killInterpreter docs for details.
726
if (!_restart) return;
727     
728     InterpreterJVMRemoteI slave = ensureInterpreterConnected();
729     try { slave.setPrivateAccessible(allow); }
730     catch (RemoteException re) { _threwException(re); }
731   }
732   
733   /** If an interpreter has not registered itself, this method will block until one does.*/
734   public InterpreterJVMRemoteI ensureInterpreterConnected() {
735 // _log.log("ensureInterpreterConnected called by Main JVM");
736
try {
737       synchronized(_interpreterLock) {
738         /* Now we silently fail if interpreter is disabled instead of throwing an exception. This situation
739          * occurs only in test cases and when DrJava is about to quit.
740          */

741         //if (! _restart) {
742
//throw new IllegalStateException("Interpreter is disabled");
743
//}
744
InterpreterJVMRemoteI slave = _interpreterJVM();
745         while (slave == null) {
746 // _log.log("interpreter is null in Main JVM, waiting for it to register");
747
_interpreterLock.wait();
748           slave = _interpreterJVM();
749         }
750         
751 // _log.log("interpreter " + interp + " registered in Main JVM");
752
return slave;
753       }
754     }
755     catch (InterruptedException JavaDoc ie) { throw new UnexpectedException(ie); }
756   }
757   
758   /**
759    * Asks the main jvm for input from the console.
760    * @return the console input
761    */

762   public String JavaDoc getConsoleInput() { return _interactionsModel.getConsoleInput(); }
763   
764   /**
765    * Peforms the appropriate action to return any type of result
766    * from a call to interpret back to the GlobalModel.
767    */

768   private class ResultHandler implements InterpretResultVisitor<Object JavaDoc> {
769     /**
770      * Lets the model know that void was returned.
771      * @return null
772      */

773     public Object JavaDoc forVoidResult(VoidResult that) {
774       _interactionsModel.replReturnedVoid();
775       return null;
776     }
777     
778     /** Returns a value result (as a String) back to the model.
779      * @return null
780      */

781     public Object JavaDoc forValueResult(ValueResult that) {
782       String JavaDoc result = that.getValueStr();
783       String JavaDoc style = that.getStyle();
784       _interactionsModel.replReturnedResult(result, style);
785       return null;
786     }
787     
788     /** Returns an exception back to the model.
789      * @return null
790      */

791     public Object JavaDoc forExceptionResult(ExceptionResult that) { /**/
792       _interactionsModel.replThrewException(that.getExceptionClass(), that.getExceptionMessage(), that.getStackTrace(),
793                                             that.getSpecialMessage());
794       return null;
795     }
796     
797     /** Indicates there was a syntax error to the model.
798      * @return null
799      */

800     public Object JavaDoc forSyntaxErrorResult(SyntaxErrorResult that) {
801       _interactionsModel.replReturnedSyntaxError(that.getErrorMessage(), that.getInteraction(), that.getStartRow(),
802                                                  that.getStartCol(), that.getEndRow(), that.getEndCol() );
803       return null;
804     }
805     
806     public Object JavaDoc forInterpreterBusy(InterpreterBusy that) {
807       throw new UnexpectedException("MainJVM.interpret() called when InterpreterJVM was busy!");
808     }
809   }
810   
811   /** InteractionsModel which does not react to events. */
812   public static class DummyInteractionsModel implements InteractionsModelCallback {
813     public int getDebugPort() throws IOException { return -1; }
814     public void replSystemOutPrint(String JavaDoc s) { }
815     public void replSystemErrPrint(String JavaDoc s) { }
816     public String JavaDoc getConsoleInput() {
817       throw new IllegalStateException JavaDoc("Cannot request input from dummy interactions model!");
818     }
819     public void setInputListener(InputListener il) {
820       throw new IllegalStateException JavaDoc("Cannot set the input listener of dummy interactions model!");
821     }
822     public void changeInputListener(InputListener from, InputListener to) {
823       throw new IllegalStateException JavaDoc("Cannot change the input listener of dummy interactions model!");
824     }
825     public void replReturnedVoid() { }
826     public void replReturnedResult(String JavaDoc result, String JavaDoc style) { }
827     public void replThrewException(String JavaDoc exceptionClass, String JavaDoc message, String JavaDoc stackTrace, String JavaDoc specialMessage) { }
828     public void replReturnedSyntaxError(String JavaDoc errorMessage, String JavaDoc interaction, int startRow, int startCol, int endRow,
829                                         int endCol) { }
830     public void replCalledSystemExit(int status) { }
831     public void interpreterResetting() { }
832     public void interpreterResetFailed(Throwable JavaDoc th) { }
833     public void interpreterReady(File wd) { }
834     public void slaveJVMUsed() { }
835   }
836   
837   /** JUnitModel which does not react to events. */
838   public static class DummyJUnitModel implements JUnitModelCallback {
839     public void nonTestCase(boolean isTestAll) { }
840     public void classFileError(ClassFileError e) { }
841     public void testSuiteStarted(int numTests) { }
842     public void testStarted(String JavaDoc testName) { }
843     public void testEnded(String JavaDoc testName, boolean wasSuccessful, boolean causedError) { }
844     public void testSuiteEnded(JUnitError[] errors) { }
845     public File getFileForClassName(String JavaDoc className) { return null; }
846     public ClassPathVector getClassPath() { return new ClassPathVector(); }
847     public void junitJVMReady() { }
848   }
849   
850   /** DebugModelCallback which does not react to events. */
851   public static class DummyDebugModel implements DebugModelCallback {
852     public void notifyDebugInterpreterAssignment(String JavaDoc name) {
853     }
854   }
855 }
856
Popular Tags