Extending Eclipse EMF with statechart-based specification of model constraints Mariana Lasprilla

advertisement
Extending Eclipse EMF with statechart-based
specification of model constraints
Mariana Lasprilla
Matriculation # 23654
Supervisors
Prof. Dr. Joachim W. Schmidt
Prof. Dr. Volker Turau
MSc. Miguel García
A Thesis submitted in partial fulfillment of the requirements for the degree
of Master of Science in Information and Communication Systems
Hamburg, November 2004
I declare that:
this work has been prepared by myself,
all literally or content-related quotations from other sources are clearly pointed out,
and no other sources or aids than the ones that are declared are used.
Hamburg, 08.11.2004
Mariana Lasprilla
2
Table of Contents
Chapter 1: Introduction............................................................................................... 6
1.2.
Task description............................................................................... 7
1.3.
Related work ................................................................................... 7
1.4.
Structure of the thesis ...................................................................... 8
Chapter 2: The Eclipse framework.............................................................................. 9
2.1.
Extensibility via plug-ins ................................................................. 9
2.2.
The Eclipse Modeling Framework (EMF)...................................... 11
2.2.1. Basic concepts ............................................................................. 11
2.2.2. Limitations................................................................................... 15
2.3.
Java Emitter Templates (JET) ........................................................ 16
Chapter 3: Specification of model constraints ........................................................... 18
3.1.
Design by Contract ........................................................................ 18
3.2.
The UML approach: OCL.............................................................. 18
3.3.
ECA rules ...................................................................................... 19
3.4.
Statechart Diagrams....................................................................... 20
Chapter 4: Analysis and Design ................................................................................ 23
4.1. Choice of the constraint representation ............................................... 23
4.2. Design guidelines ............................................................................... 24
4.3. The input: Omondo EclipseUML........................................................ 26
4.4. Model of a statechart diagram............................................................. 27
4.5. Mapping from model to code .............................................................. 27
4.6. Interconnection to EMF code.............................................................. 31
4.7. Code generation.................................................................................. 33
4.8. Architecture: the overall system.......................................................... 33
Chapter 5: Implementation........................................................................................ 35
5.1. Implementation highlights .................................................................. 35
5.2. Usage ................................................................................................. 38
Chapter 6: Evaluation ............................................................................................... 41
6.1. Case Study I: Purchase Order.............................................................. 41
6.2. Case Study II: Well-formedness rules in UML2 Editor ....................... 43
6.2.1. Background: the Eclipse UML2 project ....................................... 43
6.2.2. Some well-formedness OCL rules................................................ 44
6.2.3. Results ......................................................................................... 50
Chapter 7: Summary and Conclusions ...................................................................... 52
7.1. Summary ............................................................................................ 52
7.2. Future work ........................................................................................ 53
Appendix A: Omondo EclipseUML.......................................................................... 54
Installation ................................................................................................ 54
Usage ........................................................................................................ 54
3
Usage with the StatefulEMF plug-in.......................................................... 55
Appendix B: Restrictions of the StatefulEMF plug-in version 1.0 ............................. 56
Bibliography............................................................................................................. 57
4
Index of Tables, Code and Figures
Figure 2-1: Eclipse Platform Architecture (taken from [Griffin04])........................... 10
Code 2-1: Plug-in manifest for an HTML editor ....................................................... 10
Figure 2-2: The Ecore Model (from [EMF04]).......................................................... 12
Figure 2-3: Execution of the EMF „Validate“ action................................................. 15
Code 2-2: A first JET template ................................................................................. 16
Figure 2-4: JET Generation process (adapted from [Popma04]) ................................ 17
Figure 3-1: MP3Player Statechart ............................................................................. 21
Figure 4-1: The process of obtaining a statechart-augmented implementation ........... 24
Code 4-1: A simplified EclipseUML statechart file................................................... 26
Figure 4-2: Statechart model in StatefulEMF ............................................................ 27
Figure 4-3: Statechart model in UML ....................................................................... 28
Table 4-1: Java representation of statechart elements ................................................ 28
Code 4-2: Java code extract of MP3 Player statechart ............................................... 29
Figure 4-4: Sequence diagram of StatefulEMF.......................................................... 34
Table 5-1: Template names and description .............................................................. 36
Figure 5-1: Opening the StatefulEMF editor ............................................................. 39
Figure 5-2: Error on selection of a non-class object................................................... 40
Figure 6-1: Class Diagram of our Purchase Order model .......................................... 41
Figure 6-2: PurchaseOrder Statechart Diagram ......................................................... 42
Figure 6-3: A look on the PurchaseOrder EMF project ............................................. 43
Figure 6-4: Generalizations in UML2 ....................................................................... 45
Figure 6-5: Statechart of UML2 Generalization class without circular inheritance .... 46
Figure 6-6: Statechart of Property class without doubled attributes ........................... 47
Figure 6-7: The UML2 GenModel ............................................................................ 48
Code 6-1: Generalization.eSet() method with dynamic interface ............................... 48
5
Chapter 1: Introduction
1.1.
Motivation
The world of software applications is a rapidly changing one. Development
teams are daily confronted with increasingly complex projects and tighter
deadlines; the fast pace of the technology field puts mounting pressure on software
designers and programmers. At the same time, computer-based services and
devices pervade more of our daily activities. From banking to music stores, from
ERPs to mobile phones to airline reservations, much of our society relies on the
proper functioning of computer programs. A software bug translates to big
financial losses, sometimes even personal damages.
These two concerns of speed and reliability have strongly shaped the
current development of the software industry: the latter puts pressure on
programmers to bring forward the software creation process from a craft to a real
engineering discipline, with proved, trustable methodologies and results; while
developers increasingly look for tools that automate the repetitive implementation
tasks and let them speed up results and focus on the fundamental, high-level system
analysis and modeling.
Several methodologies emerging in the last years proposed different
approaches to deal with both challenges, but what most methods have in common
is an emphasis on the importance of a clear system definition and analysis via
modeling tools and languages, since only so can reusable, reliable, testable systems
be obtained. The most successful modeling language is by far UML, already a de
facto industry standard. Many IDEs in the market propose UML as a means to ease
and strengthen the software development process, with varying success.
Eclipse is an open source Java-based IDE with a strong and fast-growing
market share; its high potential for extensibility has made it a popular platform for
software teams to produce their own applications and development tools. Its
contribution to UML-based development has taken the form of the Eclipse
Modeling Framework (EMF); this tool receives the underlying structural model of
an application as a UML class diagram and is able to generate robust, extensible
code for the model and a basic editor.
However, the model characterization in EMF is currently limited to the data
that can be expressed in a class diagram. Information like well-formedness rules
and dynamic behavior must be manually coded, thus it is implementationdependent and difficult to update; the coding is often repetitive and error prone.
It is therefore a logical step in the evolution of EMF to contemplate the
support for additional information inside the EMF model of a system. The
inclusion of well-formedness rules and dynamic behavior at the modeling layer, to
state constraints and complex actions in an unambiguous, explicit manner, appears
particularly important to achieve the goal of robust applications.
6
1.2.
Task description
This thesis focuses on proposing an architecture to integrate wellformedness rules into models built with Eclipse EMF. In particular, we want to
analyse the suitability of UML statecharts for such inclusion, and how well this
proposal achieves the benefits of (a) greater correctness and clearness, through
explicit constraints at design level, and (b) efficiency, through the automatic
generation of code that binds the constraint implementation to the rest of the
model. The main goals of this work are thus to:
Design and implement an extension of Eclipse EMF that includes UML
statechart diagrams into the application model and generates suitable code from
the diagrams.
Evaluate the adequacy of the solution in implementing the behavior of reactive
systems
Analyze the usefulness of the tool in incorporating system constraints at the
model level and in implementing them efficiently.
The steps to follow during the project are: first an analysis of the current
situation of constraint integration in the EMF environment and of several options to
achieve the integration; then the selection of a suitable alternative; the design of the
Eclipse extension; the assessment of the solution using a real-life, sufficiently
complex test case; and finally the deduction of conclusions and possible directions
of further development.
1.3.
Related work
There has been strong research on the development of tools supporting
OCL-enhanced UML diagrams, most outside of the Eclipse realm. The only work
currently applicable inside Eclipse has been done by Akehurst and Patrascoiu
[AP03] at the University of Kent: the resulting Kent Modeling Framework, a
project similar to EMF, can evaluate constraints defined in OCL against runtime
instances of a model. A connection of the KMF library to EMF has also been
developed, [Wahler04] describes how to incorporate the OCL processor on EMF.
However, the OCL for EMF tool works only as an assessment of existing runtime
instances, and it does not automatically bind the OCL constraints to the existing
code, instead the user must manually create an OCL processor instance and
explicitly call each rule evaluation. Additionally, the reliance on OCL brings
several limitations, discussed in more detail on chapter 3.2. As of this writing, the
project does not offer a graphical interface for the definition and evaluation of
constraints.
On general OCL-enabled tools, Cañete et al [CGT] have developed their
own CASE software that generates Java code based on OCL-augmented class and
statechart diagrams. The tool is limited in the number of OCL expressions it can
interpret, and no user interface for modeling is available, which limits its usability.
Verheecke [Verheecke02] thoroughly analyzes the mapping from OCL constraints
at the model stage to implementation classes; his framework does a better job than
KMF in automatically identifying the “insertion points” where a constraint should
7
be bound to the model code. It also offers a visual interface, although as an
extension of the Argo/UML CASE tool.
The use of other alternatives to define constraints and compound actions at
the design stage appears less explored. Liu et al [LEM02] make use of production
rules to detect –not prevent- model inconsistencies. Cibran [Cibran02] discusses
the use of aspect-oriented programming to define and enforce business rules while
maintaining them decoupled from the implementation code.
1.4.
Structure of the thesis
This work is structured as follows:
Chapter 2 describes the basic concepts of the Eclipse platform and of EMF,
it also introduces other technologies and tools that are of importance during
the implementation.
Chapter 3 discusses the main concepts regarding model constraints and
different approaches for their expression.
Chapter 4 presents the design of our extension tool as an Eclipse plug-in.
Chapter 5 briefly describes highlights of the implementation process and
extracts lessons that may be of use in future Eclipse extension projects
Chapter 6 evaluates the resulting tool with two use cases: a first test is done
with a simple PurchaseOrder model, then a more real-life evaluation is done
on the existing Eclipse UML2 editor.
Chapter 7 draws conclusions from the development and evaluation, and
proposes directions of future work.
8
Chapter 2: The Eclipse framework
The Eclipse Technical Overview [Eclipse03] defines the platform as “an
IDE for anything, and for nothing in particular”. Aiming for a bit more preciseness,
we could define Eclipse is an open-source project, under the supervision of the
eclipse.org consortium, which aims to offer an extensible framework for building
development tools. The core Eclipse platform provides, besides some generic
functionality, the foundation to construct and seamlessly integrate a wealth of tools,
regardless of the source or the type of content to manage. Through a well-defined
API interface, anyone can create their own tool to perform a specific task –from an
editor for their preferred programming language to a project management tool or a
logical gate designer, almost anything is possible. This flexibility, added to the high
quality of available editors, has made Eclipse one of the most popular IDEs
worldwide [Cassia04].
The next sections describe in more detail the extension mechanism of
Eclipse; in particular the development of visual editors through the Eclipse
Modeling Framework (EMF) and related technologies.
2.1.
Extensibility via plug-ins
Plug-ins are the basic components of Eclipse, each providing a specific
functionality to the framework; besides some core actions, every service in Eclipse
is provided by plug-ins. A plug-in is usually composed of the following:
Java classes to execute the operations, often packaged in a JAR file,
Text files containing plug-in localization properties or help files.
A manifest file (plugin.xml) describing in XML format the main features of
the plug-in: its main class, associated file extensions, and the
interconnection to other plug-ins.
Optionally images, native code libraries, etc.
A plug-in adds processing elements, e.g. buttons and menu items, to the
base Eclipse workbench via extensions. An extension can either add new elements
or customize existing ones to either the workbench or other plug-ins that allow this
mechanism. Plug-ins that let themselves be extended declare the possible slots of
extension on their plug-in manifest; these sockets where other plug-ins can plug to
are called extension points. Figure 3-1 illustrates the main Eclipse architecture and
the plug analogy of component interaction.
To briefly examine the extension point mechanism, let us consider we want
to create an editor for HTML code. To do so, we must create a plug-in with an
extension on the „org.eclipse.ui.editors” extension point. Our editor can add items
to the toolbar and the workbench menus by defining a Contributor Class. Code 3-1
shows an extract of our plug-in manifest:
9
Figure 2-1: Eclipse Platform Architecture (taken from [Griffin04])
Code 2-1: Plug-in manifest for an HTML editor
<extension point = "org.eclipse.ui.editors">
<editor
id = "de.tuhh.sts.mlasprilla.examples.htmltool.HtmlEditor"
name="My HTML Editor"
icon="icons/obj16/editor.gif"
class="de.tuhh.sts.mlasprilla.examples.htmltool.HtmlEditor"
extensions="html"
contributorClass="de.tuhh.sts.mlasprilla.examples.htmltool.HtmlEdit
orActionBarContributor">
</editor>
</extension>
Since an HTML editor shares much of its functionality with a text editor,
the HtmlEditor class would best subclass the existing TextEditor class in Eclipse:
org.eclipse.ui.editors.text.TextEditor, saving us some implementation work. In
general, the development of new plug-ins is supported by the two mechanisms of
extension points to interconnect components and subclassing to reuse existing
functionality. If our plug-in is supported by another plug-in in any of those
manners, then the latter constitutes a dependency of our plug-in; the list of plug-ins
our project depends on is also declared on the manifest file, and if a dependency is
not installed in the current Eclipse workspace, then our plug-in cannot be activated.
In order to ease the development of new plug-ins, Eclipse provides the
Plug-in Development Environment (PDE), a set of tools that “assist the Eclipse
developer in developing, testing, debugging, building, and deploying Eclipse plugins while working inside the Eclipse workbench”. The process of building a plug-in
using PDE is described in detail in [MG03] and [Shavor03].
10
2.2.
The Eclipse Modeling Framework (EMF)
The Eclipse Modeling Framework is a Java framework and code generation
facility for building tools and other applications based on a structured model. Two
key concepts are achieved by EMF:
Data integration: a user can define the model behind a certain application in
different formats depending on his individual preferences –he can draw a
UML class diagram, write a set of Java interfaces or an XML Schema.
Before EMF, it would have been very costly to exchange data between these
different representations, even when the semantics behind –the model they
represent– was the same. EMF eases the data exchange by generating one
default XMI (XML Metadata Interface) representation from any of the
sources above; thus it is said that EMF unifies Java, XML and UML.
Code generation: to let the user focus on the high-level model and not on the
implementation details, EMF can generate skeleton Java code corresponding
to a given model, as well as a simple tree-based editor for model instances.
Furthermore, the generated code already includes useful features like
change notification, persistence and model validation.
Section 2.2.1 below describes the basic concepts behind EMF, and 2.2.2
discusses the pros and cons of the framework.
Note: unless otherwise specified, this document refers to Eclipse version 3.0.0,
build 200406251208, and EMF version 2.0.0, build 200406280827.
2.2.1. Basic concepts
A Model in EMF
A model is an abstraction used to describe a system in an orderly manner.
For developers with knowledge of object-oriented analysis and design, it is often
equal to the combination of several UML diagrams that best define an application.
In EMF, though, a model is a subset of UML, including only the basic concepts of
a Class diagram: definition of classes, their attributes and operations, and
associations. As of today, EMF can handle 4 types of model input:
UML class diagrams drawn a compatible model tool. Currently EMF
supports only the Rational Rose format.
Ecore model: a user can give as input the final EMF model representation,
known as Ecore and described in more detail below. Since Ecore is written
in XMI, a very knowledgeable modeler can write the model directly in a
text editor. More practical though is to use a graphical modeling tool that
allows export to the Ecore format. Omondo EclipseUML [Omondo] is one
such tool; full description is done in Appendix A, but for now, suffice it to
say that it is a commercial Eclipse plug-in for drawing UML diagrams, and
that it also outputs class diagrams in Ecore format.
XML Schema that specifies what instance serializations would look like.
Annotated Java interfaces: users not familiar or not interested in high-level
modeling mechanisms can give partial Java interfaces, which annotations
11
(comments marked with the @model tag) to specify things like whether an
attribute is read-only or a class is abstract.
As said before, EMF will generate from any of the input formats an Ecore
version of the model: a text file with the “.ecore” extension that describes the
model in XMI. This file constitutes the fundamental information of an EMF
project.
Ecore: The EMF Metamodel
EMF itself is based on a model known as Ecore, shown on figure 2-2. We
mentioned before that an EMF model defines a class’ attributes, operations and
associations; these data, plus some additional details, are represented in the Ecore
as objects of one of the 12 non-abstract classes shown in the figure –the abstract
classes are shown in blue.
Figure 2-2: The Ecore Model (from [EMF04])
Some mappings are obvious, for example, a class in the UML class diagram
is represented in the Ecore as an EClass object. Others are not so direct: an
association is represented indirectly through its association ends, objects of the
EReference class. All the Ecore objects are serialized as XML nodes in the “.ecore”
file. Notice that all EMF objects implement the EObject interface.
12
It is also worth noticing that all classes on the Ecore correspond to elements
in the UML model; however, Ecore is a simplified subset of the full UML,
focusing only on class modeling.
EMF Model, Edit and Editor Plug-ins
A key goal of EMF is the generation of Java code from an Ecore model.
The output code is by default distributed in 3 plug-ins:
Model: The main elements of the Ecore are translated into classes. Each
EClass in the model is coded as 2 files: a Java interface named after the
EClass’s name (e.g. PurchaseOrder.java for a class called PurchaseOrder),
and an implementation class which attaches „Impl“ to its name (e.g.
PurchaseOrderImpl.java). The interfaces are located in the base package, the
classes in a second implementation package (basePackage+“.impl“). A third
optional package contains code to handle change notification, explained
later.
EMF.Edit: The second plug-in contains classes to edit a model on a
command basis, independently of the user interface. Sample commands are
Add, Copy and Set. Each executed command is kept in a CommandStack
object, providing unlimited undo and redo.
EMF Editor: The last item encapsulates the UI-dependent parts of the
generated model editor; its classes will then produce a tree editor for our
model.
EMF Edit and Editor plug-ins represent a bridge between the core model
implementation and the Eclipse UI framework, and are very useful if one wants to
have an easy-to-use editor for model instances with only a few clicks. They are
unnecessary, though, if one plans to handle model instances via code, or if a
different graphical editor is desired. For the latter case of building a custom visual
editor, the Eclipse Graphical Editor Framework (GEF) is the tool of choice.
Codegen: Generating the Java code
The code generation given an Ecore model is achieved in two steps:
Step I: Create the Generator Model file (.genmodel)
The Ecore file contains most of the data the generator needs to output the
Java implementation. Yet certain variables to better configure the output, mostly
package and directory names, are still missing; EMF will generate default values
for each of these variables and store them in a Generator Model file –an XMI file
with the same name as the Ecore model but the .genmodel extension. The
Genmodel is also modeled in EMF, it contains among other elements the base
directory where all packages will be generated and the plug-in class for the model
editor. The file can be viewed and modified using the GenModel editor; the editor
will show a tree representation of the model; the tree root represents the whole
model, its child is the package, containing as children all classes.
Step II: Generate the code
13
To generate code, we must open the GenModel editor and right-click on a
node. Four actions will be shown: Generate Model Code, Generate Edit Code,
Generate Editor Code, to generate each of the 3 output plug-ins, and Generate All
to get them all at once. If we select the Generate All action on the root element, all
the necessary code for the model and its editor will be generated.
An important aspect of the code generation is the ability to regenerate, that
is, to keep model and implementation in synch. But since the implementation is
intended to be a mix of generated code –e.g. a method signature- and hand-written
code –most method implementation-, the code generator needs to know what to
overwrite and what to let as-is, this is done with the @generated marker on the
Javadoc comments. Only those methods that include the marker will be overwritten
during a regeneration, the rest will be left untouched. Initially all generated
methods will include the marker, so the developer must erase the tag on every
method that he manually changes. More details on the merging process during a
regeneration are explained on [BSMEG04].
Change Notification
Following the Observer Design Pattern [GHJV95], EMF provides a
mechanism to attach observers, also known as adapters, to an object. Every object
in EMF, instance or subclass of EObject, implements the Notifier interface. An
adapter can attach itself to the object using the object eAdapters().add() method,
so that whenever the object changes, the adapter notifyChanged() method is
called. The method can then take care of updating a property viewer, checking a
rule, incrementing a counter or another desired behavior we want to execute.
Model Validation
Starting from version 2.0.0 (June 2004), EMF includes the ability to validate
a model instance against certain restrictions like multiplicity. The “Validate” action
is a posteriori, that is, it does not prevent model inconsistencies from occurring,
instead it detects them and points to a solution. The EMF validation framework is
still under development and there is no documentation available for the user, so far
it appears the validation is limited to those attribute restrictions that can be defined
in an XML Schema file –e.g. required vs optional, lower and upper bounds.
Figure 2-3 shows what the error messages resulting from a Validate action
look like: in the example an instance of a PurchaseOrder model, which Ecore limits
the maximum number of Item children to 5, has defined 6 Items. Upon executing
the Validate action, EMF returns the message “Problems encountered during
validation” and the reason for the failure.
14
Figure 2-3: Execution of the EMF „Validate“ action
2.2.2. Limitations
The advantages of EMF should be clear by now: mainly the possibility to
store and share structured data regardless of the initial input format, and a useful
code generation facility that automatically produces a robust, flexible, efficient
model implementation. Though these functions make EMF a helpful tool in many
situations, there are still several limitations that restrict its usefulness.
EMF’s biggest drawback is arguably the limited modeling data it handles:
only the subset of UML class diagrams covered by the Ecore. All other data, from
OCL constraints to every other diagram in UML, cannot be kept in an application’s
Ecore model. It was decided early on to keep the Ecore model small in order to
maintain simplicity and offer a low barrier of entry to developers, even to those
with little modeling experience. This decision has supported the popularity and
robustness of EMF, but as a payoff, a wealth of benefits in terms of model
completeness and code generation are currently ignored.
In a related note, the simplicity of EMF makes its generated code not much
more than a skeleton of the application, meaning, as discussed on [Verheecke02],
that most of the implementation work is still done manually, with negative
consequences on productivity and accuracy. Similarly, [MDGWV04] states EMF is
unsuitable for developing and deploying applications at a company wide level that
combine several technologies like XML, EAI, EJBs and Web services.
15
Other less serious limitations include the few compatible graphical
modeling tools –only Rational Rose and Omondo EclipseUML as of this writing--,
and the tight coupling of EMF with Eclipse as the IDE and Java as output code.
These barriers are not unsurmountable: it is already possible to use EMF outside of
Eclipse, and to produce output code in a programming language besides Java, if
only with heavy configuration; while the inclusion of input formats from other
modeling tools should be addressed in the near future.
2.3.
Java Emitter Templates (JET)
Eclipse provides a very useful tool for code generation, called Java Emitter
Templates (JET). Although JET is shipped as part of the EMF project (and is the
basis of EMF’s own code generation), it is a generic template engine that can be
used to obtain any kind of text output including Java code, but also HTML or SQL.
The developer defines what kind of output he wants using a JET template: a text
file, with a name ending in “jet”, where the desired output is expressed using a JSPstyle syntax. A template often includes regular text –the fixed parts in the outputmixed with Java code enclosed between the <% and %> tags. Variable values can
be written into the output using the shortcut <%= some text %>. The template can
receive Java objects as arguments.
Code 2-2 shows a very simple JET template which receives a String as an
argument and outputs the text: “Hello [inputString]!”.
Code 2-2: A first JET template
<%@ jet package="hello" imports="java.util.*" class="GreetingTemplate" %>
Hello, <%=argument%>!
The JET generation process can be done in a visual manner inside an
Eclipse Workspace project using the “JET Builder” tool. More important, it can
also be run in code using the org.eclipse.emf.codegen.jet.JETEmitter class. To
generate code, one must create an instance of JETEmitter, then call its generate()
method. This will execute 3 steps:
i.
Configure the workspace for code generation: create a .JETEmitters
project and add to its classpath the packages defined on the template
“imports” attribute.
ii.
Translate the template to a template implementation Java source file in
the .JETEmitters project, then compile the source into a .class file.
iii.
Call the generate method on the implementation class and return the
generated text as a String
Figure 2-4 illustrates the code generation process.
16
JET
Template
In plug-in
directory
translate
Implementation
class
In .JETEmitters
project
generate
Generated
code
In client
project
Figure 2-4: JET Generation process (adapted from [Popma04])
17
Chapter 3: Specification of model constraints
We’ve already mentioned that a model serves the main purpose of
describing a software system; a big part of this description consists of restricting
the model instances that are to be considered valid in our system. The mechanism
to achieve such restriction of the universe of possible instances is precisely the
specification of model constraints.
Wagner et al [WTB03] define an integrity constraint, also known as
integrity rule, as a logical sentence which most hold in all evolving states and
transitions of the system for which it is defined. Other types of rules defined by
Wagner, as derivation (conditions that lead to a conclusion), and production rules
(conditions that lead to an action) are outside the scope of this project.
Some integrity constraints can be implicitly expressed through the basic
elements in a UML class diagram, others, which we call user-defined constraints,
require a different mechanism. On the sections below we describe several
mechanisms to express such rules.
3.1.
Design by Contract
One significant step in the area was Meyer’s Design by Contract
[Meyer04], a technique which states the collaboration between different
components of a software system should be based on contracts: precise
specifications of what each component expects and guarantees. The specification is
done using assertions, a concept equivalent to constraints; assertions are divided in:
Pre-conditions state what the caller must satisfy before executing an
operation
Post-conditions describe what the operation must return or what it will do –
but now how it will do it,
Class invariants are assertions that must always be true for every instance of
a class.
Design by Contract was integrated to the Eiffel language developed by
Meyer, so that Eiffel offers syntax to implement each type of assertion through the
require, ensure and invariant keywords, respectively. Although Design by Contract
is a method independent of Eiffel, other programming languages do not directly
support assertions, so the effort to implement them –the decision on how to and the
responsibility on doing it correctly, by adding the right checks in all places where
an assertion could be violated—are left to the programmer.
3.2.
The UML approach: OCL
According to the UML specification [OMG99], user-defined constraints can
be added to a UML diagram by means of tags, which are simply text strings
surrounded by braces ({}). The nature of the text between the tags can be freely
defined by the modeler. UML does not force the usage of any specific constraint
language; even natural language can be used. This means the modeler does not
need to learn yet another grammar in order to specify his models, but as a pay off,
the constraints are often ambiguous and lead to implementation errors; additionally,
18
the automatic translation to implementation code is made virtually impossible by
such flexibility.
To address those issues, UML proposed the usage of a predefined
language, the Object Constraint Language (OCL). In OCL, a constraint is defined
as a restriction on one or more values of an object-oriented model system. Similar
to Design by Contract, constraints are classified as invariants, pre- and postconditions. Each OCL expression is written in the context of an instance of a
specific type; such context is accessible through the „self“ keyword. Other
keywords and operations allow the declaration of types, the access of object
properties and, to be expected, the application of boolean and algebraic operations.
It is important to note that OCL is a pure expression, not a programming language:
it formally describes a constraint, but is not executable and cannot be used to write
program logic or control flow.
Although OCL is part of the UML specification, and practical studies show
its potential to improve modeling results (see [BLYD03]), it is still among the least
used tools of the specification. Besides the entry barrier posed by the effort to learn
a new language, the acceptance problems appear tied to several deficiencies in
OCL:
[FM02] discusses that OCL lacks syntactical means to handle constraints on
dynamic behavior, which makes the language unsuitable for models of realtime and highly reactive systems.
The OCL feature of “no side effects” means the evaluation of a constraint
cannot change anything in the system; it can only tell whether the system
state is valid. Therefore, in the case the result is invalid, no action –either to
restore the system or to give adequate feedback to the user—can be defined
in OCL. The OMG consortium has developed a complementing
specification, the Action Semantics [], to be able to define actions at the
modeling level inside the UML.
[VJ99] sustains the language is often unnecessarily verbose and hard to
read, and though it was designed to be a conceptual language, it is closer to
implementation.
Most object-oriented modeling tools available as of today handle OCL
constraint similar to comments: namely a OCL expression can be added to a UML
diagram, but code completion, validation or automated implementation of the
expression are not possible.
3.3.
ECA rules
The concept of Event-Condition-Action rules originally developed in the
field of active databases, where they are also known as database triggers. An ECA
rule is basically an activity to be executed when the associated triggering event
occurs, if and only if the defined condition is valid at the moment.
Unlike OCL, ECA is not a specific constraint language, it is only a
paradigm on how to express certain model constraints by associating them with 3
elements: an event, one or more conditions and an action. The language in which
these elements are defined can vary, it can be high or low level, it can even be OCL
19
in the case of the condition. A second difference with OCL is that ECA rules are
executable; they express control flow and, if expressed in a programming language,
can be directly compiled and executed. Additionally, the existence of an action
means a constraint expressed in ECA can attempt to correct the violation of a
constraint by applying a compensating action or by undoing the offending step.
Although ECA rules are defined as only one type of model constraints, in
practice virtually every constraint can be expressed in the form of one or several
ECA rules, by associating the ECA-event with each operation or assignment that
could lead to a violation of the constraint. In fact, tools which translate OCL
constraints from the model to the implementation often do that process: identify the
“insertion points” of the constraint –i.e. the possible triggering events—and check
the condition on each of those points. [Verheecke02] presents an in-depth
discussion on one such a tool. One advantage of using ECA directly instead of
OCL is that the former provides the modeler with a mechanism not only for
checking a constraint but also for determining which action to execute in case of
violation.
Graphical representation of ECA rules can be done using different
approaches; two of them are UML Statecharts, and Object-Process Methodology
(OPM); for a comparison between the two see [RSD02]. For the purposes of this
work we will only consider the Statechart representation of ECA rules, given its
popularity compared to OPM.
3.4.
Statechart Diagrams
A Statechart Diagram, as defined on [OMG99], is a graph that represents
the dynamic behavior of an entity –typically a class--, by describing the sequence
of states and actions the entity can take as a response to certain events. Statecharts
in UML are based on Harel state machines; they are composed of the following
elements:
The main blocks in the diagram are the states, representing the current
status of an object. When an object is in a certain state, it may be performing a
(long-duration) activity, or it may be waiting for an event to occur. An object
moves between states via transitions, represented by arrows that connect a source
and a target state. Transitions can be labeled with an ECA rule, following the
notation:
eventName(comma-separated-eventArguments) [condition] / action
The entry and exit keywords define two special action labels, the associated
actions are executed whenever a state is entered or left, respectively. Other action
labels are do, to denote an action executed as long as the element is in the state, and
include, to invoke another statechart. Internal transitions denote the case when a
state responds to an event with an action that does not cause a transition to a
different state; these transitions are represented inside the state block. Unlike selftransitions (transitions where the source state is equal to the target state), internal
transitions do not fire the exit and entry actions. Composite states are those that can
be decomposed in either several concurrent sub-states –grouped in state regions-20
or in mutually exclusive sub-states. Statecharts may also include also an initial
state, denoted with a black dot, and a final state, denoted with a dot surrounded by
a circle.
Figure 3-1 shows an example of a simplified statechart diagram for an MP3
player. The statechart has 3 states plus the initial state, and no final state.
Figure 3-1: MP3Player Statechart
Statechart diagrams are a powerful tool to represent the dynamic behavior
of a class in a clear, graphical manner; they are most useful to represent a reactive
object, part of a reactive system. A reactive object is that whose behavior is best
described by its response to outside (asynchronous) events, and where that
response depends on the object history. Wieringa [Wier03] defines the main
features of reactive systems as:
Being in continuous interaction with its environment,
Interacting in a non-terminating manner, unless a failure occurs
Responding to external stimuli as and when they occur, and being able to
respond to interrupts.
Having a response dependent on its current state and the external triggering
event
Producing a response which consists of enabling, enforcing, or prohibiting
communication or behavior in its environment
Often operating in real time and under stringent time requirements
Categories of reactive systems mentioned by Wieringa are: real-time and/or
embedded (e.g. an elevator controller), control (e.g. a cruise control), workflow
management, groupware (a distributed infrastructure for cooperation), e-commerce
(e.g. e-auctioning) and enterprise resource planning systems. In all these cases, the
use of statecharts to model certain objects is highly useful. When the collaboration
between different objects is the main focus, though, other tools like activity or
sequence diagrams are better suited for the job.
Since many concepts of statechart diagrams are not present in OO
languages, there isn’t a direct one-to-one mapping from model to implementation.
Although the translation to code can be –and traditionally has been—done in an adhoc manner, several alternatives to do a systematic mapping, especially to Java
code, have emerged in the last years. Moving away from the primitive method of
equating states to an integer variable and switch statements, Sane and Campbell
21
[SC95] proposed the mapping of states as classes and transitions as operations.
Niaz and Tanaka [NT04] extend this approach and include the State pattern
[GHJV95]; the resulting code appears robust and efficient, and it maps almost all
features of UML statecharts.
The main advantage of choosing a mapping paradigm, besides the decrease
of implementation errors achieved with a systematic approach, is the possibility to
automate the code generation process.
22
Chapter 4: Analysis and Design
In this chapter the background concepts and goals of the project serve as
basis for the design of a tool capable of integrating statechart-based rules into an
EMF model an implementation, tool we baptised as StatefulEMF. First of all, the
choice of statecharts as the representation of constraints is justified, then the steps
to be executed inside the tool are identified, and a suitable solution is proposed for
each step. Finally, the overall architecture of the StatefulEMF tool is explained on
section 4.7.
4.1. Choice of the constraint representation
Chapter 3 presented us with different alternatives to express model
constraints. Recapitulating: the concept of Design by Contract offers different types
of assertions which are unfortunately only implemented inside the Eiffel
programming language; OCL is the language of choice inside the UML realm,
while ECA rules, and their graphical representation via statecharts, are a paradigm
that expresses constraints in an executable manner.
Implementation
StatefulEMF
Most work in the subject of constraint specification focuses on OCL as the
notation of choice, however, we believe the use of statecharts, where constraints
are expressed as Java-formatted ECA rules on the diagram transition, is a better
choice for our extension of EMF, for two reasons:
OCL constraints are not able to specify actions, for example the rescue
action to execute if a constraint fails or the error to show the user when
attempting to violate a rule. [Verheecke02] proposes an architecture that
lets the user specify rescue actions for OCL, but only through significant
user interaction. This approach would mean more work for the user and
negatively affect our goal of keeping the constraint data centralized instead
of scattered around the implementation.
ECA rules can be defined as directly executable, OCL constraints are not.
OCL can be transformed to executable code, as was done by [Finger00],
[Verheecke02] and [AP03], but only
by going through an intermediate step
of moving the constraints from a
OCL constraints
declarative to an executable format –
which can very well be ECA. Thus,
instead of restricting our tool to an
OCL input, we consider it more useful
ECA rules
to build a tool that accepts input in
ECA format and let open the option of
integrating OCL support as a layer on
top of ECA.
23
In a sense, our decision to use statecharts as input mirrors the recent
outcome of the UML development group to add an Actions Semantics specification
to the UML standard; the Action Semantics also aims to overcome the nonexecutability of OCL. But although this new standard might be a UML-compliant
alternative to statecharts, we chose not to follow it mainly because of its lack of
popularity: while virtually every developer using Eclipse knows Java, and most
know class and statechart diagrams, those who can confidently use OCL are still a
low percentage –anecdotal evidence would put it around 10%-, and of those, only a
small fraction are knowledgeable in Action Semantics, a very new specification
still far from mainstream.
There are undoubtedly several disadvantages of the statechart approach
when compared to OCL: for one, OCL constraints are high-level, Java-based ECA
rules are not. This violates the ideal separation between model and implementation,
and limits the reuse of our models; on the other hand, Java code is still more
understandable for the majority than OCL syntax. Another obstacle is that OCL
constraints are usually more compact than ECA; one of the former can be often
covered by several executable rules –depending on the number of „insertion
points“ that could violate the constraint. Furthermore, our tool leaves the developer
the responsibility to adequately identify all the insertion points; but this task can be
simplified by a correct usage of object-oriented practices, e.g. writing and reading
attribute values only through the setXxx() and getXxx() methods.
4.2. Design guidelines
Analysing a bit more deeply the goals established in section 1.2, we can
describe our desired StatefulEMF tool as:
(Java files)
Statechart
file
1
loads
( .utd file)
2
Statechart
model
generates
( Java objects)
Statechartaugmented
code
0
generates
EMF
model
( .ecore file)
Figure 4-1: The process of obtaining a statechart-augmented implementation
An extension of the Eclipse EMF, programmed in Java, that takes as an
input a text-based representation of a statechart diagram and generates (a) an
appropriate Java implementation of the statechart information, as well as (b) the
code that connects this part to the existing model implementation. Figure 4-1
illustrates these steps: (0) previously an Ecore model has served as source to
implement the model in Java, then one or more statechart files –developed on an
existing visual UML tool- are added to the project, and for each (1) the file data is
loaded into Java objects, and from them, (2) the code is generated.
24
Note that since the statechart data is based on a different file and not the
Ecore, a StatefulEMF model is now composed of 2 or more files with different data
formats, somewhat detrimental to the clearness of EMF. Yet the alternative of
including the constraint data inside the Ecore is not feasible: the EMF development
decided to keep the Ecore small and simple, it is not intended to support the whole
range of model constraints –in OCL, ECA or any other representation.
Additional guidelines we would like to cover in our implementation
include:
The model and code should be able to remain synchronized –that is,
changes in the model should be reflected in the implementation. The
synchronization need not be automatic, but the user should be able to
regenerate the statechart code after making changes to the statechart
diagram. Reverse engineering –reflecting in the model changes in the
code—is not applicable.
The user interface should match the Eclipse guidelines and work as
seamlessly as possible with that of EMF.
The StatefulEMF should be as decoupled as possible from the core EMF
project, for clarity and in order not to alter the usage and robustness of the
original plug-in. If possible, the original EMF plug-in code should not be
modified; furthermore, in the model instances, keeping the statechart code
separate from the core implementation, would also bring an advantage in
terms of usability and decoupling.
Going one step further, it would be ideal to be able to change the constraints
inside the statechart without the need to recompile the whole application
afterwards. This guideline, although not required, follows the idea in
[Cibran02] that many constraints refer to business rules, which often change
at a faster pace than the main application. Think for example of a rapidlygrowing mail service: to keep up with competition, some business rules like
the allocated storage capacity per user, the access to given services by paid
or free members, the session duration, and so on, would likely be updated
more often than the core architecture of the mail service. Ideally, both parts
could be changed and compiled separately
If a total decoupling of the rule compilation from the core is not possible, it
would be beneficial to at least be able to activate and deactivate a given
transition at runtime –e.g. to disable a certain service temporarily.
Customization of the output –as that achievable via the Genmodel in
EMF—is not a core goal of the project and thus is delayed to later versions.
In this first iteration, output features like class and package names will be
fixed and equal to the default EMF values.
25
4.3. The input: Omondo EclipseUML
We mentioned above that our tool will receive an already-drawn statechart
diagram as the input; the graphical interface to create the diagram is outside the
scope of this project, and it would only constitute yet another “reinvention of the
wheel”, given the wealth of UML visual editors available. Instead, we chose to use
as the source of our input –the statechart diagram(s)- the Omondo EclipseUML
tool [Omondo], given that (a) first observations indicate it is the most popular
Eclipse plug-in to create UML diagrams, and (b) its diagrams are stored on XMI
format, rendering the data extraction simpler. Since EclipseUML is not an opensource plug-in and it does not offer the XML Schema describing statechart files, we
used several examples to infer the following data structure:
Each state in the chart corresponds to a <children> node below the root with
type “StateEditModel”; the initial and final states have types “StartEditModel” and
“EndEditModel” respectively. The name and internal transitions of a state are given
in the “itemName” and “content” attributes. Each connection sourcing out of a state
is represented as a <sourceConnection> child node of the state. The target of the
connection is given in Xpath inside the “target” attribute, the data held in the
“eventName”, “eventArgument”, “condition” and “action” attributes are clear.
Code 4-1 shows a simplified EclipseUML statechart file, with only the relevant
data displayed:
Code 4-1: A simplified EclipseUML statechart file
<editmodel:StateDiagramEditModel >
<children xsi:type="[StartEditModel|StateEditModel|EndEditModel]"
itemName="StateName"
content=""
>
<sourceConnections
xsi:type="editmodel:TransitionEditModel"
source="//@children.0"
target="//@children.1"
eventName="playBut"
eventArgument="x,y"
condition="x>0"
action="resume();"
>
</sourceConnections>
...<!—other transitions sourcing out of this state-->
</children>
...<!—other states in the chart-->
</editmodel:StateDiagramEditModel>
Based on this structure, we used the XML helper classes inside the
javax.xml.parsers package to implement the data loading. This task is encapsulated
in the StateXMI2GenModel class.
26
4.4. Model of a statechart diagram
Starting from the XMI file structure, we read the file data and store it in
several Java objects, the in-memory model of the statechart. But to do so, we need
to create a model of what our statecharts can look like –which objects they include,
their attributes and operations. Emulating the EMF architecture of building the
GenModel itself as an EMF model, we described our statechart model as a class
diagram in EclipseUML, shown on Figure 4-2, and imported the resulting Ecore
file to an EMF project, automatically generating a Statechart model implementation
code.
Figure 4-2: Statechart model in StatefulEMF
The statechart model comprises three classes: the chart itself, its states and
transitions. Transitions objects are part of their source state. Compare this to the
UML graphical statechart model on figure 4-3: our model is much simpler, given
that a first stage nested states will not be supported, and we only model complex
objects as independent classes; events, conditions and actions are treated and string
and will only be “copied and pasted” into the adequate slots in the code, thus not
requiring specific classes.
4.5. Mapping from model to code
The next design step is to define the conversion of the statechart in-memory
model to a Java implementation, which both represents the chart and binds it to the
rest of the model code. As explained on section 3.4, there are several options to
map statecharts into Java, but the proposal by Niaz and Tanaka [NT04] is ahead in
terms of completeness, therefore we will make use of it.
27
Figure 4-3: Statechart model in UML
In short, [NT04] proposes an extension of the State pattern: each state in the
diagram will be represented in a class, all of them deriving from an abstract state
class defining the general state interface. Each event defined as a state operation.
The class, whose behavior is shown in the diagram, becomes the context class and
a reference to it is kept by all state classes. Each action will be an operation on the
context class, called in the body of the event operation. The context class will also
keep a state variable pointing to the current state, so transitions are accomplished
by changing the variable value. The attributes representing the current state of the
modeled class are maintained in the context class. The mapping details are
summarized on Table 4-1:
Table 4-1: Java representation of statechart elements
Statechart item
Context class
Java
representation(s)
EMF-generated
implementation class
Details
The other statechart objects have a
private „context“ variable pointing to
28
State
One abstract state
class per statechart
One derived state
class per state
Operation on the
source state class
Operation on the
source state class
Event
Condition
Action
Transition
Operation on the
context class
Enumeration value
Entry and exit
actions
Composite
states
Operations on the
abstract state class
Context on nested
statechart
this object
Declares an operation for each unique
event in the statechart
Overwrites the operation for each event
sourcing out of the state
The method body will call the action if
the condition method returns true
The condition is encapsulated in a
„can_execute“ which returns a boolean
value
Each transition will also be uniquely
identified as a constant on the source
state.
Will be overwritten by each state that
has actual entry and exit actions
Substates are again represented as
classes, the context is the superstate
As an example, let us consider the MP3 Player statechart from figure 2-1.
The conversion to Java code produces 5 classes: the context class
(MP3PlayerContextImpl.java), one abstract state class (MP3PlayerState), and 3
concrete state classes, one per state. Note that the initial state is not represented in a
class. Code 4-2 shows what this mapping looks like –for simplicity only the code
for the Stopped state is shown.
Code 4-2: Java code extract of MP3 Player statechart
/* Abstract state class */
public abstract class MP3PlayerState{
MP3PlayerImpl context; //reference to the context object
EList transitions=new BasicEList();
MP3PlayerState(MP3PlayerImpl context){
this.context=context;
addTransitions();
}
/* Delegates incoming events to concrete state class*/
void playBut(){};
void stopBut(){};
void entry(){}
void exit(){}
abstract void addTransitions();
public EList getTransitions(){
return transitions;
}
}//MP3PlayerState
public class Stopped extends MP3PlayerState{
/*Each transition is uniquely identified by the string EVENT_TARGETSTATE,
except internal transitions, identified with EVENT */
29
static final int PLAYBUT_PLAYING = 0;
Stopped(MP3PlayerImpl context){
super(context);
}
void playBut(){ //Transition Event
if (canExecute_playBut_Playing()){
//Transition Action
context.start();
//Non-internal transitions execute exit action
exit();
context.setState(context.statePlaying);
}else{
System.out.println("playBut event Error: Conditions
for transition are not met");
}
}
//Encapsulation of condition into a method
public boolean canExecute_playBut_Playing(){
try{
return (context.getSongName().length()>0?true:false);
}
catch(Exception e){
System.out.println("Statechart
error:"+e.getMessage());
return false;
}
}
void addTransitions(){
transitions.add(Stopped.PLAYBUT_PLAYING,new
Transition("PLAYBUT_PLAYING"));
}
}//Stopped
public class MP3PlayerContextImpl extends EobjectImpl{
protected MP3PlayerContextImpl()
{
super();
stateStopped= new Stopped(this);
states.add(STOPPED,stateStopped);
statePlaying= new Playing(this);
states.add(PLAYING,statePlaying);
statePaused= new Paused(this);
states.add(PAUSED,statePaused);
//Code for the initial transition
state=stateStopped;
}
. . .
MP3PlayerState state;
Stopped stateStopped;
Playing statePlaying;
Paused statePaused;
EList states=new BasicEList();
/*4. Method to set a new state*/
void setState(MP3PlayerState st){
state=st;
//Execute entry action, if new state is not null (=end state)
30
if(state!=null){state.entry();}
}
public boolean isInState(int stateNumber){
return(this.state.equals(this.states.get(stateNumber)));
}
/*5. Event definitions*/
public void playBut(){
state.playBut();
}
public void stopBut(){
state.stopBut();
}
}//MP3PlayerContextImpl
Notice that on the implementation of the Stopped state, each transition –in
this case only one- is represented as one object of the Transition class in the
de.tuhh.sts.mlasprilla.statefulemf10.core package; and all transitions sourcing out
of a state are kept in a list inside that state. This mechanism allows individual
transitions to be turned on or off at runtime, one of our design goals.
Another important point is the generation of error messages for the user
whenever a constraint fails to execute: by default we have chosen a simple console
message stating that “Conditions for transition are not met”, as shown on the
Stopped class, playBut() operation, in the “else” block. The developer can always
replace this action by another output of their choice, e.g. a message window or an
item on Eclipse “Error log” view.
4.6. Interconnection to EMF code
Once the implementation code for the statechart is defined, we need to
decide how it will be bound to the existing EMF implementation so that events are
recognized and the corresponding state transitions are accomplished. Going back to
the design guidelines, we stated it would desirable to keep the StatefulEMF part as
decoupled as possible from the core implementation. Ideally not only the EMF
plug-in itself should remain unaltered, but also the statechart-dependent code could
be kept in a different package and bound at runtime.
A first solution: Statechart as an adapter
The first idea that comes to mind is to make use of the notification
paradigm inside EMF: the whole statechart code would be generated in different
classes, where the statechart object itself would be an extension of the Adapter
class and would add itself at runtime as an adapter of the context class. Then, upon
changing an attribute value of the context class, the statechart´s notifyChanged()
method would be called, firing the change of state and acting as the connection
between the core and statechart codes.
However, a test implementation made apparent several disadvantages:
Since the notifyChanged is tied to changes in the model object, the
architecture would be limited to those events corresponding to attribute
31
changes – e.g. in the MP3Player model, changing the songLength value
would fire an event, not so for a call to a playBut() operation--. This
drawback could be artificially overcome by adding a “state” attribute to the
context model, where changes on this variable would mean moving to
another state and would fire the notifyChanged() property. Internal
transitions, producing no real change of state, could not be handled in this
manner though.
The fundamental drawback is that the notification to adapters occurs after
an attribute is changed, so that the transition condition, inside the statechart
code, would be checked after the transition has been executed. The EMF
notification architecture does not include the possibility of adding a
constraint before the action that fires the notification occurs. Compensating
actions –contemplated in EMF as Undo actions- could solve part of the
problem, but (a) they might not be able to undo every action, and (b) they
would still push the action to the command stack.
In short, the notification mechanism, being aimed at informing of changes
and not preventing them, is not an adequate option to implement constraints.
A tighter approach: Statechart inside the model code
A second alternative is to follow exactly Niaz’s implementation and include
all the statechart code in the core package –the state code as new classes and the
context code inside the context implementation class. Going back to code 4-2, the
lines shown on MP3PlayerContextImpl will simply be pasted into the context class
implementation, MP3PlayerImpl in this case, which was already generated by
EMF. Some helper methods, e.g. getCurrentState() will also be added to the
context class.
The question remaining would be: how do we enforce the use of the
statechart-augmented code? That is, if we want a certain condition to be checked
before an action is executed, how do we force, or at least allow the user to call the
appropriate event instead of the action directly? To solve this, we developed an
additional
interface,
named
[ContextInterface]_Dynamic
(e.g.
MP3Player_Dynamic.java in our example), which inherits from the original
interface but adds all events as operations. Thus, all a user interested in enforcing
the statechart-defined rules needs to do is use the Dynamic interface instead of the
original one, and call not the actions directly but the events that wrap the actions.
This approach has the drawback that the statechart data is in the same
package as the core model, so changed constraints require a model recompilation.
On the other hand, this tight coupling is more efficient and simpler than the
notification approach. A practical difficulty might arise upon attempting to merge
some of our generated code into the core-EMF-generated class, but the JMerge
class inside EMF should include sufficient customization options to solve the
problem.
32
4.7. Code generation
Once we know what our statechart classes will look like, we can write the
JET templates upon which the code generation will be based. The easiest procedure
is to build by hand a sample set of statechart classes –part of which was shown on
code 4-2-, then replace all the variable parts with the appropriate <%%>-encased
Java code. Once all templates are done, we can make use of the JETEmitter class to
generate the Java output at runtime. More details on this procedure are given on
section 5.1.
4.8. Architecture: the overall system
At this point, every step required for the StatefulEMF code generation has
been analyzed and a design selected; now we just need to (a) tie the steps and
define the control flow and (b) add the user interface. Starting with the latter point,
the user interface in our first, non-customizable version does not need any sort of
“Property” view; we only require one menu item –an “Action” as they are known
in the Eclipse realm-, which ignites the statechart code generation process. The
action can be easily created by extending an ActionBarContributor class. Our
derived class will be StateGenActionBarContributor; it will define a
generateAction, shown on the GenModel editor. This action will then control the
flow of data between the other objects, as shown on Figure 4-4. An execution will
look like this:
When the user clicks on the “Generate Stateful Code” right-click-menu
item, inside the GenModel editor, the generate() operation of our
ActionBarContributor is invoked. This method will first attempt to load the
statechart file into a in-memory model, if this succeeds, it will invoke the outermost
generateState() operation on the statechart object. This operation handles the code
generation: for each state in the chart, a generate() operation is invoked, which in
turn uses JETEmitter to produce code from the adequate template and save the
output in a file. This process is repeated for all states, then for the context class,
after which the StatefulEMF execution is complete.
Note that on figure 4-4 the four classes on the left belong to our
implementation, while JETEmitter and all other objects called by it belong to the
core EMF. The JETEmitter.generate() method serves as the point where we bind to
and reuse the existing EMF code generation tools.
One last architectural point is how to encapsulate our StatefulEMF
functionality: in a separate plug-in or by modifying EMF. The design guidelines
stated two seemingly contradicting goals: we would like to leave the core EMF asis, but at the same time provide a seamless interface to the user. This is when the
extensibility of Eclipse comes to our advantage: we can encapsulate all our classes
in a new EMF-based plug-in, which registers itself as an editor of “.genmodel”
files. By making our StateGenActionBarContributor a subclass of the
GenModelActionBarContributor which adds the “Generate Stateful Code” action,
we can get a new editor for GenModel files which to the user looks exactly like the
original editor but with one statechart-oriented action added to it. Seamless, clean
user interface, achieved thanks to subclassing and a few lines of our own code.
33
Figure 4-4: Sequence diagram of StatefulEMF
34
Chapter 5: Implementation
This chapter delves deeper on the implementation of the StatefulEMF plugin. Considering unnecessary to explain all the execution details, section 5.1
presents a few less obvious, trickier points that emerged during the realization,
while section 5.2 describes the plug-in usage step by step.
5.1. Implementation highlights
Initial settings
To develop the plug-in, we started with a new EMF project, called
de.tuhh.sts.mlasprilla.statefulemf10, to which the different blocks of statechart
model, core classes, JET templates and finally the GUI class were added. The main
package of the project is also called de.tuhh.sts.mlasprilla.statefulemf10; all
package names below will be relative to it.
Statechart model and mapping
As explained on section 4.4, the statechart information is represented in
memory as objects of the classes GenStatechart, GenState and GenTransition,
defined in the genmodel package. The classes were automatically generated from
the Ecore model of figure 4-2, then extended with a few helper methods to invoke
the JET emitter code generation.
On the model instance side, most of the objects used by the statechart
mapping are of core Java or EMF classes; only one new class is used on the
runtime representation of statechart data: Transition, which encapsulates the
information whether a transition is on, i.e. it can occur, at a certain moment, via the
boolean “on” attribute. The Transition runtime class belongs to the core package, it
derives from java.lang.Object.
Another detail to note is that, in accordance with the EMF paradigm, all
classes from the statechart model and core will be represented by two Java files: an
interface (e.g. GenState.java) and an implementation class (e.g. GenStateImpl.java)
on the impl subpackage. Also on the generated statechart code, this interface-plusimplementation convention will be followed.
An implementation decision that could be cause of discussion is on how to
handle best the use of event arguments. The UML statechart specification states
that events can have zero, one or more arguments; on the statechart event
definition, arguments are separated by coma, and each is defined by a name and a
type, separated by a colon. As an example, a display() event with name and
numOfLines arguments will look like:
display(name:String, numOfLines:int);
Given that, in our mapping, an event is represented as a Java operation, the
event arguments will be the arguments of that operation, and as expected in Java,
each argument should be declared with a type. Since the event arguments are likely
35
used by the event action, the wrong event argument type declaration would often
produce a casting exception when the action operation was called; thus, the right
argument type is critical. However, the EclipseUML statechart editor does not offer
an explicit input of argument types: the arguments of an event are given as a single
string that could or could not follow the expected format shown above. The second
drawback of using explicitly typed arguments is adequate imports: what if the user
refers to a type that is not on the core Java or EMF classes? Unless we identify all
necesssary classes and write the import lines, the state generated code will display a
“Missing import” error. A possible solution to this problem could be given by the
EMF GenBaseImpl.ImportManager inner class; but due to poor documentation and
time limitations, the use of this tool was delayed to a future version. As of this
writing, the event argument types will be handled in a compromise solution:
An event argument can be defined with a type according to the name : type
format; all arguments without a type will be by default of type Eobject.
The user must manually import those classes referred in argument types that
are not basic Java or EMF classes.
JET code generation
The StatefulEMF code generation makes use of 6 templates, described on
table 5-1: two for the abstract state class, two for the concrete state classes, one for
the „dynamic“ interface of the context class that adds the event operations, and one
for the additional code to insert into the context class implementation. For each
template we decide, based on what sort of variable data is required, which object
will be sent as argument to the template generation function. Most templates need
either the Statechart object or the State –the latter also holds a reference for its
“parent” statechart--; only the GenClass template requires data from two objects:
the statechart and the model context class. Since the generate() argument receives
only one argument, we wrap both objects in an ArrayList –note that the argument
can be any subclass of java.lang.Object- and give the list as the input argument.
Table 5-1: Template names and description
Template name (.javajet) Corresponds to
Input argument(s)
GenAbstractState
Abstract
state Statechart object
implementation
GenAbstractStateInterface Abstract state interface
Statechart object
GenClass
Context
class Statechart, context class
implementation
objects
GenInterface
Context dynamic interface Statechart object
GenStateClass
Concrete
state State object
implementation
GenStateInterface
Concrete state interface
State object
One critical point in the JET functioning is the adequate setting of the
classpath variable: every class used inside the <%%> of a template that is not a
standard Java class must be added to the intermediate .JETemitters project
36
classpath, using the JETEmitter.addVariable() method. There were two points
related to this issue that appear to be errors in EMF version 2.0:
Although the EMF classes should be added by default on the JETEmitter
classpath, they weren’t and we needed to include the lines:
addVariable("EMF_CODEGEN", "org.eclipse.emf.codegen");
addVariable("EMF_CODEGEN_ECORE", "org.eclipse.emf.codegen.ecore");
addVariable("EMF_COMMON", "org.eclipse.emf.common");
addVariable("EMF_ECORE", "org.eclipse.emf.ecore");
The classpath variables were not updated adequately: often when the
classpath was changed or extended using addVariable(), the .JETEmitters
project did not seem to take the changes and kept signaling the new
classpath items as missing. In these cases, it was necessary to delete the
existing .JETEmitters project from the Eclipse workspace before running
again the plug-in.
The last issue that arose during the code generation programming was the
most difficult to address. It is related to the merge of the EMF-generated and our
own context class code: as of this writing it wasn’t possible to control the merge so
that our code is inserted in the adequate places. For the most part the code is
inserted in the end, but after the closing class tag, so with a little extra copy-andpaste work it is fixed. Or to prevent the problem altogether, we can choose to
generate first the statechart code, and then the core code. The critical point is
caused by one line to which we wish to add extra information, namely the class
declaration.
Let us explain the subject further with the MP3Player example. The
declaration generated by EMF for this class looks like:
public class MP3PlayerImpl extends EObjectImpl implements MP3Player
We wish to add the dynamic interface to this class, so that it looks like:
Variable data
public class MP3PlayerImpl extends EObjectImpl implements MP3Player,
MP3Player_Dynamic
Since word-wise append is not possible, we need to generate the whole line
again. But to do so, we need the variable data inside the line, shown above as
underlined. This data is basically (a) the context class name, and (b) its superclass.
The former is easily retrieved from the context class object, but the latter is
retrievable through a method, getClassExtends(), that depends on protected
settings, which we cannot set from our own project –only by modifying the EMF
GenClass code itself. As a workaround, we assume that, as is in most real cases, the
context class has EObject as a superclass. For any other superclass, this line of code
will be wrong and the developer must change it manually.
37
Summarizing, it appears that the code merging in EMF offers a limited, or
at least poorly documented, API, which undermines the extensibility of EMF.
5.2. Usage
Step I: Installing
As with other Eclipse plug-ins, StatefulEMF is installed by simply
extracting it into the plug-in folder, then restarting Eclipse.
Step II: Running
In order to make use of the StatefulEMF functionality, we must first execute
the usual steps in an EMF project: create a blank project, define the model, and
generate its code. Then:
1. Start by creating the EclipseUML statechart diagram(s). Locate them on
the uppermost folder of the project. Each diagram should be named as the
class it belongs to, followed by the “.utd” extension (e.g. MP3Player.utd).
2. Once the statecharts are ready, right-click on the GenModel file, and select
the option „Open with
Stateful EMF 1.0 Editor“. This will open an
editor exactly like the original GenModel editor, except that upon rightclicking on an object, the “Generate Stateful Code” option now appears.
3. To generate the statechart code, select each of the context classes, rightclick on it and select „Generate Stateful Code“. The operation of code
generation should last a few seconds, after which the appropriate code
should appear on the project’s uppermost and „impl“ packages.
Although in this first version, the “Generate Stateful Code” action is enabled
on all GenModel objects, upon attempting to run it on a non-class object, the
following error shows on the Error Log: “Selected object is not a class. Please
select a class node”. Similarly, upon attempting to generate code on a class which
statechart file does not exist –or does not follow the name convention:
ClassName+”.utd”-, the error “The system cannot find the specified file” will
appear.
38
Figure 5-1: Opening the StatefulEMF editor
If the context class implementation code has not been generated correctly,
the user can correct the problem by:
- Erasing the file from the project.
- Regenerating the stateful code, then
- Regenerating the model code –with the „Generate model code“
action.
39
Figure 5-2: Error on selection of a non-class object
40
Chapter 6: Evaluation
Once a first version of the StatefulEMF is ready, we evaluate its usefulness
against two case studies: first we model a simplified version of an online Purchase
Order system; then we focus on a real-life, more complicated example, namely the
integration of well-formedness rules in a visual language editor. Since we want to
focus on the evaluation of our tool and not on the whole editor development
process, we make use of an existing Eclipse editor: the UML2 project.
6.1. Case Study I: Purchase Order
As a first example let us consider we want to model the dynamic behavior
of an online purchase system: a purchase order, just like in a traditional system,
likely consists of attributes like order ID and total amount, as well as one or more
product items. However, the static modeling is not the focus of our tests, since that
can be handled well by the existing Ecore editor. Instead, we want to model the
different stages of the order placement process and how an object moves between
these states; for this purpose, we have defined several operations that act as
conditions (e.g. isPaymentValid()) or actions to take when going to another phase.
Figure 6-1: Class Diagram of our Purchase Order model
Figure 6-2 describes the dynamic behavior of our PurchaseOrder class
through a statechart diagram: first a provisional order is first built, it can then be
cancelled or placed, and if the payment is valid, it is confirmed and shipped. Notice
the actions to take at certain transitions: e.g. when an order is cancelled, the product
items that were “reserved” for it are released and counted again in the inventory.
To prove our implementation thoroughly, we have included both an initial state
with an action and a final state.
41
Figure 6-2: PurchaseOrder Statechart Diagram
Following the instructions of use on section 5.2, we built an EMF project,
included the PurchaseOrder Ecore and statechart diagram, and generated both the
core and statechart-augmented codes. The list of generated classes and part of the
PlacedImpl state code is shown on figure 6-3. The only error in the generated code
was due to the merge problem already described in the last chapter; it was easily
corrected by, starting on a clean PurchaseOrderImpl file, regenerating first the
statechart code and then the core code.
Once the implementation was ready, it was tested by creating a Java class
called POClient, which creates a PurchaseOrder instance and attempts to switch
among the states. The test was successful in that, by using the generated
PurchaseOrder_Dynamic interface, the conditions defined on the statechart are kept
–e.g. we can confirm an order only if the paymentValid argument is set to true,
otherwise a message error is shown and the order remains on the Placed state. The
init() action was executed on initialization as desired, and if a certain transition was
turned off at runtime with the Transition.setOn(false) operation, then the transition
was no longer achievable.
In conclusion, this first case study points to the StatefulEMF plug-in being
very useful in implementing dynamic behavior as that of figure 6-2, and its only
significant limitations at the moment are the lack of nested states and the code
merge problems in the context class.
42
Figure 6-3: A look on the PurchaseOrder EMF project
6.2. Case Study II: Well-formedness rules in UML2 Editor
Let us move on now to a more complicated case, with a bigger base model
and where the data to be implemented by StatefulEMF is not dynamic behavior
already in a statechart diagram, but rather well-formedness rules about a certain
model, expressed likely in OCL or natural language. For this purpose, we have
chosen the UML2 editor of Eclipse, given its open-source, modifiable nature, and
the widespread use of UML-related editors and standards.
6.2.1. Background: the Eclipse UML2 project
One of the latest subprojects in Eclipse, UML2 is an EMF-based
implementation of the UML 2.0 metamodel. The UML 2.0 [OMG03] is a major
revision of the current 1.5 version, and it is expected to replace the latter as the
official UML standard from late 2004 on. The new specification is designed to
support the Model Driven Architecture (MDA) among other software development
paradigms.
The UML 2.0, as previous specifications, is defined using a metamodel
approach, which makes it an ideal candidate for an EMF-based implementation. In
this manner, the Eclipse UML2 project took the OMG definition, developed from it
an Ecore model of UML2, then generated Java code from it, and did the necessary
code modifications to obtain a working model. The resulting UML2 project is
divided into three plug-ins: for the model classes, for the EMF.Edit classes and for
the standard tree editor. Neither a visual UML editor nor code generation are
43
contemplated at this stage, although the former could be built with the GEF tool,
while the latter can be achieved with an available UML2 to EMF conversion
action. The goal of the project is more towards achieving a robust implementation
of the semantic metamodel.
We consider this particular Eclipse language editor to be a relevant case
study for our tool because of the expected popularity of UML 2.0 in the following
months, which will in turn increase the demand for strong, correct
implementations, and because the UML 2.0 specification includes an extensive list
of model well-formedness rules, thus being a good example of the amount of work
it means to implement a language editor manually. Even more important, many of
these rules were not enforced in the UML2 project as of this writing.
6.2.2. Some well-formedness OCL rules
In order to test the usefulness of StatefulEMF in the UML2 realm, we first
selected a few well-formedness rules stated in the OMG specification, to then test if
they were enforced in the UML2 project, and if they were not, attempt to
implement them with the help of our plug-in. The rule selection was basically done
at random, although we did try to avoid both rules implicitly implemented in the
diagrams (e.g. multiplicity rules) and those whose violation is visible at the code
and not at the model stage (e.g. „the member attributes of a class include its owned
and inherited attributes“). After observing which rules were still not implemented,
we selected for enforcement two constraints related to class diagrams:
Rule I: „Circular inheritance is not allowed“
Rule II: „A class cannot have two attributes with the same names“.
Let us next analyze: (a) the current state of each rule in the UML2 project,
(b) which runtime code is associated with the rule violation, (c) how to convert the
rules to the appropriate ECA format and express them via statechart diagrams, and
(d) the enforcement of the rules via StatefulEMF.
Current State
All the data on this section refers to the Eclipse UML2 project version
1.0.0, from June 2004.
Rule I: “Circular inheritance is not allowed”
The UML2 editor does not enforce this rule, allowing the creation of
circular dependencies without showing any error message. On the other hand, the
Ecore editor does enforce the rule, and any attempt of creating a circular
inheritance results in a Stack Overflow exception
Rule II: “A class cannot have two attributes with the same names”
44
Neither the UML2 nor the Ecore editor enforce this rule, thus permitting
two attributes of a class to have the same name.
Code associated with the constraint violation
Rule I:
The generalization relation between a superclass A and a specific class B is
denoted in the UML2 tree representation as follows: the class B has an object,
which we call Gen, as a child –that is, Gen’s owner property equals B- of type
Generalization, and the “general” property of Gen is equal to A; as shown below:
Figure 6-4: Generalizations in UML2
Two Java operations allow the creation of a generalization with code:
Generalization objGen=((Classifier)objB).createGeneralization(
UML2Package.eINSTANCE.getGeneralization());
ObjGen.setGeneral((Classifier)objA);
Rule II:
The constraint violation is associated with 2 Java lines: first the creation of
a class attribute, then the name assignment:
String name="unoriginalName";
Property attributeA =
aClass.createOwnedAttribute(UML2Package.eINSTANCE.getProperty());
attributeA.setName(name);
Property attributeB =
aClass.createOwnedAttribute(UML2Package.eINSTANCE.getProperty());
attributeB.setName(name);
Conversion to ECA
Rule I:
45
Based on the code above, we can easily express a subset of our constraint,
namely for a 2-element loop (A->B and B->A) as a Java condition:
objA.getGeneral(((Classifier)objGen.getOwner()).getName())==null
that is, objA can be a superclass of objB, which is the owner of objGen, if
and only if objB is not already a superclass of objA.
Once the constraint is expressed in code, we can wrap the setGeneral()
operation, which in ECA format will be our action, around an event, which we will
call doSetGeneral. Note that this is taken as a name convention on our ECA
conversion: actions are wrapped around events with called do[ActionName]. Our
constraint is thus expressed in ECA format as:
Event:
Condition:
objGen.doSetGeneral(newValue)
newValue.getGeneral(((Classifier)objGen.getOwner()).getName())==nul
l
Action:
objGen.setGeneral(newValue)
Notice that the event and actions are members of the GeneralizationImpl
class in the UML2 model. The ECA above was incorporated in a statechart diagram
as shown on figure 6-5, where the state in which a Generalization object should
stay is named ValidGeneral, and the ECA rule is an internal transition.
Figure 6-5: Statechart of UML2 Generalization class without circular
inheritance
We should highlight that this is not the only ECA representation of Rule I:
we could have also chosen the opposite condition (that which points to a circular
inheritance) and written a more specific error message and a compensating action
in the action part. Our choice is likely the simplest mapping, though.
Rule II:
Applying a similar logic to the second rule, we obtain the Java condition:
((Classifier)attributeA.getOwner()).getAttribute((String)newName)==null
That is, currently there must be no attribute on the owner class with the
same name as the one we intend to assign to the attributeA object –an instance of
the Property class. The ECA rule is then:
Event:
attributeA.doSetName(newName);
46
Condition:((Classifier)attributeA.getOwner()).getAttribute((String)newName)
==null
Action:
attributeA.setName((String)newName);
Shown in the statechart in figure 6-6:
Figure 6-6: Statechart of Property class without doubled attributes
Constraint integration with Stateful EMF Plug-in
Once the constraints have been expressed in EclipseUML statechart files,
we can invoke the statechart code generation. As a prerequisite, we must have first
downloaded the UML2 source code from the Eclipse.org website, extracted it on
the Eclipse plug-ins folder, and imported it as 3 workspace plug-in projects –the
latest step is done with the “Import External plug-ins and projects” action. The 2
statechart files –Generalization.utd and Property.utd— must be located in the
uppermost folder in the UML2 model project.
As a next step, we open the UML2 GenModel file with the StatefulEMF
editor, locate each of the context classes, Generalization and Property, and select
the “Generate Stateful Code” for each. Figure 6-7 is a snapshot of the workspace
before the code generation –note that the UML2 model has about 200 different
classes.
If the code was generated without any error, both the “Problems” and “Error
Log” views should be empty or showing only warnings. In our case, however, a
few merging errors arose; we describe them below.
The first drawback resulted from the merge of the context class code: as
mentioned on chapter 5, the statechart lines are not correctly merged into the
existing code. But unlike the PurchaseOrder case, here we cannot easily solve the
problem by regenerating the core again, since the UML2 classes have been
modified from the initial EMF generation to improve the model. Thus, in this case
we had to go through more work to fix the code order in the context class
implementation.
47
Figure 6-7: The UML2 GenModel
After all the code errors were corrected, we can compile the UML2 model
project and test it. The statechart-based constraints are only taken into account,
though, when we make use of our generated dynamic interface
(Generalization_Dynamic and Property_Dynamic). Thus, before running the test
we must locate the “insertion points” of the constraint. In both cases, the constraint
is violated when changing an object attribute value (Generalization.general in rule
I, Attribute.name in rule II). Since the EMF implementation framework manages
by default the changes to object attributes through an eSet() method, we locate this
method on each of the classes and replace (a) the original interface by the dynamic
one, and (b) the action call (e.g. setGeneral()) by the event call (e.g.
doSetGeneral()). Code 6-1 illustrates what the Generalization.eSet() method looks
like after the change:
Code 6-1: Generalization.eSet() method with dynamic interface
public void eSet(EStructuralFeature eFeature, Object newValue) {
switch (eDerivedStructuralFeatureID(eFeature)) {
case UML2Package.GENERALIZATION__EANNOTATIONS:
getEAnnotations().clear();
getEAnnotations().addAll((Collection)newValue);
return;
case UML2Package.GENERALIZATION__OWNED_COMMENT:
getOwnedComments().clear();
48
getOwnedComments().addAll((Collection)newValue);
return;
case UML2Package.GENERALIZATION__SPECIFIC:
setSpecific((Classifier)newValue);
return;
case UML2Package.GENERALIZATION__GENERAL:
//The original line
//setGeneral((Classifier)newValue);
//is replaced by the dynamic interface and the event
call
((Generalization_Dynamic)this).doSetGeneral((EObject)newValue);
return;
case UML2Package.GENERALIZATION__IS_SUBSTITUTABLE:
setIsSubstitutable(((Boolean)newValue).booleanValue());
return;
case UML2Package.GENERALIZATION__GENERALIZATION_SET:
getGeneralizationSets().clear();
getGeneralizationSets().addAll((Collection)newValue);
return;
}
eDynamicSet(eFeature, newValue);
}
Once the dynamic interface and the event call are used in all places where
the original action call was done, the constraint can be considered enforced. We
tested the resulting model on a runtime workspace project, and indeed both rules
were now enforced: neither 2-element loops between classes (a subset of circular
inheritance) nor doubled attribute names were possible. The user interface reacts to
the attempted violation by clearing the attribute value on the object Property view,
and by displaying the default console message “Conditions for transition are not
met” on the original workspace. A better response to attempted violations can be
done by modifying the “else” block on the event methods of each concrete state
class. For example the line:
StatefulEmfPlugin.INSTANCE.log(new Exception("Conditions for transition
are not met"));
will display the same error message but on the “Error Log” view of the
project.
It was during testing that a second, less obvious problem emerged: while a
file was open the constraint implementation did not obstruct the functioning of the
UML2 editor; but if a UML2 instance file was closed and opened again, the
generalizations were lost –that is, the general attribute of each Generalization
object was set to null. After some debugging, it became clear that the problem was
due to the manner in which the model loading process occurred inside UML2:
when a model file is loaded into a memory model, the latter goes temporarily
through certain invalid states. For example, the Generalization.general attribute is
assigned a value before the Generalization.owner attribute has been set. But since
our constraint checking of Rule I is invoked every time we attempt to set the
Generalization.general attribute, the statechart-augmented code will try to check
the condition
49
newValue.getGeneral(((Classifier)objGen.getOwner()).getName())==null
and the empty owner attribute will cause a null-pointer exception, which
will in turn prevent the corresponding action –the set of the general attribute- from
occurring. Thus, the general attribute remains null and the model instance is
corrupted upon reloading.
In this specific “null owner” case, we can avoid the error by extending our
condition so that it skips the exceptional case of owner being not yet set:
(getOwner()==null)||(newValue.getGeneral(((Classifier)objGen.getOwner()).g
etName())==null)
which indeed solved this particular error. The general conclusion is, though,
that the statechart-augmented code can interfere with all those existing parts that
lead the model to intermediate invalid steps, often in manners that are not
foreseeable; a workaround like the one above may not be always possible to find.
6.2.3. Results
The UML2 case study allowed us to test the StatefulEMF plug-in for the
implementation of constraints in a real model editor. This evaluation served as
proof that the plug-in can indeed be used to express at a higher level and
automatically generate code that implements typical constraints in a complex EMFbased model, like those in modeling language tools. The process to express OCL
constraints in an ECA format proved straightforward. The identification of the
constraint insertion points was less obvious, on the other hand, the EMF-generated
code is structured in a manner that the automatization of the insertion point task
should be achievable in future versions of the tool. Yet the test also made clear two
main disadvantages of the current implementation, namely:
That the code merge problems in one of our classes diminishes the ability of
our solution to fulfill the goal of automatic code generation: most of the
implementation is indeed automatically generated, but the user still needs to
take some time to fix some merge problems. This is an implementation
drawback that should be solvable in future versions; yet its solution, and in
general the generation of error-free, ready-to-compile code is key to the
actual usefulness of our tool: the overhead in code and the additional work
of drawing a statechart and so on is only worth it in the eyes of the user if in
return there is no need for programming or debugging and the result is
directly testable and useable.
A more general, interaction-oriented limitation is posed by the fact that the
statechart-augmented code may interfere with existing edit and editor code,
thus it is not always possible to introduce the statechart behavior as layer
between the existing model and editor, without doing changes to the latter.
A stronger approach would be to generate the statechart plug-in right after
the model implementation; then design an editor that takes into account the
constraints at all times. This means that the EMF-based editor may not be
used unless a few changes are applied, nevertheless, we don’t consider this
50
a critical drawback because anecdotal evidence shows more real-life
applications of EMF-based make use of their own custom editor, often a
visual editor designed with the Eclipse GEF tool.
51
Chapter 7: Summary and Conclusions
7.1. Summary
The unambiguous, explicit expression of well-formedness rules at a high
level modeling layer, and their automatic enforcement in the implementation are
highly desirable in the realm of model-based software development, for reasons of
robustness, clarity and efficiency. Eclipse EMF is a useful, popular framework to
build model-based applications, which nevertheless does not include the possibility
to define most constraints at the model level.
This work developed an extension of EMF, in the form of an Eclipse plugin, to express and automatically implement system constraints via statechart
models. Different options on how to define the constraints were analyzed;
statecharts were the chosen mechanism for reasons of executability, popularity and
future extensibility. The resulting prototype tool, baptised StatefulEMF, takes as
input a statechart diagram drawn with the EclipseUML plug-in, then generates Java
code that maps the diagram data and integrates it with the existing EMF model. A
Java interface lets the user enforce the implemented statechart data, offering
additional helper methods and the capability to turn transitions on or off at runtime.
The GUI seamlessly extends the existing EMF interface. More generally, the
project serves as a proof of concept regarding the potential for extensibility and
code customization of EMF, while pointing to a current limitation, namely in the
lack of documentation of the EMF code merge classes.
The evaluation of the tool with two different test cases led us to the
following conclusions:
The StatefulEMF plug-in is highly useful in the implementation of models
that exhibit a dynamic behavior, by automatically mapping that behavior,
clearly expressed in a statechart diagram, into Java code. Categories of
EMF-based applications where the tool could be particularly helpful are
real-time, embedded, control, workflow management and e-commerce
systems.
On the more general area of constraint implementation, especially in the
development of modeling language editors, StatefulEMF provides the
advantage of clarity: the constraints enforced in code are explicitly shown at
the model level through statecharts. The goal of efficiency, however, is not
as successfully achieved by the current version. Besides the code merge
deficiency, the need to identify the points where a constraint can be violated
and must be enforced, as well as the possibility of statechart-augmented
code conflicting with existing editor functions, translates into some
debugging work for the developer. It will thus be necessary to improve
these areas in order to offer a truly efficient tool.
52
7.2. Future work
One further development that was made clear by the evaluation results, and
which could bring immediate benefits, would be the automatic identification of the
“insertion points” of a constraint, based on the naming and attribute access
conventions inside EMF. Once the insertion points are recognized, the statechartaugmented interface could also be automatically integrated. The interconnection of
the statechart implementation with an existing OCL-to-ECA translator, in order to
offer OCL as another input format for constraints, could also add much to the
usefulness of the tool.
An interesting direction of research would be to compare our architecture
with an aspect-oriented approach that enforces model constraints as a layer
decoupled from the core EMF model. The analysis by [Cibran02] can serve as
theoretical basis for an AspectJ-based EMF extension; the comparison of both
architectures would be then focused on the usability of the tool versus the
independence achieved via aspect orientation –that is, whether it is possible to
define and enforce constraints on an EMF model without recompiling it.
The subject of how to implement well-formedness rules that stretch across
different models or different plug-ins, or the related subject of interdiagram
constraints, has not been touched upon in this project and could also be a point of
future development.
53
Appendix A: Omondo EclipseUML1
Installation
EclipseUML plugin is provided as an executable installer jar to download
on the http://www.eclipseuml.com/ site. This jar must only be uncompressed or
installed through the following procedure. If the .jar extension is associated to your
java runtime program, a simple double-click will launch the installer. Otherwise
(nothing happened or another program has been launched), you will have to open a
shell under you operating system, go to the directory where you downloaded
EclipseUML Plugin then type the following :
java -jar eclipseuml-installer_1.1.4.jar
The installation will be done through several steps. To move forward to the
following step, just use the next button. The directory where you want to install the
EclipseUML plugin must be the eclipse root directory.
To run the EclipseUML Plugin, you will have to restart the eclipse program.
Once restarted, Eclipse will automatically detect the plugin and will launch it as
soon as you will use an UML functionality. When the program is launched, the
EclipseUML plugin splashscreen will appears on your screen.
Usage
EclipseUML enables software modeling through two main categories: UML
and EMF diagrams. On the first category, the following types are supported:
- Class diagram
- Sequence diagram
- State diagram
- Use case diagram - Collaboration diagram
- Activity diagram
- Object diagram
- Component diagram
- Deployment diagram
EMF support is limited to all the diagrams above except state diagrams. To
create a new diagram, simply select inside Eclipse the option New Other, then
either UML or EMF diagram.
EclipseUML offers a wider functionality associated with UML diagrams
than with EMF diagrams: first, it automatically generates the classes defined in a
UML class diagram; second, it provides the possibility of reverse engineering, that
is, it is capable of generating a class diagram given a set of Java classes.
One important point in the creation of certain designs, e.g. state,
collaboration or sequence diagrams, is that EclipseUML requires the previous
definition of the classes used in the diagrams –be it through a UML class diagram
or directly through Java classes in the same project. For example, the user interface
for the creation of a state diagram requires the user to input the name of the Java
class that the diagram refers to. If the class defined by the user does not exist, the
1
help
Part of the data on this appendix was extracted from the Omondo EclipseUML online
54
interface gives the error message „This class doesn’t exist“ and disables the state
diagram creation.
Usage with the StatefulEMF plug-in
The StatefulEMF plug-in requires as input one or more state diagrams,
defined in the format of EclipseUML. Although the user could attempt to write by
hand the XMI-based format, it is much easier and less error prone to make use of
the graphical interface in EclipseUML. To do so, the user must:
Install the EclipseUML plug-in, if not already done, and restart Eclipse. See
the section above for installation details.
Create the EMF project and define the Ecore model of the case currently
under development –the Ecore can also be created with the EclipseUML
„EMF class diagram“ function, or it can be defined with Java annotated
classes, Rational rose files or XML schemas.
Do a first EMF model-code generation, so that there exists a Java file for
each class to be further described with a state diagram.
For each state diagram, select File New Other UML Diagrams UML
State diagram. Give the file the same name as the associated class but with
the „utd“ extension. On the „Class“ textbox, type the qualified name of the
class –that is, including the package name.
Draw the state diagram, and save it when done.
Open the „genmodel“ file of the project, select each class that has a state
diagram associated with it, right click on the class node, and select
„Generate Stateful Code“.
In order to avoid a wrong merge of the class implementation file, it is
preferable to erase that file before running the „Generate Stateful Code“
action,and running again the „Generate Model Code“ action once the
stateful code has been generated.
55
Appendix B: Restrictions of the StatefulEMF plug-in version
1.0
Version 1.0 of the StatefulEMF plug-in works under the following
assumptions and limitations:
Entry and exit are reserved action labels, so that other events cannot have
these names. The entry and exit actions cannot have arguments or
conditions associated to them.
The do and include internal actions are currently not supported.
Action code must be valid Java code –including the “;” at the end of each
code line. All methods of the context class must be explicitly referenced
with the "this" word.
Condition code must be a valid Java "if" condition (e.g. no ";" at the end).
Nested states are not supported.
The type of an event argument can be defined using the “argName :
argType” syntax, where argType is one of the standard Java classes or
primitive types. If an argument type is not stated, the argument is assumed
to be of type EObject.
Fork, joins, choice points and junction points are not supported.
Transitions sourcing out of a state must be uniquely distinguished by the
combination of their firing event and target state. Internal transitions, having
no target state, must be uniquely identifiable by their event. That is, the
event that fires an internal transition cannot be used to fire any other
transition in that same (source) state.
Unique naming is not explicitly enforced by the StatefulEMF plug-in, but its
non-observance will cause erroneous output code. So the user is in charge of
ensuring that e.g. no event in a statechart has the same name as an action on
that chart.
56
Bibliography
[ALP03] David Akehurst, Peter Linington and Octavian Patrascoiu. "OCL 2.0:
Implementing the Standard". Software and Systems Modeling, 2(4):215-239, 2003.
Available on
http://www.cs.kent.ac.uk/pubs/2003/1746/content.pdf
[AP03] Dave Akehurst and Octavian Patrascoiu. „OCL for KMF Project“.
University of Kent at Canterbury, Computing Laboratory, 2003. Documentation
available on http://www.cs.kent.ac.uk/projects/ocl/documents.html
[BLYD03] L.C. Briand, Y. Labiche, H.-D. Yan and M. Di Penta "A Controlled
Experiment on the Impact of the Object Constraint Language in UML-based
Development".
Available
on
http://www.sce.carleton.ca/Squall/pubs/tech_report/TR_SCE-03-22.pdf
[BSMEG04] F. Budinsky, D. Steinberg, E. Merks, R. Ellersick and T. Grose.
„Eclipse Modeling Framework: A Developer’s Guide“. Addison-Wesley. 2004
[Cassia04] Fernando Cassia. “Eclipse.org eclipsing Borland’s Jbuilder”. The
Inquirer Online. May 12, 2004. Retrieved on August 20th, 2004 from
http://www.theinquirer.net/?article=15862
[CGT] José M. Cañete, Francisco J. Galán and Miguel Toro. "Filling the Gap
between Specification and Implementation of Systems by an Executable Code
Generator of UML/OCL Models". Proc. of 12th International Conference of
Software and Systems Engineering and their Applications (ICSSEA99), Paris
(France), 1999.
[Cibran02] M. A. Cibrán. “Using aspect-oriented programming for connecting and
configuring decoupled business rules in object-oriented applications”. Master
Thesis, Vrije Universiteit Brussel, Belgium, 2002. Available on
http://ssel.vub.ac.be/Members/MariaAgustinaCibran/papers/AOPForBR.pdf
[Eclipse03] Object Technology International, Inc. “Eclipse Platform Technical
Overview”. February 2003. Available on
http://www.eclipse.org/whitepapers/eclipse-overview.pdf
[EMF04]. eclipse.org Consortium. „The Eclipse Modeling Framework (EMF)
Overview“. June 2004.
[Finger00] Frank Finger. “Design and Implementation of a Modular OCL
Compiler“. Diploma Thesis, March 2000. Documentation and software available
on
http://dresden-ocl.sourceforge.net/
[FM02] S. Flake and W. Mueller. “An OCL extension for real-time constraints”.
Available on http://jerry.c-lab.de/~wolfgang/springer01.pdf
57
[Griffin04] Catherine Griffin. “Transformations in Eclipse”. Workshop on ModelDriven Development. Oslo, Norway. June 2004.
[GHJV95] E. Gamma, R. Helm, R. Johnson and J. Vlissides. “Design patterns:
elements of reusable object-oriented software”. Addison Wesley, 1995.
[GSMD03] Pieter Van Gorp, Hans Stenten, Tom Mens and Serge Demeyer.
"Enabling and Using the UML for Model Driven Refactoring". Proceedings
WOOR'
03 (ECOOP'
03 Workshop on Object-Oriented Re-engineering), pages 3740. University of Antwerp, July 2003.
[KC] Kennedy Carter. “Action Semantics FAQ”. Retrieved on September, 12th,
2004 from http://www.kc.com/as_site/frames/faq.html
[LEM02] W. Liu, S. M. Easterbrook and J. Mylopoulos, "Rule-Based Detection of
Inconsistency in UML Models". Presented at the Workshop on Consistency
Problems in UML-Based Software Development, at the Fifth International
Conference on the Unified Modeling Language, Dresden, Germany, October 1,
2002.
[MCS02] Rakesh Mohan, Mitchell A. Cohen, and Josef Schiefer. “A State Machine
Based Approach for a Process Driven Development of Web-Applications”
http://www.mm.di.uoa.gr/~rouvas/ssi/caise2002/23480052.pdf
[MDGWV04] B. Moore, D. Dean, A. Gerber, G. Wagenknecht and P.
Vanderheyden. „Eclipse Development using the Graphical Editing Framework and
the Eclipse Modeling Framework“. IBM Redbooks. January 2004.
[Meyer04] Bertrand Meyer."Building bug-free O-O software: An introduction to
Design by Contract". Available on
http://archive.eiffel.com/doc/manuals/technology/contract/page.html
[MG03] Wassim Melhem and Dejan Glozic. „PDE Does Plug-ins“. IBM Canada
Ltd. September 2003.
[NT04] I. A. Niaz and J. Tanaka. "Mapping UML Statecharts To Java Code".
Proceedings of the IASTED International Conference on Software Engineering (SE
2004), Innsbruck, Austria, February 17-19, 2004, pp. 111-116. Available on
http://www.iplab.cs.tsukuba.ac.jp/paper/international/niaz_se2004.pdf
[OMG99] “OMG Unified Modeling Language Specification”
[OMG03] OMG. “Unified Modeling Language (UML) Specification:
Infrastructure. Version 2.0”. December 2003. Retrieved on Septemper 20th, 2004
from
http://www.omg.org/docs/ptc/03-09-15.pdf
[Omondo] Omondo EclipseUML Visual Modeling tool. Download and
documentation available at http://www.omondo.com/
58
[Popma04] Remko Popma. „JET Tutorial Part 2 (Write Code that Writes Code)“.
Eclipse EMF Tutorials. May 2004.
[RSD02] Iris Reinhartz-Berger, Arnon Sturm and Dov Dori. “Modeling Events in
Object-Process Methodology and in Statecharts”. Available on
http://www.haifa.il.ibm.com/info/ple/papers/EventsHandlingInOPM.pdf
[SC95] A. Sane and R. Campbell. “Object-Oriented state machines: subclassing,
composition, delegation, and genericity”. ACM SIGPLAN Notices, OOPSLA'
95,
vol.30, Austin, Texas, USA, 1995.
[Shavor03] Sherry Shavor. “The Java Developer’s Guide to Eclipse”. Addison
Wesley, 2003.
[Verheecke02] Bart Verheecke and Ragnhild Van Der Straeten. “Specifying and
Implementing the Operational Use of Constraints in Object-Oriented
Applications”. Available on http://crpit.com/confpapers/CRPITV10Verheecke.pdf
[VJ99] Mandana Vaziri and Daniel Jackson “Some Shortcomings of OCL, the
Object Constraint Language of UML”. Available on
http://sdg.lcs.mit.edu/~dnj/publications/omg.pdf
[Wahler04] Michael Wahler. „Using OCL to interrogate your EMF model“. IBM
Zurich. Retrieved on September 7th from
http://www.zurich.ibm.com/~wah/doc/emf-ocl/index.html
[Wier03] R.J Wieringa. “Design Methods for Reactive Systems”. Morgan
Kaufmann, 2003.
[WTB03] Gerd Wagner, Said Tabet and Harold Boley. "MOF-RuleML: The
Abstract Syntax of RuleML as a MOF Model". Available on
http://www.omg.org/docs/br/03-10-02.pdf
59
Download