edu.rice.cs.drjava.model.repl.newjvm
Class MainJVM

java.lang.Object
  extended by edu.rice.cs.util.newjvm.AbstractMasterJVM
      extended by edu.rice.cs.drjava.model.repl.newjvm.MainJVM
All Implemented Interfaces:
MainJVMRemoteI, MasterRemote, Remote
Direct Known Subclasses:
NewJVMTest.TestJVMExtension

public class MainJVM
extends AbstractMasterJVM
implements MainJVMRemoteI

Manages a remote JVM. Includes methods for communication in both directions: MainJVMRemoteI provides callbacks allowing the remote JVM to access the model, and a variety of delegating methods wrap calls to the InterpreterJVMRemoteI methods, taking care of any RMI-related errors. In the case of errors, these interpreter-delegating methods communicate the failure via the return value. (Note that it is impossible to guarantee success of these methods -- the remote process may exit arbitrarily at any time -- and clients should behave gracefully when failures occur.)

The current design is flawed: strictly speaking, two sequential interpreter-delegating calls to this object may communicate with different JVMs if the remote JVM happens to reset in the interim. A better design would return a separate object for interfacing with each unique remote JVM. In this way, clients would know that all calls to a certain object would be forwarded to the same remote JVM.

Version:
$Id: MainJVM.java 5436 2011-08-02 06:58:19Z mgricken $

Nested Class Summary
private  class MainJVM.DisposedState
           
static class MainJVM.DummyDebugModel
          DebugModelCallback which does not react to events.
static class MainJVM.DummyInteractionsModel
          InteractionsModel which does not react to events.
static class MainJVM.DummyJUnitModel
          JUnitModel which does not react to events.
private  class MainJVM.FreshRunningState
          Variant of RunningState where the interpreter JVM has not yet been used.
private  class MainJVM.FreshState
          Fresh, hasn't yet been started.
private  class MainJVM.RestartingState
          Waiting for stop, should automatically start when that happens.
private  class MainJVM.ResultHandler
          Performs the appropriate action to return any type of result from a call to interpret back to the GlobalModel.
private  class MainJVM.RunningState
          Has an active interpreter available.
private  class MainJVM.StartingState
          Has been started, waiting for startup to complete.
private  class MainJVM.State
          State-based implementation of the starting/stopping functionality.
private  class MainJVM.StoppingState
          Waiting for stop, no restart.
 
Field Summary
private  boolean _allowAssertions
          Whether to allow "assert" statements to run in the remote JVM.
private  DebugModelCallback _debugModel
          Listens to debug-related events
private  MainJVM.ResultHandler _handler
          Instance of inner class to handle interpret result.
private  InteractionsModelCallback _interactionsModel
          Listens to interactions-related events.
private  JUnitModelCallback _junitModel
          Listens to JUnit-related events.
private  Iterable<File> _startupClassPath
          Class path to use for starting the interpreter JVM
private  StateMonitor<MainJVM.State> _state
          Contains the current InterpreterJVM stub, or null if it is not running.
private  File _workingDir
          Working directory for slave JVM
private static int MAX_STARTUP_FAILURES
          Number of slave startup failures allowed before aborting the startup process.
private static int STARTUP_TIMEOUT
          Number of milliseconds to block while waiting for an InterpreterJVM stub.
 
Constructor Summary
MainJVM(File wd)
          Creates a new MainJVM to interface to another JVM; the MainJVM has a link to the partially initialized global model.
 
Method Summary
private  void _doStartup()
          Call invokeSlave with the appropriate JVMBuilder.
private  int _getDebugPort()
          Returns the debug port to use, as specified by the model.
private  void _handleRemoteException(RemoteException e)
          Lets the model know if any exceptions occur while communicating with the Interpreter JVM.
 boolean addBuildDirectoryClassPath(File f)
          Blocks until the interpreter is connected.
 boolean addExternalFilesClassPath(File f)
          Blocks until the interpreter is connected.
 boolean addExtraClassPath(File f)
          Blocks until the interpreter is connected.
 boolean addInterpreter(String name)
          Adds a named interpreter to the list.
 boolean addProjectClassPath(File f)
          Blocks until the interpreter is connected.
 boolean addProjectFilesClassPath(File f)
          Blocks until the interpreter is connected.
 void classFileError(ClassFileError e)
          Called if the slave JVM encounters an illegal class file in testing.
 void dispose()
          Stop the interpreter JVM, do not restart it, and terminate the RMI server associated with this object.
 Option<List<String>> findTestClasses(List<String> classNames, List<File> files)
          Sets up a JUnit test suite in the Interpreter JVM and finds which classes are really TestCase classes (by loading them).
 Option<Iterable<File>> getClassPath()
          Returns the current class path of the interpreter as a list of unique entries.
 String getConsoleInput()
          Asks the main jvm for input from the console.
 File getFileForClassName(String className)
          Called when the JUnitTestManager wants to open a file that is not currently open.
 Option<Pair<String,String>> getVariableToString(String var)
          Gets the string representation of the value of a variable in the current interpreter, or "none" if the remote JVM is unavailable or an error occurs.
protected  void handleSlaveConnected(SlaveRemote newSlave)
          Callback for when the slave JVM has connected, and the bidirectional communications link has been established.
protected  void handleSlaveQuit(int status)
          Callback for when the slave JVM has quit.
protected  void handleSlaveWontStart(Exception e)
          Callback for when the slave JVM fails to either run or respond to SlaveRemote.start(edu.rice.cs.util.newjvm.MasterRemote).
 boolean interpret(String s)
          Interprets string s in the remote JVM.
 void nonTestCase(boolean isTestAll, boolean didCompileFail)
          Called if JUnit is invoked on a non TestCase class.
 boolean removeInterpreter(String name)
          Removes the interpreter with the given name, if it exists.
 void restartInterpreterJVM(boolean force)
          Get a "fresh" interpreter JVM.
protected  InterpretResult.Visitor<Void> resultHandler()
          Declared as a getter in order to allow subclasses to override the standard behavior.
 boolean runTestSuite()
          Runs the JUnit test suite already cached in the Interpreter JVM.
 Option<Pair<Boolean,Boolean>> setActiveInterpreter(String name)
          Sets the current interpreter to the one specified by name.
 void setAllowAssertions(boolean allow)
          Sets whether the remote JVM will run "assert" statements after the next restart.
 void setDebugModel(DebugModelCallback model)
          Provides an object to listen to debug-related events.
 boolean setEnforceAllAccess(boolean enforce)
          Sets the interpreter to enforce access to all members.
 boolean setEnforcePrivateAccess(boolean enforce)
          Sets the interpreter to enforce access to private members.
 void setInteractionsModel(InteractionsModelCallback model)
          Provides an object to listen to interactions-related events.
 void setJUnitModel(JUnitModelCallback model)
          Provides an object to listen to test-related events.
 boolean setPackageScope(String packageName)
          Sets the Interpreter to be in the given package.
 boolean setRequireSemicolon(boolean require)
          Require a semicolon at the end of statements.
 boolean setRequireVariableType(boolean require)
          Require variable declarations to include an explicit type.
 void setStartupClassPath(String classPath)
          Sets the class path to use for starting the interpreter JVM.
 Option<Pair<Boolean,Boolean>> setToDefaultInterpreter()
          Sets the default interpreter to be the current one.
 void setWorkingDirectory(File dir)
          Sets the working directory for the interpreter (takes effect on next startup).
 void startInterpreterJVM()
          Starts the interpreter if it's not running already.
 void stopInterpreterJVM()
          Stop the interpreter if it's current running.
 void systemErrPrint(String s)
          Forwards a call to System.err from InterpreterJVM to the local InteractionsModel.
 void systemOutPrint(String s)
          Forwards a call to System.out from InterpreterJVM to the local InteractionsModel.
 void testEnded(String testName, boolean wasSuccessful, boolean causedError)
          Called when a particular test has ended.
 void testStarted(String testName)
          Called when a particular test is started.
 void testSuiteEnded(JUnitError[] errors)
          Called when a full suite of tests has finished running.
 void testSuiteStarted(int numTests)
          Called to indicate that a suite of tests has started running.
 
Methods inherited from class edu.rice.cs.util.newjvm.AbstractMasterJVM
checkStillAlive, invokeSlave, isDisposed, quitSlave
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface edu.rice.cs.util.newjvm.MasterRemote
checkStillAlive
 

Field Detail

MAX_STARTUP_FAILURES

private static final int MAX_STARTUP_FAILURES
Number of slave startup failures allowed before aborting the startup process.

See Also:
Constant Field Values

STARTUP_TIMEOUT

private static final int STARTUP_TIMEOUT
Number of milliseconds to block while waiting for an InterpreterJVM stub.

See Also:
Constant Field Values

_state

private final StateMonitor<MainJVM.State> _state
Contains the current InterpreterJVM stub, or null if it is not running.


_handler

private final MainJVM.ResultHandler _handler
Instance of inner class to handle interpret result.


_interactionsModel

private volatile InteractionsModelCallback _interactionsModel
Listens to interactions-related events.


_junitModel

private volatile JUnitModelCallback _junitModel
Listens to JUnit-related events.


_debugModel

private volatile DebugModelCallback _debugModel
Listens to debug-related events


_allowAssertions

private volatile boolean _allowAssertions
Whether to allow "assert" statements to run in the remote JVM.


_startupClassPath

private volatile Iterable<File> _startupClassPath
Class path to use for starting the interpreter JVM


_workingDir

private volatile File _workingDir
Working directory for slave JVM

Constructor Detail

MainJVM

public MainJVM(File wd)
Creates a new MainJVM to interface to another JVM; the MainJVM has a link to the partially initialized global model. The MainJVM but does not automatically start the Interpreter JVM. Callers must set the InteractionsModel and JUnitModel and then call startInterpreterJVM().

Method Detail

startInterpreterJVM

public void startInterpreterJVM()
Starts the interpreter if it's not running already.


stopInterpreterJVM

public void stopInterpreterJVM()
Stop the interpreter if it's current running. (Note that, until startInterpreterJVM() is called again, all methods that delegate to the interpreter JVM will fail, returning "false" or "none".)


restartInterpreterJVM

public void restartInterpreterJVM(boolean force)
Get a "fresh" interpreter JVM. Has the same effect as startInterpreterJVM() if no interpreter is running. If a currently-running JVM is already "fresh", it is still stopped and restarted when force is true.


dispose

public void dispose()
Stop the interpreter JVM, do not restart it, and terminate the RMI server associated with this object. May be useful when a number of different MainJVM objects are created (such as when running tests).

Overrides:
dispose in class AbstractMasterJVM

handleSlaveConnected

protected void handleSlaveConnected(SlaveRemote newSlave)
Callback for when the slave JVM has connected, and the bidirectional communications link has been established. Provides access to the newly-created slave JVM.

Specified by:
handleSlaveConnected in class AbstractMasterJVM

handleSlaveQuit

protected void handleSlaveQuit(int status)
Callback for when the slave JVM has quit.

Specified by:
handleSlaveQuit in class AbstractMasterJVM
Parameters:
status - The exit code returned by the slave JVM.

handleSlaveWontStart

protected void handleSlaveWontStart(Exception e)
Callback for when the slave JVM fails to either run or respond to SlaveRemote.start(edu.rice.cs.util.newjvm.MasterRemote).

Specified by:
handleSlaveWontStart in class AbstractMasterJVM
Parameters:
e - Exception that occurred during startup.

systemErrPrint

public void systemErrPrint(String s)
Forwards a call to System.err from InterpreterJVM to the local InteractionsModel.

Specified by:
systemErrPrint in interface MainJVMRemoteI
Parameters:
s - String that was printed in the other JVM

systemOutPrint

public void systemOutPrint(String s)
Forwards a call to System.out from InterpreterJVM to the local InteractionsModel.

Specified by:
systemOutPrint in interface MainJVMRemoteI
Parameters:
s - String that was printed in the other JVM

getConsoleInput

public String getConsoleInput()
Asks the main jvm for input from the console.

Specified by:
getConsoleInput in interface MainJVMRemoteI
Returns:
the console input

nonTestCase

public void nonTestCase(boolean isTestAll,
                        boolean didCompileFail)
Called if JUnit is invoked on a non TestCase class. Forwards from the other JVM to the local JUnit model.

Specified by:
nonTestCase in interface MainJVMRemoteI
Parameters:
isTestAll - whether or not it was a use of the test all button
didCompileFail - whether or not a compile before this JUnit attempt failed

classFileError

public void classFileError(ClassFileError e)
Called if the slave JVM encounters an illegal class file in testing. Forwards from the other JVM to the local JUnit model.

Specified by:
classFileError in interface MainJVMRemoteI
Parameters:
e - the ClassFileError describing the error when loading the class file

testSuiteStarted

public void testSuiteStarted(int numTests)
Called to indicate that a suite of tests has started running. Forwards from the other JVM to the local JUnit model.

Specified by:
testSuiteStarted in interface MainJVMRemoteI
Parameters:
numTests - The number of tests in the suite to be run.

testStarted

public void testStarted(String testName)
Called when a particular test is started. Forwards from the slave JVM to the local JUnit model.

Specified by:
testStarted in interface MainJVMRemoteI
Parameters:
testName - The name of the test being started.

testEnded

public void testEnded(String testName,
                      boolean wasSuccessful,
                      boolean causedError)
Called when a particular test has ended. Forwards from the other JVM to the local JUnit model.

Specified by:
testEnded in interface MainJVMRemoteI
Parameters:
testName - The name of the test that has ended.
wasSuccessful - Whether the test passed or not.
causedError - If not successful, whether the test caused an error or simply failed.

testSuiteEnded

public void testSuiteEnded(JUnitError[] errors)
Called when a full suite of tests has finished running. Forwards from the other JVM to the local JUnit model.

Specified by:
testSuiteEnded in interface MainJVMRemoteI
Parameters:
errors - The array of errors from all failed tests in the suite.

getFileForClassName

public File getFileForClassName(String className)
Called when the JUnitTestManager wants to open a file that is not currently open.

Specified by:
getFileForClassName in interface MainJVMRemoteI
Parameters:
className - the name of the class for which we want to find the file
Returns:
the file associated with the given class

setInteractionsModel

public void setInteractionsModel(InteractionsModelCallback model)
Provides an object to listen to interactions-related events.


setJUnitModel

public void setJUnitModel(JUnitModelCallback model)
Provides an object to listen to test-related events.


setDebugModel

public void setDebugModel(DebugModelCallback model)
Provides an object to listen to debug-related events.

Parameters:
model - the debug model

setAllowAssertions

public void setAllowAssertions(boolean allow)
Sets whether the remote JVM will run "assert" statements after the next restart.


setStartupClassPath

public void setStartupClassPath(String classPath)
Sets the class path to use for starting the interpreter JVM. Must include the classes for the interpreter.

Parameters:
classPath - Class path for the interpreter JVM

setWorkingDirectory

public void setWorkingDirectory(File dir)
Sets the working directory for the interpreter (takes effect on next startup).


resultHandler

protected InterpretResult.Visitor<Void> resultHandler()
Declared as a getter in order to allow subclasses to override the standard behavior.


interpret

public boolean interpret(String s)
Interprets string s in the remote JVM. Blocks until the interpreter is connected and evaluation completes.

Returns:
true if successful; false if the subprocess is unavailable, the subprocess dies during the call, or an unexpected exception occurs.

getVariableToString

public Option<Pair<String,String>> getVariableToString(String var)
Gets the string representation of the value of a variable in the current interpreter, or "none" if the remote JVM is unavailable or an error occurs. Blocks until the interpreter is connected.

Parameters:
var - the name of the variable

addProjectClassPath

public boolean addProjectClassPath(File f)
Blocks until the interpreter is connected. Returns true if the change was successfully passed to the remote JVM.


addBuildDirectoryClassPath

public boolean addBuildDirectoryClassPath(File f)
Blocks until the interpreter is connected. Returns true if the change was successfully passed to the remote JVM.


addProjectFilesClassPath

public boolean addProjectFilesClassPath(File f)
Blocks until the interpreter is connected. Returns true if the change was successfully passed to the remote JVM.


addExternalFilesClassPath

public boolean addExternalFilesClassPath(File f)
Blocks until the interpreter is connected. Returns true if the change was successfully passed to the remote JVM.


addExtraClassPath

public boolean addExtraClassPath(File f)
Blocks until the interpreter is connected. Returns true if the change was successfully passed to the remote JVM.


getClassPath

public Option<Iterable<File>> getClassPath()
Returns the current class path of the interpreter as a list of unique entries. The result is "none" if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.


setPackageScope

public boolean setPackageScope(String packageName)
Sets the Interpreter to be in the given package. Blocks until the interpreter is connected.

Parameters:
packageName - Name of the package to enter.

findTestClasses

public Option<List<String>> findTestClasses(List<String> classNames,
                                            List<File> files)
Sets up a JUnit test suite in the Interpreter JVM and finds which classes are really TestCase classes (by loading them). Blocks until the interpreter is connected and the operation completes.

Parameters:
classNames - the class names to run in a test
files - the associated file
Returns:
the class names that are actually test cases

runTestSuite

public boolean runTestSuite()
Runs the JUnit test suite already cached in the Interpreter JVM. Blocks until the remote JVM is available. Returns false if no test suite is cached, the remote JVM is unavailable, or an error occurs.


addInterpreter

public boolean addInterpreter(String name)
Adds a named interpreter to the list. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.

Parameters:
name - the unique name for the interpreter
Throws:
IllegalArgumentException - if the name is not unique

removeInterpreter

public boolean removeInterpreter(String name)
Removes the interpreter with the given name, if it exists. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.

Parameters:
name - Name of the interpreter to remove

setActiveInterpreter

public Option<Pair<Boolean,Boolean>> setActiveInterpreter(String name)
Sets the current interpreter to the one specified by name. The result is "none" if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.

Parameters:
name - the unique name of the interpreter to set active
Returns:
Status flags: whether the current interpreter changed, and whether it is busy; or "none" on an error

setToDefaultInterpreter

public Option<Pair<Boolean,Boolean>> setToDefaultInterpreter()
Sets the default interpreter to be the current one. The result is "none" if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.

Returns:
Status flags: whether the current interpreter changed, and whether it is busy; or "none" on an error

setEnforceAllAccess

public boolean setEnforceAllAccess(boolean enforce)
Sets the interpreter to enforce access to all members. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.


setEnforcePrivateAccess

public boolean setEnforcePrivateAccess(boolean enforce)
Sets the interpreter to enforce access to private members. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.


setRequireSemicolon

public boolean setRequireSemicolon(boolean require)
Require a semicolon at the end of statements. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.


setRequireVariableType

public boolean setRequireVariableType(boolean require)
Require variable declarations to include an explicit type. The result is false if the remote JVM is unavailable or if an exception occurs. Blocks until the interpreter is connected.


_doStartup

private void _doStartup()
Call invokeSlave with the appropriate JVMBuilder.


_getDebugPort

private int _getDebugPort()
Returns the debug port to use, as specified by the model. Returns -1 if no usable port could be found.


_handleRemoteException

private void _handleRemoteException(RemoteException e)
Lets the model know if any exceptions occur while communicating with the Interpreter JVM.