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