In the 1960's we learned how to compile assembly language into machine language. Twenty years later, we learned how to do the same for high-level languages such as C. Now it is time to compile models so that developers work at a more productive level of abstraction, and automate tedious manual tasks.
A logical next step, model-based execution using the Universal Modeling Language (UML) allows the developer to focus on solving application and product problems, not on implementation details. Executable UML offers the benefits of early verification through simulation, the ability to translate the UML model directly into efficient code and the ability to delay implementation decisions until the last minute.
Although it is possible today to add code to UML models and then execute them, in so doing you must make a series of decisions about implementation that may not be correct, appropriate or even known. Executable UML models a system at a higher level of abstraction, thus avoiding the costs involved in a premature design.
Model execution is a form of code generation, which uses the model to generate class headers and the like, but leaving the user to fill in the code for the functions. As a consequence, there are two sources for the code: the code generated from the models and hand-written code.
Therein lies the problem: the two sources must be kept in synch somehow. If you change the headers as a result of writing detailed code, it is impossible to regenerate the code from the model.
Behavioral code generation solves the "unsynchronized code" problem by adding code directly to the model. Tools that support behavioral code generation usually generate code from a state chart, rather than just allowing the developer to specify the code for the functions.
For behavioral code generation, we therefore need to have an agreed execution semantics for the state chart. Because behavioral code generation links the logic to the model, it enables early error detection through verification and reuse of the model compiler. It is fair to call this form of code generation "executable" with a one-to-one correspondence between elements on the state chart and the generated code.
Although it is possible to add code to UML models and then execute them, in so doing you must make a series of decisions about implementation that may not be correct, appropriate or even known. The code generation system is closed, and the performance is determined by the model compiler. If the performance is inadequate, the developer generally has to distort the application model.
By comparison, in translative code generation the model compiler is open. This approach relies on subject matter separation-the complete separation of the application model from the model compiler. The model compiler comprises a set of reusable execution engine components, such as inter-task communication and list libraries, and a set of archetypes that direct the generation. The archetypes are completely open, so they may be used to generate C, C++, Ada, Forth and even VHDL.
An executable model would have to be based on a limited subset of UML assembled in a particular way, so that the models have meaning. For example, in UML one may build a statechart for a class, an object, a method, a use case, a component, a package just about anything. While the execution semantics of the state chart are defined in isolation, this leaves open the question of how the various elements relate to one another.
Executable UML comprises only three models, the class diagram, which outlines the conceptual entities in the domain, a state chart for each class that models the lifecycle of each object, and a procedure for each state.
The classes must be abstracted based on both similar behavior and characteristics. This requirement allows a state chart to be built for each class that applies uniformly to each object, avoiding logic to determine what type of object each action is executing on. In contrast, polymorphic functions don't allow the structure of the state chart to differ from one object to the next, nor do they easily allow for different data structures for each object.
State charts would model the lifecycle of each object. There is one state chart per class because the behavior of each object is exactly the same. In some cases, there is no need to build a state chart because the only operations are synchronous data access under the control of other objects.
For other classes that manage contention, we build a single state chart for the class as a whole. Conceptually, then, a class may have no state charts, or one chart that applies uniformly to each object, and another that applies to the class as a whole.
An executable model operates on data about objects. Each class defines attributes for objects, and each attribute may have an initial value for each object. When the model executes, each object has its own state, and each object executes concurrently and asynchronously with respect to all others.
Each state chart instance, then, is in its own state, either executing a procedure to completion or not. The actions inside procedures may read data through encapsulated functions of other classes at the same time as the target object is executing a procedure. It is the modeler's responsibility to avoid data access conflict by synchronizing the behavior of the various objects, if required, using a state chart.
State charts recognize events. Each event may be a signal sent from another object from the outside, or it can be the result of a "deferred event" that signals expiry of a timer or of some absolute time.
Thus, an executable UML "program" comprises a collection of concurrently executing state machines communicating by sending signals. This highly concurrent model can be reorganized to be fully synchronous or distributed without changing its behavior. Indeed, it is possible to reorganize the model so that some states are executed periodically in a separate task, and the attributes of a single object are stored in multiple processors. So long as the behavior is the same.
Until recently, the UML had an under-developed model of actions. The model comprised seven actions, including create an instance, send a signal, destroy an instance, terminate an instance, and, my personal favorite, "uninterpreted string."
In 1998, the Object Management Group developed a Request for Proposal for a precise Action Semantics that laid out the requirements. One of these key requirements is that the semantics should require the definition only of required sequencing, and should not over-constrain the sequence. Specifically, this means that the sequential nature of today's third-generation programming languages is would be considered over-constrained.
Second, the action semantics should separate functional computation from data access. The intent of this requirement is to allow the statement of a function to be reusable regardless of where the data came from.
Also, the action semantics should manipulate only UML elements. This means two things. One is that the action semantics must know about classes, attributes, and other UML concepts. We can think of these elements as being "pre-declared". The other is that only UML elements are permitted. No pointers, no arrays, no magic, except as explicitly modeled in UML.
But, executable UML only carries us so far. We also need to interchange the models between tools. XML is a data interchange tool that relies on data type definitions (DTDs) to define the semantics of the interchanged data. This definition, for the UML in XML, is called XMI. However, XMI is still insufficient because it allows any syntactically correct UML model to be interchanged, and what we need is to be able to interchange standard semantics for execution.
A consortium of companies has formed an organization at the time of this writing, there is no name for this organization to define the precise semantics of executable UML and an interchange standard using XMI. The resulting work should then become an OMG standard profile on UML.
Executable UML enables a market in model compilers, because a standard interchange mechanism for standard semantics means that any vendor who builds a model compiler will have access to all executable UML models. Each model compiler can then target a specific platform. For example, one model compiler can target small embedded systems generating highly optimized C straight onto the silicon without an operating system, while another can generate multi-tasking C++ with persistence capabilities. Still others can generate VHDL from executable UML models.