KickJava   Java API By Example, From Geeks To Geeks.

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


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.util.LinkedList JavaDoc;
37 import java.util.ListIterator JavaDoc;
38 import java.util.ArrayList JavaDoc;
39 import java.util.Hashtable JavaDoc;
40 import java.util.Enumeration JavaDoc;
41 import java.util.List JavaDoc;
42 import java.io.*;
43
44 import java.rmi.*;
45 import java.net.URL JavaDoc;
46 import java.net.MalformedURLException JavaDoc;
47
48
49 // NOTE: Do NOT import/use the config framework in this class!
50
// (This class runs in a different JVM, and will not share the config object)
51

52
53 import edu.rice.cs.util.ClassPathVector;
54 import edu.rice.cs.util.Log;
55 import edu.rice.cs.util.OutputStreamRedirector;
56 import edu.rice.cs.util.InputStreamRedirector;
57 import edu.rice.cs.util.StringOps;
58 import edu.rice.cs.util.UnexpectedException;
59 import edu.rice.cs.util.classloader.ClassFileError;
60 import edu.rice.cs.util.newjvm.*;
61
62 import edu.rice.cs.drjava.platform.PlatformFactory;
63 import edu.rice.cs.drjava.model.junit.JUnitModelCallback;
64 import edu.rice.cs.drjava.model.junit.JUnitTestManager;
65 import edu.rice.cs.drjava.model.junit.JUnitError;
66 import edu.rice.cs.drjava.model.repl.*;
67 import edu.rice.cs.drjava.model.ClassPathEntry;
68
69 // For Windows focus fix
70
import javax.swing.JDialog JavaDoc;
71
72 import koala.dynamicjava.parser.wrapper.*;
73 import koala.dynamicjava.parser.*;
74
75 /** This is the main class for the interpreter JVM. All public methods except those involving remote calls (callbacks)
76  * synchronized (unless synchronization has no effect). This class is loaded in the Interpreter JVM, not the Main JVM.
77  * (Do not use DrJava's config framework here.)
78  * <p>
79  * Note that this class is specific to DynamicJava. It must be refactored to accommodate other interpreters.
80  * @version $Id: InterpreterJVM.java 4033 2006-11-16 20:16:51Z rcartwright $
81  */

82 public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRemoteI, JUnitModelCallback {
83
84   /** Singleton instance of this class. */
85   public static final InterpreterJVM ONLY = new InterpreterJVM();
86   
87   private static final Log _log = new Log("MasterSlave.txt", false);
88   private static final boolean printMessages = false;
89   
90   /** String to append to error messages when no stack trace is available. */
91   public static final String JavaDoc EMPTY_TRACE_TEXT = "";
92     
93   /** Metadata encapsulating the default interpreter. */
94   private final InterpreterData _defaultInterpreter;
95   
96   /** Maps names to interpreters with metadata. */
97   private final Hashtable JavaDoc<String JavaDoc,InterpreterData> _interpreters;
98  
99   /** The currently accumulated classpath for all Java interpreters. List contains unqiue entries. */
100   private final ClassPathVector _classPath;
101   
102   /** Responsible for running JUnit tests in this JVM. */
103   private final JUnitTestManager _junitTestManager;
104   
105   /** manages the classpath for all of DrJava */
106   private final ClassPathManager _classPathManager;
107   
108   /** Remote reference to the MainJVM class in DrJava's primary JVM. Assigned ONLY once. */
109   private volatile MainJVMRemoteI _mainJVM;
110
111   /** The current interpreter. */
112   private volatile InterpreterData _activeInterpreter;
113   
114 // /** Busy flag. Used to prevent multiple interpretations from running simultaneously. */
115
// private volatile boolean interpretationInProgress = false;
116

117   /** Interactions processor, currently a pre-processor **/
118   // private InteractionsProcessorI _interactionsProcessor;
119

120   /** Whether to display an error message if a reset fails. */
121   private volatile boolean _messageOnResetFailure;
122   
123   /** Private constructor; use the singleton ONLY instance. */
124   private InterpreterJVM() {
125
126     _classPath = new ClassPathVector();
127     _classPathManager = new ClassPathManager();
128     _defaultInterpreter = new InterpreterData(new DynamicJavaAdapter(_classPathManager));
129     _interpreters = new Hashtable JavaDoc<String JavaDoc,InterpreterData>();
130     _junitTestManager = new JUnitTestManager(this);
131     _messageOnResetFailure = true;
132     
133     // _interactionsProcessor = new InteractionsProcessor();
134

135     _quitSlaveThreadName = "Reset Interactions Thread";
136     _pollMasterThreadName = "Poll DrJava Thread";
137     _activeInterpreter = _defaultInterpreter;
138     
139     try { _activeInterpreter.getInterpreter().interpret("0"); }
140     catch (ExceptionReturnedException e) { throw new edu.rice.cs.util.UnexpectedException(e); }
141   }
142
143   private static void _dialog(String JavaDoc s) {
144     //javax.swing.JOptionPane.showMessageDialog(null, s);
145
_log.log(s);
146   }
147   
148   /** Actions to perform when this JVM is started (through its superclass, AbstractSlaveJVM). Contract from superclass
149    * mandates that this code does not synchronized on this across a remote call. This method has no synchronization
150    * because it can only be called once (part of the superclass contract) and _mainJVM is only assigned (once!) here. */

151   protected void handleStart(MasterRemote mainJVM) {
152     //_dialog("handleStart");
153
_mainJVM = (MainJVMRemoteI) mainJVM;
154     
155     // redirect stdin
156
System.setIn(new InputStreamRedirector() {
157       protected String JavaDoc _getInput() { // NOT synchronized on InterpreterJVM.this. _mainJVM is immutable.
158
try { return _mainJVM.getConsoleInput(); }
159         catch(RemoteException re) {
160           // blow up if no MainJVM found
161
_log.log("System.in: " + re.toString());
162           throw new IllegalStateException JavaDoc("Main JVM can't be reached for input.\n" + re);
163         }
164       }
165     });
166     
167     // redirect stdout
168
System.setOut(new PrintStream(new OutputStreamRedirector() {
169       public void print(String JavaDoc s) { // NOT synchronized on InterpreterJVM.this. _mainJVM is immutable.
170
try {
171           //_log.logTime("out.print: " + s);
172
_mainJVM.systemOutPrint(s);
173         }
174         catch (RemoteException re) {
175           // nothing to do
176
_log.log("System.out: " + re.toString());
177         }
178       }
179     }));
180     
181     // redirect stderr
182
System.setErr(new PrintStream(new OutputStreamRedirector() {
183       public void print(String JavaDoc s) { // NOT synchronized on InterpreterJVM.this. _mainJVM is immutable.
184
try {
185           //_log.logTime("err.print: " + s);
186
_mainJVM.systemErrPrint(s);
187         }
188         catch (RemoteException re) {
189           // nothing to do
190
_log.log("System.err: " + re.toString());
191         }
192       }
193     }));
194     
195     /* On Windows, any frame or dialog opened from Interactions pane will appear *behind* DrJava's frame, unless a
196      * previous frame or dialog is shown here. Not sure what the difference is, but this hack seems to work. (I'd
197      * be happy to find a better solution, though.) Only necessary on Windows, since frames and dialogs on other
198      * platforms appear correctly in front of DrJava. */

199     if (PlatformFactory.ONLY.isWindowsPlatform()) {
200       JDialog JavaDoc d = new JDialog JavaDoc();
201       d.setSize(0,0);
202       d.setVisible(true);
203       d.setVisible(false);
204     }
205     //_dialog("interpreter JVM started");
206
}
207
208   /** Interprets the given string of source code in the active interpreter. The result is returned to MainJVM via
209    * the interpretResult method.
210    * @param s Source code to interpret.
211    */

212   public void interpret(String JavaDoc s) { interpret(s, _activeInterpreter); }
213   
214   /** Interprets the given string of source code with the given interpreter. The result is returned to MainJVM via
215    * the interpretResult method.
216    * @param s Source code to interpret.
217    * @param interpreterName Name of the interpreter to use
218    * @throws IllegalArgumentException if the named interpreter does not exist
219    */

220   public void interpret(String JavaDoc s, String JavaDoc interpreterName) { interpret(s, getInterpreter(interpreterName)); }
221   
222   /** Interprets the given string of source code with the given interpreter. The result is returned to MainJVM via
223    * the interpretResult method. Not synchronized on this!
224    * @param input Source code to interpret.
225    * @param interpreter The interpreter (plus metadata) to use
226    */

227   public void interpret(final String JavaDoc input, final InterpreterData interpreter) {
228     _log.log(this + ".interpret(" + input + ") called");
229     try {
230       synchronized(interpreter) {
231         if (interpreter.inProgress()) {
232             _mainJVM.interpretResult(new InterpreterBusy());
233           return;
234         }
235 // interpretationInProgress = true;
236
interpreter.setInProgress(true); // records that a given interpreter is in progress (used by debugger?)
237
}
238       // The following code is NOT synchronized on this. Mutual exclusion is guaranteed by preceding synchronized block.
239
// Utilities.showDebug("InterpreterJVM.interpret(" + input + ", ...) called");
240
Thread JavaDoc thread = new Thread JavaDoc("interpret thread: " + input) {
241         public void run() {
242           String JavaDoc s = input;
243           try { // Delimiting a catch for RemoteExceptions that might be thrown in catch clauses of enclosed try
244
try {
245               _log.log("Interpreter thread for " + input + " has started");
246 // _dialog("to interp: " + s);
247

248 // Utilities.showDebug("Preparing to invoke interpret method on " + s);
249
Object JavaDoc result = interpreter.getInterpreter().interpret(s);
250               String JavaDoc resultString = String.valueOf(result);
251 // Utilities.showDebug("Result string is: " + resultString);
252

253               if (result == Interpreter.NO_RESULT) {
254                 //return new VoidResult();
255
//_dialog("void interp ret: " + resultString);
256
_mainJVM.interpretResult(new VoidResult());
257               }
258               else {
259                 // we use String.valueOf because it deals with result = null!
260
//_dialog("about to tell main result was " + resultString);
261
//return new ValueResult(resultString);
262
String JavaDoc style = InteractionsDocument.OBJECT_RETURN_STYLE;
263                 if (result instanceof String JavaDoc) {
264                   style = InteractionsDocument.STRING_RETURN_STYLE;
265                   //Single quotes have already been added to chars by now, so they are read as strings
266
String JavaDoc possibleChar = (String JavaDoc)result;
267                   
268                   if (possibleChar.startsWith("\'") && possibleChar.endsWith("\'") && possibleChar.length()==3)
269                     style = InteractionsDocument.CHARACTER_RETURN_STYLE;
270                 }
271                 if (result instanceof Number JavaDoc) style = InteractionsDocument.NUMBER_RETURN_STYLE;
272                 _mainJVM.interpretResult(new ValueResult(resultString, style));
273               }
274             }
275             catch (ExceptionReturnedException e) {
276               Throwable JavaDoc t = e.getContainedException();
277 // Utilities.showStackTrace(t);
278
_dialog("interp exception: " + t);
279               // TODO: replace the following if ladder by dynamic dispatch. Create a visitor for DynamicJava errors?
280
if (t instanceof ParseException)
281                 _mainJVM.interpretResult(new SyntaxErrorResult((ParseException) t, input));
282               else if (t instanceof TokenMgrError)
283                 _mainJVM.interpretResult(new SyntaxErrorResult((TokenMgrError) t, input));
284               else if (t instanceof ParseError)
285                 _mainJVM.interpretResult(new SyntaxErrorResult((ParseError) t, input));
286               else {
287                 //Other exceptions are non lexical/parse related exceptions. These include arithmetic exceptions,
288
//wrong version exceptions, etc.
289

290                 _mainJVM.interpretResult(new ExceptionResult(t.getClass().getName(), t.getMessage(),
291                                                              InterpreterJVM.getStackTrace(t), null));
292               }
293             }
294             catch (Throwable JavaDoc t) {
295               // A user's toString method might throw anything, so we need to be careful
296
_dialog("irregular interp exception: " + t);
297 // Utilities.showStackTrace(t);
298
String JavaDoc shortMsg = null;
299               if ((t instanceof ParseError) && ((ParseError) t).getParseException() != null)
300                 shortMsg = ((ParseError) t).getMessage(); // in this case, getMessage is equivalent to getShortMessage
301
_mainJVM.interpretResult(new ExceptionResult(t.getClass().getName(), t.getMessage(),
302                                                            InterpreterJVM.getStackTrace(t), shortMsg));
303             }
304           }
305           catch(RemoteException re) { /* MainJVM no longer accessible. Cannot recover. */
306             _log.log("MainJVM.interpret threw " + re.toString());
307           }
308         }
309       }; // end of Thread definition
310

311       thread.setDaemon(true);
312       thread.start();
313     } // end of interpretation block including synchronized prelude
314
catch(RemoteException re) { /* MainJVM not accessible. Cannot recover. */
315       _log.log("MainJVM.interpret threw" + re.toString());
316     }
317     finally { // fields are volatile so no synchronization is necessary
318
// interpretationInProgress = false;
319
interpreter.setInProgress(false);
320     }
321   }
322         
323   private static String JavaDoc _processReturnValue(Object JavaDoc o) {
324     if (o instanceof String JavaDoc) return "\"" + o + "\"";
325     if (o instanceof Character JavaDoc) return "'" + o + "'";
326     return o.toString();
327   }
328   
329   /** Gets the string representation of the value of a variable in the current interpreter.
330    * @param var the name of the variable
331    * @return null if the variable is not defined, "null" if the value is null, or else its string representation
332    */

333   public synchronized String JavaDoc getVariableToString(String JavaDoc var) throws RemoteException {
334     // Add to the default interpreter, if it is a JavaInterpreter
335
Interpreter i = _activeInterpreter.getInterpreter();
336     if (i instanceof JavaInterpreter) {
337       try {
338         Object JavaDoc value = ((JavaInterpreter)i).getVariable(var);
339         if (value == null) return "null";
340         if (value instanceof koala.dynamicjava.interpreter.UninitializedObject) return null;
341         return _processReturnValue(value);
342       }
343       catch (IllegalStateException JavaDoc e) { return null; } // variable was not defined
344
}
345     return null;
346   }
347   
348   /** Gets the class name of a variable in the current interpreter.
349    * @param var the name of the variable
350    */

351   public synchronized String JavaDoc getVariableClassName(String JavaDoc var) throws RemoteException {
352     // Add to the default interpreter, if it is a JavaInterpreter
353
Interpreter i = _activeInterpreter.getInterpreter();
354     if (i instanceof JavaInterpreter) {
355       try {
356         Class JavaDoc c = ((JavaInterpreter)i).getVariableClass(var);
357         if (c == null) return "null";
358         else return c.getName();
359       }
360       catch (IllegalStateException JavaDoc e) {
361         // variable was not defined
362
return null;
363       }
364     }
365     else return null;
366   }
367   
368   /** Adds a named DynamicJavaAdapter to list of interpreters. Presets it to contain the current accumulated classpath.
369    * @param name the unique name for the interpreter
370    * @throws IllegalArgumentException if the name is not unique
371    */

372   public synchronized void addJavaInterpreter(String JavaDoc name) {
373     JavaInterpreter interpreter = new DynamicJavaAdapter(_classPathManager);
374     // Add each entry on the accumulated classpath
375
_updateInterpreterClassPath(interpreter);
376     addInterpreter(name, interpreter);
377   }
378   
379   /** Adds a named JavaDebugInterpreter to the list of interpreters.
380    * @param name the unique name for the interpreter
381    * @param className the fully qualified class name of the class the debug interpreter is in
382    * @throws IllegalArgumentException if the name is not unique
383    */

384   public synchronized void addDebugInterpreter(String JavaDoc name, String JavaDoc className) {
385     JavaDebugInterpreter interpreter = new JavaDebugInterpreter(name, className);
386     interpreter.setPrivateAccessible(true);
387     // Add each entry on the accumulated classpath
388
_updateInterpreterClassPath(interpreter);
389     addInterpreter(name, interpreter);
390   }
391   
392   /** Adds a named interpreter to the list of interpreters.
393    * @param name the unique name for the interpreter
394    * @param interpreter the interpreter to add
395    * @throws IllegalArgumentException if the name is not unique
396    */

397   public synchronized void addInterpreter(String JavaDoc name, Interpreter interpreter) {
398     if (_interpreters.containsKey(name)) {
399       throw new IllegalArgumentException JavaDoc("'" + name + "' is not a unique interpreter name");
400     }
401     _interpreters.put(name, new InterpreterData(interpreter));
402   }
403   
404   /** Removes the interpreter with the given name, if it exists. Unsynchronized because _interpreters is immutable
405    * and its methods are thread-safe.
406    * @param name Name of the interpreter to remove
407    */

408   public void removeInterpreter(String JavaDoc name) { _interpreters.remove(name); }
409   
410   /** Returns the interpreter (with metadata) with the given name
411    * @param name the unique name of the desired interpreter
412    * @throws IllegalArgumentException if no such named interpreter exists
413    */

414   InterpreterData getInterpreter(String JavaDoc name) {
415     InterpreterData interpreter = _interpreters.get(name);
416     if (interpreter != null) return interpreter;
417     else throw new IllegalArgumentException JavaDoc("Interpreter '" + name + "' does not exist.");
418   }
419   
420   /** Returns the Java interpreter with the given name
421    * @param name the unique name of the desired interpreter
422    * @throws IllegalArgumentException if no such named interpreter exists, or if the named interpreter is not a Java
423    * interpreter
424    */

425   public synchronized JavaInterpreter getJavaInterpreter(String JavaDoc name) {
426     if (printMessages) System.out.println("Getting interpreter data");
427     InterpreterData interpreterData = getInterpreter(name);
428     if (printMessages) System.out.println("Getting interpreter instance");
429     Interpreter interpreter = interpreterData.getInterpreter();
430     if (printMessages) System.out.println("returning");
431     
432     if (interpreter instanceof JavaInterpreter) return (JavaInterpreter) interpreter;
433     else {
434       throw new IllegalArgumentException JavaDoc("Interpreter '" + name + "' is not a JavaInterpreter.");
435     }
436   }
437   
438   
439   /** Sets the current interpreter to be the one specified by the given name
440    * @param name the unique name of the interpreter to set active
441    * @return Whether the new interpreter is currently in progress with an interaction
442    */

443   public synchronized boolean setActiveInterpreter(String JavaDoc name) {
444     _activeInterpreter = getInterpreter(name);
445     return _activeInterpreter.inProgress();
446   }
447   
448   /** Sets the default interpreter to be active.
449    * @return Whether the new interpreter is currently in progress with an interaction
450    */

451   public synchronized boolean setToDefaultInterpreter() {
452     _activeInterpreter = _defaultInterpreter;
453     return _activeInterpreter.inProgress();
454   }
455   
456   /** Gets the hashtable containing the named interpreters. Package private for testing purposes.
457    * @return said hashtable
458    */

459   Hashtable JavaDoc<String JavaDoc,InterpreterData> getInterpreters() { return _interpreters; }
460   
461   /** Returns the current active interpreter. Package private; for tests only. */
462   Interpreter getActiveInterpreter() { return _activeInterpreter.getInterpreter(); }
463   
464   /** Gets the stack trace from the given exception, stripping off the bottom parts of the trace that are internal
465    * to the interpreter. This would be much easier to do in JDK 1.4, since you can get the stack trace frames
466    * directly, instead of having to parse this! TODO: revise this code to use the JDK 1.4+ API.
467    */

468   public static String JavaDoc getStackTrace(Throwable JavaDoc t) {
469     //_dialog("before creating reader");
470
BufferedReader reader = new BufferedReader(new StringReader(StringOps.getStackTrace(t)));
471     
472     //_dialog("after creating reader");
473
LinkedList JavaDoc<String JavaDoc> traceItems = new LinkedList JavaDoc<String JavaDoc>();
474     try {
475       // we will generate list of trace items
476
// skip the first one since it's just the message
477
//_dialog("before first readLine");
478
reader.readLine();
479       //_dialog("after first readLine");
480

481       String JavaDoc s;
482       while ((s = reader.readLine()) != null) {
483         //_dialog("read: " + s);
484
traceItems.add(s);
485       }
486     }
487     catch (IOException ioe) {
488       return "Unable to get stack trace";
489     }
490     
491     // OK, now we crop off everything after the first "koala.dynamicjava." or "edu.rice.cs.drjava.", if there is one.
492

493     // First, find the index of an occurrence.
494
int index = -1;
495     for (int i = 0; i < traceItems.size(); i++) {
496       String JavaDoc item = traceItems.get(i);
497       item = item.trim();
498       if (item.startsWith("at edu.rice.cs.drjava.") || item.startsWith("at koala.dynamicjava.")) {
499         index = i;
500         break;
501       }
502     }
503     
504     // Now crop off the rest
505
if (index > -1) {
506       while (traceItems.size() > index) traceItems.removeLast();
507     }
508     
509     // Last check: See if there are no items left. If there are none, put one in to say it happened at top-level.
510
if (traceItems.isEmpty()) traceItems.add(EMPTY_TRACE_TEXT);
511     
512     // OK, now rebuild string
513
final StringBuilder JavaDoc buf = new StringBuilder JavaDoc();
514     final ListIterator JavaDoc itor = traceItems.listIterator();
515     final String JavaDoc newLine = System.getProperty("line.separator");
516     boolean first = true;
517     while (itor.hasNext()) {
518       if (first) first = false; else buf.append(newLine);
519
520       buf.append(" " + ((String JavaDoc) itor.next()).trim());
521     }
522     
523     return buf.toString();
524   }
525   
526   // ---------- Java-specific methods ----------
527

528   /** Sets the package scope for the current active interpreter, if it is a JavaInterpreter. */
529   public void setPackageScope(String JavaDoc s) {
530     Interpreter active = _activeInterpreter.getInterpreter();
531     if (active instanceof JavaInterpreter) {
532       ((JavaInterpreter)active).setPackageScope(s);
533     }
534   }
535   
536   /** @param show Whether to show a message if a reset operation fails. */
537   public void setShowMessageOnResetFailure(boolean show) { _messageOnResetFailure = show; }
538   
539   /** This method is called if the interpreterJVM cannot be exited (likely because of a modified security manager. */
540   protected void quitFailed(Throwable JavaDoc th) { // NOT synchronized
541
if (_messageOnResetFailure) {
542       String JavaDoc msg = "The interactions pane could not be reset:\n" + th;
543       javax.swing.JOptionPane.showMessageDialog(null, msg);
544     }
545     
546     try { _mainJVM.quitFailed(th); }
547     catch (RemoteException re) {
548       // nothing to do
549
_log.log("quitFailed: " + re.toString());
550     }
551   }
552   
553   /** Sets the interpreter to allow access to private members. */
554   public synchronized void setPrivateAccessible(boolean allow) {
555     Interpreter active = _activeInterpreter.getInterpreter();
556     if (active instanceof JavaInterpreter) {
557       ((JavaInterpreter)active).setPrivateAccessible(allow);
558     }
559   }
560   
561   // ---------- JUnit methods ----------
562
/** Sets up a JUnit test suite in the Interpreter JVM and finds which classes are really TestCases classes (by
563    * loading them). Unsynchronized because it contains a remote call and does not involve mutable local state.
564    * @param classNames the class names to run in a test
565    * @param files the associated file
566    * @return the class names that are actually test cases
567    */

568   public List JavaDoc<String JavaDoc> findTestClasses(List JavaDoc<String JavaDoc> classNames, List JavaDoc<File> files) throws RemoteException {
569     // new ScrollableDialog(null, "InterpterJVM.findTestClasses invoked", "", "").show();
570
return _junitTestManager.findTestClasses(classNames, files);
571   }
572   
573   /** Runs JUnit test suite already cached in the Interpreter JVM. Unsynchronized because it contains a remote call
574    * and does not involve mutable local state.
575    * @return false if no test suite is cached; true otherwise
576    */

577   public boolean runTestSuite() throws RemoteException {
578     // new ScrollableDialog(null, "InterpreterJVM.runTestSuite() called!", "", "").show();
579
return _junitTestManager.runTestSuite();
580   }
581   
582   /** Notifies Main JVM that JUnit has been invoked on a non TestCase class. Unsynchronized because it contains a
583    * remote call and does not involve mutable local state.
584    * @param isTestAll whether or not it was a use of the test all button
585    */

586   public void nonTestCase(boolean isTestAll) {
587     try { _mainJVM.nonTestCase(isTestAll); }
588     catch (RemoteException re) {
589       // nothing to do
590
_log.log("nonTestCase: " + re.toString());
591     }
592   }
593   
594   /** Notifies the main JVM that JUnitTestManager has encountered an illegal class file. Unsynchronized because it
595    * contains a remote call and does not involve mutable local state.
596    * @param e the ClassFileError object describing the error on loading the file
597    */

598   public void classFileError(ClassFileError e) {
599     try { _mainJVM.classFileError(e); }
600     catch (RemoteException re) {
601       // nothing to do
602
_log.log("classFileError: " + re.toString());
603     }
604   }
605   
606   /** Notifies that a suite of tests has started running. Unsynchronized because it contains a remote call and does
607    * not involve mutable local state.
608    * @param numTests The number of tests in the suite to be run.
609    */

610   public void testSuiteStarted(int numTests) {
611     try { _mainJVM.testSuiteStarted(numTests); }
612     catch (RemoteException re) {
613       // nothing to do
614
_log.log("testSuiteStarted: " + re.toString());
615     }
616   }
617   
618   /** Notifies that a particular test has started. Unsynchronized because it contains a remote call and does not
619    * involve mutable local state.
620    * @param testName The name of the test being started.
621    */

622   public void testStarted(String JavaDoc testName) {
623     try { _mainJVM.testStarted(testName); }
624     catch (RemoteException re) {
625       // nothing to do
626
_log.log("testStarted" + re.toString());
627     }
628   }
629   
630   /** Notifies that a particular test has ended. Unsynchronized because it contains a remote call.
631    * @param testName The name of the test that has ended.
632    * @param wasSuccessful Whether the test passed or not.
633    * @param causedError If not successful, whether the test caused an error or simply failed.
634    */

635   public void testEnded(String JavaDoc testName, boolean wasSuccessful, boolean causedError) {
636     try { _mainJVM.testEnded(testName, wasSuccessful, causedError); }
637     catch (RemoteException re) {
638       // nothing to do
639
_log.log("testEnded: " + re.toString());
640     }
641   }
642   
643   /** Notifies that a full suite of tests has finished running. Unsynchronized because it contains a remote call
644    * and does not involve mutable local state.
645    * @param errors The array of errors from all failed tests in the suite.
646    */

647   public void testSuiteEnded(JUnitError[] errors) {
648     try { _mainJVM.testSuiteEnded(errors); }
649     catch (RemoteException re) {
650       // nothing to do
651
_log.log("testSuiteFinished: " + re.toString());
652     }
653   }
654   
655   /** Called when the JUnitTestManager wants to open a file that is not currently open. Unsynchronized because it
656    * contains a remote call and does not involve mutable local state.
657    * @param className the name of the class for which we want to find the file
658    * @return the file associated with the given class
659    */

660   public File getFileForClassName(String JavaDoc className) {
661     try { return _mainJVM.getFileForClassName(className); }
662     catch (RemoteException re) {
663       // nothing to do
664
_log.log("getFileForClassName: " + re.toString());
665       return null;
666     }
667   }
668   
669   public void junitJVMReady() { }
670   
671   //////////////////////////////////////////////////////////////
672
// ALL functions regarding classpath
673
//////////////////////////////////////////////////////////////
674

675   /** Adds a classpath to the given interpreter. assumes that lock on this is held.
676    * @param interpreter the interpreter
677    */

678   protected /* synchronized */ void _updateInterpreterClassPath(JavaInterpreter interpreter) {
679     
680     for (ClassPathEntry e: _classPathManager.getProjectCP())
681       interpreter.addProjectClassPath(e.getEntry());
682     
683     for (ClassPathEntry e: _classPathManager.getBuildDirectoryCP())
684       interpreter.addBuildDirectoryClassPath(e.getEntry());
685     
686     for (ClassPathEntry e: _classPathManager.getProjectFilesCP())
687       interpreter.addProjectFilesClassPath(e.getEntry());
688     
689     for (ClassPathEntry e: _classPathManager.getExternalFilesCP())
690       interpreter.addExternalFilesClassPath(e.getEntry());
691     
692     for (ClassPathEntry e: _classPathManager.getExtraCP())
693       interpreter.addExtraClassPath(e.getEntry());
694   }
695   
696   /** Adds the given path to the classpath shared by ALL Java interpreters. This method <b>cannot</b> take multiple
697    * paths separated by a path separator; it must be called separately for each path. Only unique paths are added.
698    * @param s Entry to add to the accumulated classpath
699    */

700   public synchronized void addExtraClassPath(URL JavaDoc s) {
701     //_dialog("add classpath: " + s);
702
if (_classPath.contains(s)) return; // Don't add it again
703

704     // Add to the default interpreter, if it is a JavaInterpreter
705
if (_defaultInterpreter.getInterpreter() instanceof JavaInterpreter) {
706       ((JavaInterpreter)_defaultInterpreter.getInterpreter()).addExtraClassPath(s);
707     }
708     
709     // Add to any named JavaInterpreters to be consistent
710
Enumeration JavaDoc<InterpreterData> interpreters = _interpreters.elements();
711     while (interpreters.hasMoreElements()) {
712       Interpreter interpreter = interpreters.nextElement().getInterpreter();
713       if (interpreter instanceof JavaInterpreter) {
714         ((JavaInterpreter)interpreter).addExtraClassPath(s);
715       }
716     }
717     
718     // Keep this entry on the accumulated classpath
719
_classPath.add(s);
720   }
721  
722   /** Adds the given path to the classpath shared by ALL Java interpreters. This method <b>cannot</b> take multiple
723    * paths separated by a path separator; it must be called separately for each path. Only unique paths are added.
724    * @param s Entry to add to the accumulated classpath
725    */

726   public synchronized void addProjectClassPath(URL JavaDoc s) {
727     //_dialog("add classpath: " + s);
728
if (_classPath.contains(s)) return; // Don't add it again
729

730     // Add to the default interpreter, if it is a JavaInterpreter
731
if (_defaultInterpreter.getInterpreter() instanceof JavaInterpreter) {
732       ((JavaInterpreter)_defaultInterpreter.getInterpreter()).addProjectClassPath(s);
733     }
734     
735     // Add to any named JavaInterpreters to be consistent
736
Enumeration JavaDoc<InterpreterData> interpreters = _interpreters.elements();
737     while (interpreters.hasMoreElements()) {
738       Interpreter interpreter = interpreters.nextElement().getInterpreter();
739       if (interpreter instanceof JavaInterpreter) {
740         ((JavaInterpreter)interpreter).addProjectClassPath(s);
741       }
742     }
743     
744     // Keep this entry on the accumulated classpath
745
_classPath.add(s);
746   }
747  
748   /** Adds the given path to the classpath shared by ALL Java interpreters. This method <b>cannot</b> take multiple
749    * paths separated by a path separator; it must be called separately for each path. Only unique paths are added.
750    * @param s Entry to add to the accumulated classpath
751    */

752   public synchronized void addBuildDirectoryClassPath(URL JavaDoc s) {
753     //_dialog("add classpath: " + s);
754
if (_classPath.contains(s)) return; // Don't add it again
755

756     // Add to the default interpreter, if it is a JavaInterpreter
757
if (_defaultInterpreter.getInterpreter() instanceof JavaInterpreter) {
758       ((JavaInterpreter)_defaultInterpreter.getInterpreter()).addBuildDirectoryClassPath(s);
759     }
760     
761     // Add to any named JavaInterpreters to be consistent
762
Enumeration JavaDoc<InterpreterData> interpreters = _interpreters.elements();
763     while (interpreters.hasMoreElements()) {
764       Interpreter interpreter = interpreters.nextElement().getInterpreter();
765       if (interpreter instanceof JavaInterpreter) {
766         ((JavaInterpreter)interpreter).addBuildDirectoryClassPath(s);
767       }
768     }
769     
770     // Keep this entry on the accumulated classpath
771
_classPath.add(s);
772   }
773   
774  
775   /** Adds the given path to the classpath shared by ALL Java interpreters. This method <b>cannot</b> take multiple
776    * paths separated by a path separator; it must be called separately for each path. Only unique paths are added.
777    * @param s Entry to add to the accumulated classpath
778    */

779   public synchronized void addProjectFilesClassPath(URL JavaDoc s) {
780     //_dialog("add classpath: " + s);
781
if (_classPath.contains(s)) return; // Don't add it again
782

783     // Add to the default interpreter, if it is a JavaInterpreter
784
if (_defaultInterpreter.getInterpreter() instanceof JavaInterpreter) {
785       ((JavaInterpreter)_defaultInterpreter.getInterpreter()).addProjectFilesClassPath(s);
786     }
787     
788     // Add to any named JavaInterpreters to be consistent
789
Enumeration JavaDoc<InterpreterData> interpreters = _interpreters.elements();
790     while (interpreters.hasMoreElements()) {
791       Interpreter interpreter = interpreters.nextElement().getInterpreter();
792       if (interpreter instanceof JavaInterpreter) {
793         ((JavaInterpreter)interpreter).addProjectFilesClassPath(s);
794       }
795     }
796     
797     // Keep this entry on the accumulated classpath
798
_classPath.add(s);
799   }
800  
801   /** Adds the given path to the classpath shared by ALL Java interpreters. This method <b>cannot</b> take multiple
802    * paths separated by a path separator; it must be called separately for each path. Only unique paths are added.
803    * @param s Entry to add to the accumulated classpath
804    */

805   public synchronized void addExternalFilesClassPath(URL JavaDoc s) {
806     //_dialog("add classpath: " + s);
807
if (_classPath.contains(s)) return; // Don't add it again
808

809     // Add to the default interpreter, if it is a JavaInterpreter
810
if (_defaultInterpreter.getInterpreter() instanceof JavaInterpreter) {
811       ((JavaInterpreter)_defaultInterpreter.getInterpreter()).addExternalFilesClassPath(s);
812     }
813     
814     // Add to any named JavaInterpreters to be consistent
815
Enumeration JavaDoc<InterpreterData> interpreters = _interpreters.elements();
816     while (interpreters.hasMoreElements()) {
817       Interpreter interpreter = interpreters.nextElement().getInterpreter();
818       if (interpreter instanceof JavaInterpreter) {
819         ((JavaInterpreter)interpreter).addExternalFilesClassPath(s);
820       }
821     }
822     
823     // Keep this entry on the accumulated classpath
824
_classPath.add(s);
825   }
826   
827   /** Returns a copy of the list of unique entries on the classpath.
828    * @return a vector of strings so that RMI doesn't have to serialize the URL object. Serializing URL objects fails
829    * when using jsr14.
830    */

831   public synchronized ClassPathVector getAugmentedClassPath() {
832     ClassPathVector ret = new ClassPathVector();
833
834     for (ClassPathEntry e: _classPathManager.getProjectCP()) ret.add(e.getEntry().toString());
835
836     for (ClassPathEntry e: _classPathManager.getBuildDirectoryCP())
837       ret.add(e.getEntry().toString());
838     
839     for (ClassPathEntry e: _classPathManager.getProjectFilesCP())
840       ret.add(e.getEntry().toString());
841
842     for (ClassPathEntry e: _classPathManager.getExternalFilesCP())
843       ret.add(e.getEntry().toString());
844
845     for (ClassPathEntry e: _classPathManager.getExtraCP())
846       ret.add(e.getEntry().toString());
847
848     return ret;
849   }
850   
851   //// The following methods convert strings received
852
//// from RMI to URL objects since URL objects cannot
853
//// be successfully serialized when using JSR14.
854

855   public void addExtraClassPath(String JavaDoc s) {
856     try { addExtraClassPath(new URL JavaDoc(s)); }
857     catch(MalformedURLException JavaDoc e) { throw new edu.rice.cs.util.UnexpectedException(e); }
858   }
859   
860   public void addProjectClassPath(String JavaDoc s) {
861     try { addProjectClassPath(new URL JavaDoc(s)); }
862     catch(MalformedURLException JavaDoc e) { throw new edu.rice.cs.util.UnexpectedException(e); }
863   }
864   
865   public void addBuildDirectoryClassPath(String JavaDoc s) {
866     try { addBuildDirectoryClassPath(new URL JavaDoc(s)); }
867     catch(MalformedURLException JavaDoc e) { throw new edu.rice.cs.util.UnexpectedException(e); }
868   }
869   
870   public void addProjectFilesClassPath(String JavaDoc s) {
871     try { addProjectFilesClassPath(new URL JavaDoc(s)); }
872     catch(MalformedURLException JavaDoc e) { throw new edu.rice.cs.util.UnexpectedException(e); }
873   }
874   
875   public void addExternalFilesClassPath(String JavaDoc s) {
876     try { addExternalFilesClassPath(new URL JavaDoc(s)); }
877     catch(MalformedURLException JavaDoc e) { throw new edu.rice.cs.util.UnexpectedException(e); }
878   }
879   
880   /** Returns the vector of URL objects as a ClasspathVector which has an intelligent toString(). The toString()
881    * method of ClasspathVector is usable as the classpath command line argument for java, javac javadoc, and junit.
882    * @return a vector of URLs with an intelligent toString();
883    */

884   public synchronized ClassPathVector getClassPath() {
885     ClassPathVector ret = new ClassPathVector();
886     
887     for (ClassPathEntry e: _classPathManager.getProjectCP()) ret.add(e.getEntry());
888     
889     for (ClassPathEntry e: _classPathManager.getBuildDirectoryCP()) ret.add(e.getEntry());
890     
891     for (ClassPathEntry e: _classPathManager.getProjectFilesCP()) ret.add(e.getEntry());
892     
893     for (ClassPathEntry e: _classPathManager.getExternalFilesCP()) ret.add(e.getEntry());
894     
895     for (ClassPathEntry e: _classPathManager.getExtraCP()) ret.add(e.getEntry());
896     
897     return ret;
898   }
899 }
900
901 /** Bookkeeping class to maintain information about each interpreter, such as whether it is currently in progress. */
902 class InterpreterData {
903   protected final Interpreter _interpreter;
904   protected volatile boolean _inProgress;
905   
906   InterpreterData(Interpreter interpreter) {
907     _interpreter = interpreter;
908     _inProgress = false;
909   }
910   
911   // The following methods do not need to be synchronized because they access or set volatile fields.
912

913   /** Gets the interpreter. */
914   public Interpreter getInterpreter() { return _interpreter; }
915   
916   /** Returns whether this interpreter is currently in progress with an interaction. */
917   public boolean inProgress() { return _inProgress; }
918   
919   /** Sets whether this interpreter is currently in progress. */
920   public void setInProgress(boolean inProgress) { _inProgress = inProgress; }
921 }
922
Popular Tags