The new Execution Environment is part of the JavaABC framework and can (without the use of any other plugins) be used for the execution of models. Die Execution Environment provides an API, which support the funcions of starting, controlling and observing executions.
In most cases the execution should not be controlled by an individual code, that is implemanted by the user, but by a comfortable graphical user interface. This user interface is provided by a special JavaABC plugin, the Tracer-Plugin.
The Tracer-Plugin is stored in the CVS as a jar archive with the name execution.jar in the module JavaABC and there in the directory /lib/execution. This file must be in the classpath. Then the JavaABC can be started and in the options dialog the class TracerPlugin from the package de.metaframe.plugin.execution can be added to the list of active plugins. After this the JavaABC has to be restarted. Now there is a new submenu in the plugins-menu Tracer-2 and two new buttons in the toolbar. So the Tracer-Plugin is correctly installed and can be used from now on.
Structure of the Execution Environment
The central class of the Tracer is the ExecutionController. For each execution there is exactly one instance of this class. The instance controls the entire execution and manages all other components of the Tracer. The ExecutionController gets a set of models (one of them is the main model) and all needed SIBs as its input. For the execution the ExecutionController starts one or several ExecutionThreads, which performs the execution. Furthermore there is the ExecutionEnvironment. This is a layer between the ExecutionThreads and the contexts. The ExecutionEnvironment provides a comfortable and simple way of accessing the contexts.
Usage of the Tracer-Plugin
After the Tracer-Plugin has been registered in the JavaABC, there is an additional submenu Tracer in the menu Plugins (ref. to the picture above). This contains the menu items Start New Local Execution, New Local Execution, Observe Local Execution, New Remote Execution, Observe Remote Execution and Breakpoint. Here you can see what the main features of the Tracer-Plugin are. An execution can be started or a running execution can be observed. An execution can be either local or remote. Remote means here that the execution runs in another virtual machine, not necessary on another host. The menu ithem Breakpoint setzt a breakpoint at the currently selected SIB, which causes the Tracer to pause when the execution reaches this SIB. This item is addinitionally present in the popup menu, which appears when you click with the right mouse button on a SIB. SIBs that have got a breakpoint are marked in the graph with a small blue square.
After the menu item New Local Execution has been chosen, a dialog appears, which contains all elements for controlling and observing an execution. In the title bar the ID of the current execution is displayed (which is the ID of the ExecutionController). The ID is mostly the name of the executed model. If the same model is executed once again, there is an additional dot and a number at the end of the ID (the number is increased at each execution). At the bottom of the dialog there is a status bar, which always displays the current status of the execution. At the top of the dialog there are several buttons for starting an execution, for pausing it, the buttons for stepping, one button for stopping the execution after the current SIB and one button for stopping the execution immediately ("killing" the execution). After an execution has been killed the current state is undefined, so the execution cannot be continued. All buttons that were mentioned here affect the entire execution. That means that for each button there is one method in the ExecutionController.
For stepping there are the buttons Step Over, Step Into and Step Return. This actions can be used when the ExecutionController has got the status PAUSED (Step Into also in the status STOPPED). Step Over causes the execution of the current SIB. Therefore the status is changed to RUNNING, then the SIB is executed and afterwards the status is set back to PAUSED. Is the current SIB a GraphSIB, the entire referenced model is executed in one step. The action Step Into is active if the status is STOPPED or if the status is PAUSED and the current SIB is a GraphSIB. This action causes the execution to jump into the referenced model (or the main model) and pause at the first SIB. So there is no execution of a SIB. The action Step Return is available if the execution is in a submodel. It causes the execution to proceed until the end of the current model and then jump back to the referencing GraphSIB and pause at the SIB after the GraphSIB. An execution can get the status PAUSED in different ways. Either it reaches a breakpoint or a step has been executed (by Step Over, Step Into or Step Return), or the execution got the explicit command PAUSE. This command is given by calling the method pause() in the ExecutionController. The button in the dialog causes a call of this method. When an execution gets the status PAUSED, the current SIB in the model is marked by a small overlay icon (a green arrow). If there is only one thread, all actions described so far take affect exactly on this thread. During a parallel exection of several threads they take affect on the whole execution and thus on the set of all threads. So if e.g. the STOP-command is called, all threads are stopped. For controlling also one single thread, you have to click on the details button and then two tabs appear, which are captioned with Threads and Context. The initially opened tab contains a table with all currently present ExecutionThreads, the statuses of the threads the current SIBs of the threads and all buttons which were mentioned for the whole execution here for each thread.
The tab Context contains elements for observing the content of all contexts. In a drop-down-box one thread has to be chosen. Below this there is the stack of this thread. Because the stack elements do not have own names or IDs, the name of the belonging model is displayed here. If one stack element is chosen, the table at the bottom of the dialog displays the content of the context. The table contains for each context entry the key, a String representation of the value and the type of the value (which is the name of the Java class). Overview over the stacks and contexts If the Tracer dialog is closed during an execution, the execution simply continues (that differs from the old Tracer). When the JavaABC is terminated (and thus the entire virtual machine) the execution is stopped. For making sure that this does not happen accidentally, a dialog appears which asks the user if he really wants to stop all executions. Over the menu item Observe Local Execution the user gets the Tracer dialog back. It is possible to run an arbitrarily number of execution at the same time (in one virtual machine). If there is more than one execution, you have to choose one of them in a dialog, if have clicked on Observe Local Execution. All running executions, i.e. all currently existing instances of the class ExecutionController, are stored in one hashtable, which maps the IDs of the executions to the ExecutionControllers. These instances can be accessed with static methods of the class ExecutionController. An instance of the class ExecutionController is added to the hashtable when the execution is started and removed when the execution has stopped. That means, that the hashtable contains all ExecutionControllers, which have one of the statuses SUSPENDED, PAUSED or RUNNING. If a remote execution should be controlled and observed, this is possible with the menu items New Remote Execution and Observe Remote Execution. If a new execution should be started in a remote virtual machine, there must be a server program running, which performs the execution. This program is a small command line program, which is based on the JavaABC-Framework and thus can use the Tracer. On starting a remote execution a dialog appears, in which all needed settings have to be set. At first you have to choose the used communication protocol. Here you have to choose the protocol, which is provided by the execution server. At the moment there only exists the implementation in RMI, so this is the only option. The next things are hostname and port of the execution server, the path to the model file on the remote host and a suggestion for the ID of the execution. The last thing is the password of the execution server. This password is set when the execution server is started so that no unauthorized access to the server is possible. When a model should be executed on a remote host, it is impossible to find out automatically what the entire set of all needed files is. If a SIB e.g. uses reflection for making library calls, it cannot be discovered which Java classes are needed. Even if it would somehow be possible to get the information, what files are needed, all files together could have a huge size. For this reasons, the models and SIBs are not sent over the network automatically. Instead all needed files have to be copied manually to the server so that the server can access these files. Furthermore all SIBs and libraries have to be in the classpath of the server. SIBs and models have to be identical on both sides (execution server and observer). When the dialog is closed with a click on OK, the Tracer dialog appears and the execution can be treated as if it was local. Starting a remote execution
When is a model executable?
If a model should be executed, there must be exactly one StartSIB in this model. A SIB is either automatically recognised as a StartSIB or explicitely defined as a StartSIB (or as "no StartSIB". A SIB is recognised automatically as a StartSIB, if it has got no incoming branches and at least one outgoing branch.
All SIBs of a model that should be executed have to implement an interface which is known by the ExecutionController. The ExecutionController posesses a list of known interfaces, which are implemented by executable SIBs. For each interface there is a delegate object (the so called "SIBExecutor"). SIBExecutors can be registered at the ExecutionController with the method registerGlobalSIBExecutor(). Not only the ExecutionController but also each ExecutionThread has got a list of SIBExecutors. During an execution the list of the ExecutionThread is always asked at first and then the list of the ExecutionController.
The default interface for executable SIBs is de.metaframe.abc.sib.Executable. For the execution of those SIBs, the DefaultSIBExecutor is used. All SIBs which are implementing the interface de.metaframe.abc.sib.ControlSIB are executed by the ControlSIBExecutor. The process of execution
The execution of a model is done by the class ExecutionThread. The algorihm is very simple and always the same: Search for the Start-SIB, execute the SIB, determine the successor, execute that SIB and so on. Before a SIB is executed, each time the appropriate SIBExecutor is determined.
SIBs with special rights: ControlSIBs
To extend the algorithm of execution in a very simple way, there are so called ControlSIBs. These are SIBs with extended possibilities. They have full access to the complete execution environment and thus can influence the execution and take over the control. A ControlSIB can for instance start new threads, create new contexts or manipulate the execution stack. The most important ControlSIBs are the ForkSIB, the JoinSIB and the GraphSIB. By the use of ForkSIB and JoinSIB parallel executions are getting possible. A ForkSIB interprets its outgoing branches as "And"-branches and not as "Or"-branches as it is usual. That means that not only one branch is chosen, but all of them. For each outgoing branch one new thread is started. The JoinSIB is the counterpiece to the ForkSIB. A JoinSIB changes several threads to one. That means, that the execution is waiting at a JoinSIB until all threads arrived at the JoinSIB over its incoming branches. Then the execution proceeds in only one thread. The GraphSIB is used to realize hierarchical models. It is a reference to another model. When the execution arrives at a GraphSIB, the referenced model is executed.
Communication over contexts
The communication between SIBs is realized with the use of shared contexts. A context is a simple HashMap, where arbitrary Java objects can be stored under certain keys and later be read. If two SIBs shall work with the same object, the first SIB must store it under a certain key in the context and the second SIB can get this object by reading it from the same context with the same key. At the start of an executino there is exactly one context. When the execution arrives at a GraphSIB, the execution of the submodel is started. This execution has got its own context, but the two context are connected. The first context is the "Parent-Context" of the new one. That means that the entries of the first context can also be accessed during the exeuction of the submodel. If a context is asked for a certain object, it first looks at its own entries. If there is no object under the given key, the context asks its parent. This context can also ask its parent and so on. At a ForkSIB each startet thread gets its own context which has the former context as its parent. A SIB which implements the interface Executable, gets access to an object of the class ExecutionEnvironment in its trace()-method. This object is providing a simple way of accessing the contexts. The method get() reads an object from the local context (which perhaps has to ask its parent). The getLocal()-method asks the local context, too, but this time the context won't asks its parent. The method put() stores an object in the local context and the findAndPut()-method searches (starting at the local context and then at its ancestors) for an entry with the given key. The Java object is then stored in the context where the given key has been found.
The execution of a model with API calls
If a model shall be executed, at first there has to be an instance of the ExecutionController: ExecutionController executionController = new ExecutionController(model); Here model is the SIBGraphModel to be executed. Then the model can be executed either by model.execute(executionController) or by executionController.execute(). These two command do exactly the same. Does the model contain GraphSIBs and thus submodels, all submodels have to be registered at the ExecutionController by executionController.addModel(). If you want to wait until the execution is finished, this is possible by using the command executionController.waitForReturn().