blob: 86671585c38aac163129f173ba61cafc1e57ac9a [file] [log] [blame]
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<title>From Front End To Code - MDSD in Practice</title>
<link rel="stylesheet" href="../default_style.css">
<body LINK="#0000ff" VLINK="#800080">
<div align="right">&nbsp; <font face="Times New Roman, Times, serif" size="2">Copyright
&copy; 2006 Markus Voelter, Bernd Kolb, Sven Efftinge</font>
<table border=0 cellspacing=0 cellpadding=2 width="100%">
<td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF">&nbsp;Eclipse
Corner Article</font></font></b></td>
<div align="left">
<h1><img src="images/Idea.jpg" height=86 width=120 align=CENTER></h1>
<h1 ALIGN="CENTER">From Front End To Code - MDSD in Practice</h1>
Model-driven software development (MDSD) is not just about generating code. Several
additional challenges have to be mastered. These include: how to get usable
graphical and textual editors for your domain specific language (DSL), how to validate your models against
your metamodels, how to define model modifications and transformations and finally,
how to write scalable, maintainable and extensible code generators. In this article
we show how to tackle all these challenges, based on a collection of open source
tools: Eclipse, Eclipse Modeling Framework (EMF), Graphical Modeling Framework (GMF) as well as openArchitectureWare. We believe that
this tool chain provides a proven and stable stack for making MDSD a practical reality.
<p><b> By <a href="">Markus Voelter</a>, <a href="">Bernd Kolb</a>, <a href="">Sven Efftinge</a>, and <a
href="">Arno Haase</a>, all Independent Consultants</b> <br>
<font size="-1">June 15, 2006</font></p>
<hr width="100%">
For model-driven development it is essential that we provide an end-to-end tool chain.
The following challenges arise:
<li>You'll need good editors for your models: This has traditionally been a problem
that has been addressed by using Unified Modeling Language (UML) tools with profiles. With the advent of the Graphical Modeling Framework (GMF) and openArchitectureWare's (oAW) xText
things are changing for the better.
<li>Verifying the models as you build them: models must be verified for correctness interactively
during modeling, as well as just before transformation/generation.
<li>Transforming/modifying models: Models must be transformed into other models, or models
must be "enriched" with additional information.
<li>Generating code: code needs to be generated so we can execute the system described by the
models somewhere.
<li>Integrating generated and non-generated code: This is something that does require some
thought &ndash; even if it sounds trivial.
Note that in this article we're not going to address issues such as why you would want to
do model-driven software development (MDSD) at all, which advantages it has, how you'll have to organize your project, etc.
If you're interested in these aspects (which we assume you already know for this article),
we recommend reading [<a href="#SV06">SV06</a>]. We assume that you consider MDSD to be useful &ndash; and based on
that, we'll show you how to do it effectively with today's tooling.
Our example is based on state machines. Everybody knows state machines, so this sounds
like a reasonable example. We will start by defining a metamodel for state machines.
This will be based on the UML metamodel. We use Eclipse Modeling Framework (EMF) to define it.
We will then build a graphical editor for state machines using the well-known UML-based
notation; for this task we'll use the Eclipse Graphical Modeling Framework (GMF) framework. We will then add additional
constraints (e.g. "states must have unique names"). To make things more convenient, we'll
use the oAW Checks language and integrate it with GMF. Next up will be a code generator
that creates an implementation of state machines in Java. We use oAW's xPand language.
We'll then create a set of recipes that help developers with the implementation of the
actions associated with states &ndash; this illustrates the problem of integrating generated and manually
written code. We will then cover model-to-model transformations and model modifications using
oAW's xTend language. Finally, we will build a textual editor for rendering the state machines
textually using oAW's xText framework.
Note that all the code is available from
<a href=""></a>
<h2>The Metamodel</h2>
The metamodel for state machines is probably well-known. A state machine consists of a
number of states. States can be start states, stop states and "normal" states. A transition
connects two states. States know their outgoing and incoming transitions. We also support
composite states that themselves contain sub state machines. A state machine is itself a
composite state. A state has actions. Actions can either be entry or exit actions. Here's
the UML diagram for that metamodel, rendered with the GMF Ecore Modeler.
<p align="center"><img src="metamodel.gif"></p>
<h3>Defining the metamodel</h3>
You can either draw the metamodel in the editor as shown above,
or you can also use the EMF tree editor as shown below. The next illustration
shows the editor, with our metamodel opened.
<p align="center"><img src="emfmm.gif"></p>
In the properties view, as shown in the next screenshot, you can adjust the properties
of the metamodel elements.
<p align="center"><img src="emfprops.gif"></p>
You can find the metamodel in the oaw4.demo.gmf.statemachine2 project.
<h3>Generating the genmodel and the other necessary artifacts</h3>
To do useful work with this metamodel, we have to generate a number of artifacts
(note that you could in theory work with dynamic EMF models, but for our case (GMF,
Recipes, etc.) this will not work). First of all, you need to generate the genmodel.
This is basically a decorator model (or a marker model) around our metamodel that
"decorates" a number of extra properties on top of it. The yellow area
in the following screenshot shows the genmodel file. If you're familiar with EMF
you'll know how to generate it; if you're not familiar with EMF it's not important here.
<p align="center"><img src="generateEMFStuff.gif"></p>
Once you have generated the genmodel, open it, select the root element, and execute Generate
All from the context menu. This is what will be generated:
<li>The implementation classes for the metamodel are generated. Take a look at the
src folder of your metamodel project to see the effect. Each metamodel element now
has a corresponding interface and implementation.
<li>The .edit project (red in the previous screenshot) contains a number of utilities
for building editors
<li>The .editor project (also red in the previous screenshot) contains a tree-based
editor, specific to that metamodel. After running this plug-in in a runtime Eclipse workbench,
for example, you can define instances of your metamodel (i.e. actual state machines)
with a tree view. We'll not do this now, since we'll use the graphical editor for that.
<li>Finally, the .test project contains a number of tests for your metamodel. It is not
relevant here.
As of now, there's no reason to look into any of these models or projects in more detail.
You just have to make sure they're there.
<h2>Building the Graphical Editor</h2>
Building a graphical editor for your own DSL is probably the most complex and intricate
of the steps of this tutorial. This is for two reasons. The first reason is that this
tutorial has been written before GMF was finalized. So there are still a couple of
glitches in the software (to get an updated tutorial, see [<a href="#GmfTut">GmfTut</a>]). The second reason
is that building an editor is simply non-trivial! Before GMF arrived, you had to do it
manually with the Graphical Editing Framework (GEF) which was a very daunting task (unless you'd write your own generator,
as in [<a href="#RV05">RV05</a>]). Before even GEF, building a custom editor was not a viable alternative for
most projects at all. So, GMF really is a huge step forward. And the remaining complexity
is actually a joke, compared to what you'd have had to do pre-GMF. So let's get started.
<h3>Conceptual Overview / Project</h3>
For building a GMF editor (as shown in the next illustration) you start from your metamodel
(the .ecore file) and the .genmodel defined above. We assume that these are already there.
Also, the .edit and .editor projects generated from them are also assumed to be available.
To get us a step further, a number of additional models have to be defined (we'll look
into each of the later):
<li>A model defining the graphical notation including shapes, decorations and graphical
nodes and connections. This is called the .gmfgraph model.
<li>A model for the editor's palette and other tooling, called the .gmftool model.
<li>A mapping model that binds these two models to the domain metamodel. The two models
defined above are technically (but usually, not conceptually) independent of your domain
Take a look at this illustration to understand how those models fit together. From all of these
additional models, GMF creates the .gmfgen model &ndash; again a "low level" model that the code
generator uses as an input, finally creating the .diagram project which contains your
desired editor.
<p align="center"><img src="gmf.gif"/></p>
We use another project for the GMF models from which we'll create the editor:
oaw4.demo.gmf.statemachine2.gmf. This project contains all the additional models just
talked about. Here's a screenshot.
<p align="center"><img src="gmfproject.gif"/></p>
<h3>The Tool Model</h3>
The .gmftool model defines &ndash; in our example &ndash; only a set of palette entries. The palette
is the set of buttons (usually) on the right of an editor that allows you to add model
elements to your model. So we need a creation tool for each of the metamodel elements that
we want to be able to place onto the editor.
<p align="center"><img src="gmftool.gif"/></p>
We also assign icons to each of the of the creation tools. Note that we don't
need a creation tool for the state machine itself, since the state machine will be
represented by the editor itself (more specifically, it's Canvas).
<h3>The Graph Model</h3>
The graph model defines several (relatively separate) things.
<li>One is the set of figures defined in (one or more, potentially imported) FigureGalleries.
Colors, line widths and static decorations are also defined here.
<li>We also define graph nodes and connections. In the end, the editor consists of vertices
connected by edges; those are defined here, too.
<li>We also define compartments. Compartments are sections in nodes that can be collapsed
and themselves contain graphs (or lists of elements).
<li>Finally, we define diagram labels used to show text associated with graphical elements.
<p align="center"><img src="gmfgraph.gif"/></p>
Looking at the screenshot above, you can see Nodes named after the metamodel elements we're going
to map in a minute. In the properties (which you can't see here) we connect the Node elements
to the respective figures; for example, the StateNode is associated with the StateRectangle.
<h3>The Mapping Model</h3>
This is the most complex model. Here we map the tool definition and the graph definition to
the domain metamodel. For example (see the green part in the next screenshot) we map the
normal States. We'll explain this mapping in some detail. To be able to actually map the
various elements, you have to add these other Resources to the editor (see the yellow parts
in the screenshot):
<li>For each metamodel element that we want to map directly onto the diagram surface (the state machine) we have
to first define a Top Node Reference.
<li>Below that, we add a normal Node Mapping. It contains information about the model element
to map (State) and the property, in which the set of these elements is stored in the
container (the container being the StateMachine here, the property that contains the
states would be the EReference states).
<li>Below the Node Mapping there's a Label Mapping. This one associates the label defined
in the graph with the respective model element properties (here: the name of the state).
<li>We also want to see the list of (entry and exit) actions in the state; so we have a
Compartment Mapping below the Node Mapping.
<li>We also have a Child Reference that identifies the set of children that should be shown.
<li>The Child Reference is associated with the Compartment, to ensure that the child collection
is actually shown in the respective compartment.
All of this has to be mapped with a number of properties; the editors for doing that are &ndash; as
you can see &ndash; just the usual tree editors, which makes all of that stuff a bit cumbersome.
There are additional, more specialized editors in the GMF pipeline that should make this process
simpler (Some of the more specialized editors and wizards seem not to work that great at the time of this writing, or,
more specifically, don't always provide the detailed level of control you'll need, so we
use the tree editors).
<p align="center"><img src="gmfmap.gif"/></p>
The red part in the screenshot is a mapping of a link, links being the mappings of the
edges of the graph. In the properties view, you can see some of the parameterization of
the respective link:
<li>The Containment Feature is the EReference in the containing metaclass (here: StateMachine)
that contains the reference objects (here: the Transitions).
<li>Then we map the Element that should represent the Link; this is the Transition
in our case.
<li>Next we have to tell GMF which feature of the link metaclass (here: Transition)
should take the reference to the source element (Source Feature)
<li>We have to do the same thing with the target, this being stored in the Target Feature
<li>We also have to define the graphical element (defined in the .gmfgraph model) that should
represent the connection; here this is the TransitionConnection.
<li>Finally we have to define which tool should be used to actually instantiate such a link.
That (and a number of details we didn't show here) finalizes the model definitions for GMF.
<h3>Generating the Editor</h3>
We now create the .gmfgen model from the set of models we just created (see the conceptual
diagram above). From that, we can actually generate the diagram code &ndash; this will be contained
in the .diagram project.
<p align="center"><img src="diagramproject.gif"/></p>
<h3>Running the Editor</h3>
As usual, when generating editor plug-ins inside an Eclipse workspace, we have to use one of
two possible ways to see the result:
<li>we either have to export the plug-in into the current Eclipse installation and restart it
<li>or, we fork another Eclipse instance, a so-called Runtime Workbench (aka Eclipse Application) that
contains the plug-ins that are in the workspace of the current Eclipse instance.
The following screenshot shows the running editor with an example state machine (a CD player)
in the editor. This example state machine is located in a project called oaw4.demo.gmf.statemachine2.example.
<p align="center"><img src="runningEditor.gif"/></p>
Constraints are rules that models must conform to in order to be valid. These are in addition
to the structures that the metamodel defines. Formally, constraints are part of the metamodel.
A constraint is a boolean expression (a.k.a predicate) that must be true for a model to conform
to a metamodel.
Constraint Evaluation should be available in batch mode (when processing the model, see below)
as well as interactively, during the modeling phase in the editor we just built ... and we don't
want to implement constraints twice to have them available in both places!
For defining constraints, functional languages are often used, OCL is the language of choice in the
OMG/MOF/UML environment.
As a consequence of the better tooling, we'll use openArchitectureWare's Check language.
This language is very much like OCL. In fact, you'll probably not notice the differences in
the examples that follow.
<h3>Constraint Definition</h3>
Since the constraints are conceptually part of the metamodel, we'll put the constraint
definitions into the metamodel project, see the green area in the following screenshot.
<p align="center"><img src="constraintsInProject.gif"/></p>
As can be seen from the screenshot above, constraints are implemented in .chk files.
openArchitectureWare comes with custom-made editors for the .chk files. The editors
provide metamodel-aware code completion as well as error checking. The following
screenshot shows a couple of constraints.
<p align="center"><img src="constraints.gif"/></p>
To see the power of the functional constraint language, you should take a look at the
"States must have unique Names" constraint:
<li>typeSelect(TypeName) selects, from a collection of elements of type A, all elements
that are actually of type B, B being a subtype of A. Note that the typeSelect(...)
operation is statically typed to the TypeName given as the parameter. No need to cast!
<li>forAll(...) returns true, if the expression in the second part returns true for all
elements in the collection on which you call forAll(...)
<li>exists(...) returns true, if the expression in the second part returns true for at
least one of the elements in the collection on which you call exists(...)
<li>There are more predicates, the most useful probably being select(x|expression-with-x)
that selects all elements from a collection for which the expression is true. Note that
collection.typeSelect(T) is actually an abbreviation of (Collection[T])|T.isInstance(x))
To show off the editors, here's a screenshot of the editor in action :-)
<p align="center"><img src="constraintEditorError.gif"/></p>
<h3>Integration into the GMF Editor</h3>
By default, the GMF-generated editor obviously doesn't know about oAW. To make the GMF
generated editors evaluate our constraints, we needed to tweak things a little bit; most
of this is in oaw4.demo.gmf.statemachine2.etc project.
First of all, we wrote our own ConstraintEvaluators (a GMF/EMFT concept) and plugged in
the oAW CheckFacade (the facade for oAW's constraint check system). We then used AspectJ to weave
in the necessary notification adapters into the generated EMF Factory class (we really
didn't want to modify the generated EMF classes!). We wrote a watchdog that does the batch
evaluations whenever the model has changed (with a delay of two seconds). This relieves you from
having to press the Validate button over and over again. Note that all of this stuff is actually
part of the oAW/GMF integration package (released with oAW 4.1) so you don't have to
care about the details.
So what remains to be done for you as an editor developer is the following steps.
First of all, you have to write a constraint class that loads the constraint file we wrote above.
<p align="center"><img src="BatchConstraint.gif"/></p>
You then have to plug these constraints into the EMF Validation Framework's constraintProvider
extension point, as shown in the next screenshot.
<p align="center"><img src="constraintProviderXML.gif"/></p>
Finally, you have to make two important adjustments in the .gmfgen model (yes, to the generated
.gmfgen model!), as shown by the green areas in the following screenshot.
<p align="center"><img src="gmfgenadjust.gif"/></p>
<h3>Running the Editor again</h3>
Once you did all this you should be able to rerun the editor; you should get error messages
right after (actually, 2 seconds after) you did something illegal, as per the constraints.
<p align="center"><img src="editoError.gif"/></p>
<h2>Code Generation</h2>
Code Generation is used to generate executable code from models &ndash; usually the final result
we want to get to. Code Generation is based on the metamodel and uses templates to
attach to-be-generated source code. In openArchitectureWare, we use a template language
called xPand. It provides a number of advanced features such as polymorphism, AO support
and a powerful integrated expression language. We will show most of these features here.
<h3>Template/Metamodel interaction</h3>
Templates can access metamodel properties seamlessly, as shown in the next illustration.
<p align="center"><img src="propertyAccess.gif"/></p>
The code on the left is a template. Blue stuff is code that will be written to the to-be-generated
file. The capitalized, purple code are template control structures such as FOREACH, IF or
DEFINE (which I'll explain later). Finally, the black code is code that accesses the metamodel, as
well as expressions involving metamodel properties. The expression states.typeSelect(State) can be
explained as follows:
<li>We are in the context of a StateMachine. This is because the DEFINITION in which we're in is
defined for the metaclass StateMachine. Like methods in Java, templates (DEFINE..ENDDEFINE) are
associated with a specific metaclass. When the template is executed, there's an implicit this-pointer,
referencing an instance of the respective metaclass.
<li>So, since we can omit this, states references the same-named property of the StateMachine metaclass
and thus returns a collection of the States inside the state machine.
<li>Since we only need the normal States (and not start states and stop states), we do a
typeSelect(...) to filter out what we need.
To complete this one, the FOREACH statement iterates over the result of the expression, assigns the
current iterated element to the temporary variable s, and then executes the body of the
FOREACH...ENDFOREACH block &ndash; which is generating some public static final int attribute.
<h3>What to generate?</h3>
What kind of code will be generated? How do you implement a state machine? You should think
about what you want to generate before you start writing a generator. There are many ways of
implementing a state machine: GoF's State pattern, If/Switch-based, Decision Tables or
Pointers/Indexed Arrays.
We will use the switch-based alternative. It is neither the most efficient nor the most elegant
alternative, but it's simple. For more discussion of this topic, see Practical State
Charts in C/C++ by Miro Samek [<a href="#MS02">MS02</a>]. So here's the outline of the code we want to generate:
<li>Generate an enumeration for the states
<li>Generate an enumeration for the events
<li>Have a variable that remembers the state in which the state machine is currently in.
<li>Implement a function trigger(event) which
<li>First switches over all states to find out the current state
<li>Check whether there's a transition for the event passed into the function
<li>If so,
<li>execute exit action of current state,
<li>Set current state to target of transition
<li>Execute entry action of this new current state
<li>And also handle nested states :-)
<h3>Organization of the Generator Project</h3>
The generator is located in the oaw4.demo.gmf.statemachine2.generator project. There are a number of
code generation templates (green area in the following screenshot). Extensions are also defined
(purple area). There are also workflow files (.oaw) that control the workflow of a generator
run (orange area). Different workflow files contain different "parts" of the overall generator
run and call each other. Workflow files are in some small way like ant files.
<p align="center"><img src="generatorProject.gif"/></p>
<h3>Example Template</h3>
Here's an example template with a couple of annotations that explain what happens.
Note that the template language uses a very "clean" syntax, using French quotation
marks (for which the editor provides keyboard shortcuts :-)).
<p align="center"><img src="templateExample.gif"/></p>
Often, in the templates, you'll need additional properties on your metaclasses that
are not defined in the metamodel itself. Examples are things such as a state machine's
file name, the name of a state, in all-uppercase, to use as a constant, and other similar things.
To keep the metamodel clean, these properties should not be defined in the metamodel itself
(those properties aren't part of the problem space, rather, they are "helpers" useful for code
generation). This argument becomes especially obvious if you consider the situation where
you'd generate code for various target languages (say, Java, C++ and XML) from the same model.
In that case you'd pollute your metamodel with all kinds of helpers.
To avoid this, you can define extensions. Extensions can be called using member-style syntax:
myAction.methodName(). Extensions can be used in xPand templates, They are imported into template
files using the EXTENSION keyword. Check files, as well as other Extension files can also import
extensions. Here's an example.
<p align="center"><img src="extFile.gif"/></p>
Extensions are written in oAW's expression language (the same language you used in the
constraint checks). If, for some reason, the expressive power of the expression language is
not enough, you can also escape to Java code.
<h3>Workflow Definition</h3>
A workflow definition is necessary to define the sequence of steps the generator executes
in order to do something useful. In this first example, we need to execute the following steps:
read the model, verify it (i.e. verify the same constraints we verified in the interactive editor)
and generate code.
Below is a screenshot of this simple workflow file. Workflow files are XML files that contain a
number of workflow component declarations. Each workflow component declaration specifies the
component class (such as oaw.emf.XmiReader) and a number of configuration properties.
Note that you can use ${propName} notation as variables, just as in ant.
<p align="center"><img src="workflowexample.gif"/></p>
Just to recount: the CheckComponent in step two executes exactly the same constraints checks
(the same .chk file) as the editor! There is something to be said about the expression
model.eAllContents.union( {model} ). Basically what it does is that is returns a collection of all
children of the element in the model slot, unioned with the element in the model slot itself.
eAllContents is an Ecore primitive that returns all the children of an element, recursively,
but not the element itself. Since we want all elements of the model, we have to union the children
set with the set that contains only the model element itself. {...} is the literal set constructor in
the oAW expression language.
Also note that the generator component has the property skipOnErrors set to true. This
means that this component is not executed if there are already errors in the workflow context,
for example as a consequence of constraint checking in a previous step of the generator run.
There's a bit more to be said about project organization. The variable ${modelFile} points to
the actual file we want to generate. Now, the name of this file is something we cannot define
as part of the generator project, since it's specific to the application. Remember that a generator
is typically used in many applications! So, the workflow file we just defined is almost like
procedure we'd like to call from somewhere else &ndash; from the application project, and that's also
the way how we supply the value for the modelFile variable.
So, inside the oaw4.demo.gmf.statemachine2.example project (that's the one where we'd "drawn" the
CD player state machine) there's another workflow file, that looks like so:
<p align="center"><img src="exampleWorkflow.gif"/></p>
As you can see, it defines a value for modelFile and then calls the original workflow file using
the cartridge file="..." construct.
If you run that one, you'll see the code for the example state machine being generated into the
src-gen folder in your example project.
<h3>AO templates</h3>
Consider somebody gave the generator we developed above to you, or you'd downloaded a "state machine
cartridge" from somewhere. Now you notice that you want to adapt the code generator somehow, for
example, you want to add log output, or you want the state machine to throw an exception if an event
arrives which the state machine cannot handle in the current state (as opposed to just ignoring it,
as it happens in the current implementation).
How can you do that? How can you adapt the code generation process to your needs without hacking
the existing templates? Well, aspect orientation provides a solution here, and openArchitectureWare
supports aspect-oriented templates.
Here's (almost all) of the code that's necessary to achieve that.
<p align="center"><img src="aspectExample.gif"/></p>
AROUND templates advice existing templates; you can use all kinds of wildcards to define the
pointcut (i.e. the template to be advised). The joint point model is limited to template executions,
so if you want to be able to advice a certain location in the template, you need to make sure
that there's a DEFINE..ENDDEFINE block around it. In the worst case, you as the writer of the
original templates have to add dummy templates (such as in the handleIllegalTransition case).
This is the very same situation as in OO programming; in order to overwrite a piece of code
in a subclass, this piece of code has to be modularized into a method (you can't override
line 3 to 11 of a given method!). In AO this is fundamentally the same, however, the granularity of
what you can advice depends on the joint point model. Most AO systems use method-level join points
only (the equivalent being the template in our case); only AspectJ has some finer grained join points.
To actually execute the templates, you have to tell the generator component that they are
available. Now, the problem is, that the workflow file might also have been written by
"the other guy" and you can't or don't want to change it. So what you need to do is to
advise an existing component declaration and add the necessary properties. Fortunately,
you can do this, too, the workflow engine also supports advices. Without going into all
the detail (which you can find in the docs to openArchitectureWare), you can add the following
code to the example workflow file (assuming, here, that the logging stuff we wrote above is
specific to the example project):
<p align="center"><img src="aoworkflow.gif"/></p>
In the second component declaration, you use a GeneratorAdvice component and identify the
target of the advice (the pointcut, so to speak) with the adviceTarget property. It identifies
the original component by its name (if you go back to workflow file we showed, you'll
see that the generator component had the ID "generator"). The semantics of the advice component is,
that the properties added to the advice component will be "transferred" to the adviceTarget.
So, in this case, this adds the &lt;advices value="templates::Logging"&gt; property to the original
generator component, which, in turn, tells it about the template aspects. That's all you need to do.
A final word on AO: Consider how useful this feature will be if you're building families of code generators,
for example in embedded systems, where you'll often need to generate code for a number of (related) target
platforms. Most of the code will be the same for all of those platforms; however, parts will be specific to
the processor or OS abstraction layer, or whatever. You can then put all the common code into a set of
templates and have the specifics organized into a set of AO templates, everything nicely modularized and
<h2>Recipes &ndash; Integrating generated and non-generated code</h2>
In most scenarios, it is not useful, from a pragmatic point of view, to generate 100% of the
application code. For example, in our state machine example, we don't generate the implementation
of the actions (the code that is executed upon entry or exit of a state). This has to be written
manually, and integrated with the code that has been generated.
Since we don't like to use protected regions for various reasons (yes, openArchitectureWare supports them :-)),
we need some other means. The next illustration shows a couple of alternatives such as inheritance,
factories, template methods, etc.
<p align="center"><img src="codeInt.gif"/></p>
The problem that arises here is as follows: application development is not yet finished once
the generator has terminated! Developers have to add the manual code in the right way. However, the
generator cannot help developer "do the right thing" here, since it has already terminated! What you'd
want is somebody telling the developers something like "hey, you have to add this action implementation
to make the system complete". And of course, once you've added that code, that somebody should shut up
and not bother you with the reminder :-)
<h3>Using Recipes</h3>
Well, that's exactly what you can do with the Recipe framework. It provides a task-based
approach to "completing" the generated code with manual parts. This works the following way:
<li>As part of the generator run, you instantiate checks; these are written to a file
<li>After the generator finishes, the IDE (here: Eclipse) loads these checks and verifies them
against the complete code base (i.e. Generated + manual)
<li>If things don't conform to the rules, messages are output helping the developer to fix things.
For example, in the state machine case, actions must be implemented in subclasses of the
generated state machine implementation. Let's look at the effects:
<p align="center"><img src="recipeScreenshot.gif"/></p>
The code generator created a class AbstractCdPlayer (since the state machine was named
CdPlayer in the model). We've already written a class CdPlayer (which is the required name
for the implementation class) but we did not yet extend the base class. So the recipe check
that verifies the existence of the CdPlayer class is green; the check that verifies that it has to extend the
AbstractCdPlayer base class is still red. So you know what you need to do. Once you add "extends
AbstractCdPlayer" to your class definition, and save it, the red mark will get green.
Of course, then we'll get a number of compiler errors, because I have to implement the
action methods. We decided not to provide a separate recipe check for each of them, although
we could have done that.
<p align="center"><img src="comerr.gif"/></p>
<h3>Implementing Recipes</h3>
Implementing Recipes is a two stage process. The first stage is to implement the elementary
checks (such as class has to exist, or that a class has to extend a certain superclass).
oAW comes with a number of predefined recipe checks for Java, you can also define your own
checks, e.g. to verify C++ code.
<p align="center"><img src="recipeImplementation.gif"/></p>
The next step is to instantiate the checks as part of your workflow and write them to
the external file which Eclipse can then load to validate the checks against the code base.
To do this, we write a workflow component that does the work. Here's the code.
<p align="center"><img src="recipeWorkflow.gif"/></p>
<h2>Model Modifications</h2>
Model modifications are a form of model transformation, where the source model is also the
target model, i.e. the original model is modified. There's also model transformations,
where one or more output models are created from one or more input models, leaving the inputs
unchanged. Both of these tasks can be accomplished with oAW's xTend language, as we'll see.
Alternatively, you can use any other EMF-based transformation engine, such as MTF or ATL.
So what kind of modification would make sense? We decided to add an emergency shutdown to
our state machine. So, we'll have to add an additional state (emergency stop) and a transition
from each (normal) state to this emergency stop state. As usual, we have to do two things:
write the transformation and plug it into the workflow. Here's the code for the transformation:
<p align="center"><img src="emergencyStop.gif"/></p>
There's a very interesting detail hidden in there: If you look closely, you'll find that
createShutDown() is called several times; once from the modify(...) function, and then
again from each of the createTransition(...) invocations. So, will we end up with several
new States? The answer is no. The really cool thing about create extensions is that, for
each set of parameters, it is evaluated only once. Subsequent invocations with the same
set of parameters will return the identical object, the function is not reevaluated!
So, since createShutDown() has no parameters, and thus, each invocation automatically
is with the same set of parameters (the empty set) the createShutDown() function is executed
only once. So each of the several invocations returns the same object. This is exactly what
we want!
The createTransition(...) function takes one parameter, the state for which is should create
the transition to the emergency stop state. Note how the properties (event, name, from state and to state)
are set. This extension is called and evaluated once for every state &ndash; again, exactly as we need it.
Now, to make this transformation execute, we have to add something to the workflow file:
<p align="center"><img src="workflowForModification.gif"/></p>
The XtendComponent is told the metamodel as well as &ndash; and this is important &ndash; the initial expression
to evaluate. trafo::AddEmergencyShutdown.modify(model) will invoke the modify operation in the
AddEmergencyShutdown.ext file (By the time you read this, the delimiter between the file and the
function (which is currently a dot) will probably have been changed to ::, which is the otherwise
standard namespace delimiter in oAW.), which is in the trafo package (remember that oAW loads all
resources from the Java classpath). The term "model" is the name of the slot into which the reader
(see the component above the XtendComponent) has put the model's root object.
Model modifications and transformations can be a bit tricky. As of now we don't have debug
support available (although we're working on it). Consequently, it is essential to test such
a transformation. How to you do this? We won't show the code, just outline the process:</p>
<li>You define an example model (such as the CD player)
<li>You run the generator
<li>In the workflow file of the example, after running the generator workflow, you add a
CheckComponent that verifies an additional set of constraints
<li>these constraints are specific to the example. They could verify things such as:
there has to be exactly one state called EmergencyShutDown, from all normal states,
there needs to be a transition that leads to that singleton EmergencyShutDown state, and so on.
<li>By structuring your workflow files suitably, you can also run the specific constraints
not after the generator has run, but rather, after the transformation.
<li>You can also set up a specific workflow file that only reads the example model, runs
the transformation, and then checks the specific constraints.
<h2>Model Transformation</h2>
There's something we hadn't told you yet, and you probably didn't notice it either. And that is
that the code generation templates aren't based on the same metamodel as the GMF editor.
We could have based them on that same metamodel, and often that's a good choice, but to
demonstrate model transformation, we added an "intermediate" model.
So this intermediate metamodel is actually considerably simpler but contains all
the necessary information to generate the code. Here's a screenshot of the simplified metamodel.
<p align="center"><img src="simplemm.gif"/></p>
As you can see, the inheritance hierarchies are removed, actions are restructured to be
owned by the state machine, the bidirectional associations between states and transitions
are removed, we have no composite states anymore, etc. etc.
Obviously, we need a transformation that transforms the model we created in the GMF
editor to a model that is an instance of that new metamodel. Here's the transformation.
<p align="center"><img src="gmf2simple.gif"/></p>
There are a couple of things in that file that are worth mentioning.
<li>First of all, there are two metamodels in use here. The unqualified metaclasses are from
the statemachine2 package, i.e. the metamodel used in the GMF editor. They are unqualified,
because the package is imported at the beginning of the file. If we want to work with a
metaclass of the simple state machine metamodel (the metamodel just shown), we have to explicitly
qualify it with simpleSM.
<li>You can see a number of private functions. They can only be called from within the
same extension file.
<li>You can see that there's a switch statement available. Note that you have to specify a
default case, since the switch itself will evaluate to a value - it is actually an expression!
<li>You can see the same function defined for the different types. This works in case the
types are different (overriding) or in case they're subtypes, in which case a polymorphic
invocation will be performed.
<li>cached functions are only evaluated once per unique set of parameters, as explained above.
create functions are implicitly cached.
<li>Finally, you can define strongly typed Collections as in List[Action].
So as you can see the language used for model-to-model transformations is quite
powerful and ready for practical use. This is even more true if you consider to metamodel-aware
editor and the upcoming debugger.
<h2>A Textual Editor</h2>
It is not always useful to describe models in a graphical fashion. More often than you'
d thing what you really want is a textual DSL. To make working with a textual DSL nice
and easy, you'll probably want a nice editor for that language, one that features
syntax coloring, real-time constraint checking and maybe even code completion.
In short, you want the same level of convenience that GMF provides for graphical editors
for your textual ones. xText can do that for you.
<h3>How xText works in Principle</h3>
The xText Framework works as follows:
<li>You define the syntax of your textual notation in an EBNF-like form.
<li>You run the xText generator. It creates
<li>a representation of the AST (the metamodel) as an EMF instance
<li>a parser component that instantiates a dynamic instance of that metamodel when it
parses the textual model; this component can be used in oAW workflow files!
<li>Finally, it creates an Eclipse editor plug-in that provides a nice
editor for the respective syntax.
<li>You can implement additional constraints (in oAW's Checks language, as usual), that
are validated in real-time during the editing process</li>
<h3>Defining an Editor for simple State Machines</h3>
In the context of this example, we want to provide an editor for the simple state machine metamodel
(the one to which the transformation above transforms). The process to get there is as follows:
<li>define a concrete syntax, generate editors and parsers to build models
<li>since the AST derived from that syntax is not identical to the simple state
machine metamodel, we'll create another (simpler) transformation.
So let's start with the definition of the syntax:
<p align="center"><img src="grammar.gif"/></p>
The Grammar language consists of two core abstractions: Rules and Tokens. Currently, the following
token types available:
<li>keyword or symbol (e.g. "state", "{")
<li>ID (an identifier)
<li>STRING (a string)
The main concept is Rules. Let's look at an example, the State rule from the above screenshot
(the third paragraph). Each rule has a name (State). This is by convention the name of the
corresponding AST type, so the generator will create a metaclass State. In our example, the concrete
syntax for a state starts with the keyword state followed by an identifier (i.e. ID) which is
assigned to the property "name" of the generated metaclass. Then an opening curly brace
(i.e. "{") is expected. Next up one or more Actions (described in its own rule) are assigned
to the reference entryActions. The '+=' operator specifies that entryActions is a list, and the
Action should be added to it. Then one or more Transitions are added to the transitions reference,
before the following actions are added to the exitActions reference. The description of a state is
terminated using the closing curly bracket ("}").
As a consequence of this definition, the derivable (and thus, generated) metaclass State (also referred
to in this context as AST type) needs to look as follows:
<li>a property name of type String
<li>a reference entryActions of type List of Action
<li>a reference transitions of type List of Transition
<li>a reference exitActions of type List of Action
In addition to the rules that define concrete syntax (as shown above), there are also abstract rules.
As an example, look at the AbstractState rule in the screenshot above.
An abstract rule (preceded by the Abstract keyword) will result in an abstract AST type (AbstractState).
The body of the rule consists of a sequence of alternative rules (here: State vs. CompositeState). Since
the AST types of the alternative rules (State and CompositeState) must be compatible with this rule's
abstract AST type (AbstractState), the metamodel generated by xText automatically contains a corresponding
type hierarchy. Also, xText normalizes the types (i.e. moves properties contained in all subtypes
to the abstract super type). The derived metamodel of the state machine example shows how all
the general features for State and CompositeState have been moved to there common super type AbstractState.
<p align="center"><img src="grammarAST.gif"/></p>
Note that the automatic generation of the AST metamodel is optional! You could design it manually, if you want to.
Of course, one piece of the puzzle is still missing: the constraints that should be validated
in the editor haven't been defined yet. So let's do this; again, we simply write a .chk file, possibly
accompanied by a set of extensions defined in an .ext file. Here's the metamodel (left side) and
the defined constraints (right side).
<p align="center"><img src="xtextconstraints.gif"/></p>
<h3>Running the Editor</h3>
Let's look at the generated editor. As mentioned, it provides syntax highlighting and
real-time constraint checking (constraints are evaluated once you save the file; this is the
standard approach in Eclipse).
<p align="center"><img src="generatedXtextEditor.gif"/></p>
<h3>Integrating the Parser into the Workflow</h3>
Just as we used the models created by GMF in the example generator, we also want to
be able to use the textually described state machines in a generator workflow. Here's a
workflow file that shows that one. Some explanations are in order:
<li>first we declare a bean, that gets its contents from a file generated as part of the
xText editor generation that declares the metamodel to be used
<li>Then we call the parser cartridge, also generated by the xText generator. This cartridge reads
the model and stores the instance in the "model" slot.
<li>Then, as in the GMF example, we run the constraints that were already used in
the generated textual editor
<li>Next up is the transformation of the instance of the textual syntax into
the simple state machine metamodel.
<li>Finally, we call the backend cartridge; this one does the actual code generation based
on the simple state machine instance. This is the exact same backend cartridge that's also
used from in the GMF example after it has been transformed to the simple state machine metamodel.
<p align="center"><img src="xTextworkflow.gif"/></p>
This wraps up our tour of Eclipse, EMF, GMF and openArchitectureWare. We have seen a complete
tool chain, from front end to code. Note that all the building blocks are nicely modularized
and reusable, and each comes with nice Eclipse editor support. Future enhancements of openArchitectureWare
will be take place in all kinds of directions (see our development roadmap at [<a href="#OawRm">OawRm</a>]); a
particularly interesting area we are working on is debugger support. For more information on
openArchitectureWare go to [<a href="#Oaw">Oaw</a>].
We would really like to give us feedback. You can reach us through the URLs
given at the top of the article.
<li><a name="GmfTut">[GmfTut]</a> GMF Tutorial Wiki, <a href=""></a></li>
<li><a name="MS02">[MS02]</a> Miro Samek, Practical Statecharts in C/C++, CMP Books, 2002</li>
<li><a name="Oaw">[Oaw]</a> openArchitectureWare, <a href=""></a> and <a href=""></a></li>
<li><a name="OawRm">[OawRm]</a> oAW Development Roadmap, <a href=""></a></li>
<li><a name="RV05">[RV05]</a> Rudorfer, Voelter, Domain-specific IDEs in embedded automotive software, <a href=""></a></li>
<li><a name="SV06">[SV06]</a> Stahl, Voelter, Model-Driven Software Development, Wiley, 2006, see <a href=""></a></li>