Tuesday, January 19, 2010

Quick'n Dirty Tutorial on Modelling with Eclipse

A friend of mine asked me how to modelling with Eclipse. I gave him a quick tour, demonstrating how to create a model with EMF, how to create an instance of that model, how to query the model with OCL, and how to create a text editor for that model with Xtext. It's pure modelling, i.e. no Java programming at all! I thought that maybe other people would be interested in that topic as well, so I created this little tutorial showing how all these Eclipse modelling "ingredients" can work together.

Preparations

Install Eclipse 3.5 (Galileo) with all the modelling tools, that is the Galileo Eclipse Modeling Tools package. Additionally, we will need the OCL interpreter console, which can be installed using the MDT update site:
http://download.eclipse.org/modeling/mdt/updates/releases/
The OCL console actually is an example, so you will have to install that specific example.

The Runtime Example

Let's assume a small IT company. The employees of that company develop software for other companies. Of course, they use models for that purpose! In order to better organize their work, they want to document who is working on what. That is, they want to assign certain task to developers. A task could be the implementation of a use case or writing a test case for a class. Let's see if we can use models for that...

Company Model with EMF

First of all, we have to create a model describing the company. Let's start from scratch:
  1. create a new project:
    File / New / Project... / Eclipse Modeling Framework /Empty EMF project
  2. create a new ecore diagram (we want to graphically design the model) in the newly created model folder: Right click on model folder and select
    New / Other... / Ecore Tools / Ecore Diagram
  3. enter the Domain file name:: company.ecore and press Finish.
Now, we can "draw" our model using the graphical editor. Create four classes (EClass), add a name attribute (EAttribute) to one of them, create references (EReference) between the classes, and set the properties (in the properties view, this view can be activated by right-clicking in the diagram and select Show Properties View. The result should look like Figure 1.
Figure 1: The domain model of a company
Some remarks:
  • The model is an instance of the ecore model, which is quite similar to class models in UML. All elements in ecore start with an "E", so it is EClass, EAttribute, or EString.
  • The EType of the attribute name is EString.
  • The upper bound of all references is "*" (you can enter "-1", which actually is the same as "*").
  • You will need a class containing all your elements later. In the example, the Company serves as a container. Make sure the Is Containment flag is set for the two references employees and skills (see Fig. 1)
Now that we have created a model of our company, we can create an instance of that model. Later, we will generate an editor, but right now we want to quickly create an instance to get a feeling for our model. An instance of our model actually is an instance of our container element, that is the Company class. There is a generic editor available, which can create an instance of a model (or its elements) directly without the need of code generation. All you need is the ecore model. This is how to activate it:
  1. Close the ecore diagram
  2. Open the ecore model, this time use the Sample Ecore Model Editor. Usually, this editor is used if you double-click the ecore-file, but to be sure use the Open With... context menu entry.
  3. Select the Company class (the class, not the package!), and select Create Dynamic Instance... from its context menu. This is shown in Figure 2.
    Figure 2: Create a dynamic instance.
  4. enter the File name: Company.xmi and press Finish. The instance will be created in the model folder.
  5. edit the model instance using the context menus New Child of the elements in the model.
  6. edit the elements properties in the properties section of the editor, see Figure 3.
    Figure 3: Edit properties with the Generic EMF Form Editor
Now that we have create a model and an instance of that model, we want to "work" with that model instance. E.g., we can use OCL to query the model:
  1. Activate the console view and open the Interactive OCL console with the button on the left of the console, see Figure 4.
    Figure 4: Open OCL interpreter console
  2. Select an element in the editor, This selected element is the context of the OCL query.
  3. Enter your OCL query in the console (in the lower section of the OCL interpreter console). E.g. query the name of the selected element with self.name. The result will be displayed in the upper section of the OCL console as shown in Figure 5. We will demonstrate other queries later on.
    Figure 5: Query the model instance with OCL

Task Model with Xtext

Now that we have a model of our company, we want to assign tasks to the employees. We could create a new ecore model just as demonstrated above, but we want to try something new. So, let's try to not only create a model, but a text editor as well. For that, we will use Xtext. Based on a grammar, Xtext can create an ecore model and a text editor with nice features. The grammar is an annotated EBNF grammar, I do not want to go into the details here (otherwise it wouldn't be a q'n d-tutorial ;-) ). So, we have to create a new project and enter a grammar:
  1. Create a new Xtext project:
    File / New / Project... / Xtext /Xtext Project
  2. SetMain project name, the Language name (to de.feu.Tasks), and the DSL-File extension (to tasks).
  3. Open Tasks.xtext (this is the EBNF-like grammar) and enter your grammar according to Figure 6.
    Figure 6: Xtext grammar defining our task model along with a concrete textual syntax
    Here is the grammer (for copy & paste):
    grammar de.feu.Tasks with org.eclipse.xtext.common.Terminals
    
    import "http://www.eclipse.org/emf/2002/Ecore" as ecore
    import "platform:/resource/de.feu.company/model/company.ecore" as company
    
    generate tasks "http://www.feu.de/Tasks"
    
    Tasks :
     (developers+=Developers)*
     (domainmodels+=DomainModels)*
     (tasks+=Task)*;
     
    Developers :
     'developer' importURI=STRING;
     
    DomainModels :
     'domainmodel' importURI=STRING;
    
    
    Task: 'task' element=[ecore::EObject] 
       'by' developer=[company::Developer] 
       ':' description=STRING;
    
    This grammar defines a container element Tasks. Inside, we can "load" a list of developers and domain models. Eventually, tasks can be defined by assigning an element from a domain model (see element=[ecore::EObject]) to a developer (developer=[company::Developer]) and add a description of that task.
  4. Save the gammar and generate the ecore model along with the text editor using the MWE-workflow. For that, select file GenerateTasks.mwe and run the workflow via its context menu Run As / MWE Workflow
Notes:
  • In Xtext, a model is called a DSL. Experts like to use different names for "model", sometimes it's cooler to call a model "meta-model" (or, even cooler, "meta-meta-model"), sometimes they call it "domain specific language", abbreviated with a nice TLA (three letter acronym): DSL. Of course, there are good (and sometimes not so good) reasons for doing so, but usually it's easier to simply call a model a model (and an "instance of a model" an "instance of a model") .
  • The grammar shown in Figure 6 actually uses a lot of nice Xtext features and you will have to read the Xtext documentation for details. I only want to explain one thing here, which is a little bit advanced. You can import existing models into the grammar (import ...). In the example, we import the ecore model and our previously created company model. We import these models in order to be able to define inter-model references later on. Our Task element will refer to an element, which can be any ecore::EObject, and the developer is to one developer defined in our company model (company::Developer). EMF supports inter-model-references, and Xtext generated editors support that feature as well! The import statements in our grammar only import the models, but later on we want to actually import existing model instances. For that, we need the importURI feature of Xtext to define what instance to import in our actual task model instance.
Although we haven't written a single line of Java code yet, a lot of code has been generated automatically. In order to "activate" that code, which actually defines a new Eclispe plugin, we have to start a new runtime instance of Eclipse, that is we start Eclipse from within Eclipse. In the long run, you will have to get used to Eclipse plugin development anyway... As we have to run a new instance anyway, we can generate an editor for our company model as well. Instead of using a dynamically created instance using the "Generic EMF Form Editor", EMF can generate a Java implementation of the model and an editor as well. We will do that now:
  1. Select the company.ecore file and choose New / Other... / Eclipse Modeling Framework / EMF Generator Model from its context menu. Use the suggested file name, select Ecore Model in the next wizard page. Then, press Load in the next page, and eventually Finish on the last wizard page. This creates a generator model, which basically adds some information to the original ecore model which is necessary in order to actually generate code (e.g., the name of the plugins and so on). We do not want to change anything here, but we still need that model.
  2. In that model, select the very first element (Company) and select Generate All from its context menu (see Figure 7).
    Figure 7 Generate the model and editor code

Work with Multiple Models

Now that we have generate a lot of code, we want to use the newly created tools. Start a new Eclipse runtime instance (e.g. by selecting Run As / Eclipse Application from a project context menu) and do the following:
  1. Create a new project (in the runtime instance):
    File / New / Project... / General /Project
  2. Select the project and choose from its context menu:
    New / Other... / Example EMF Model Creation Wizard / Company Model
    This activates our previously generated editor (and a wizard) for creating a company model instance. We can edit a company instance just as we did it with the generic editor. In the wizard, select the Model Object Company and then create a new company as shown in Figure 8. (Don't forget to save the model ;-) ).
    Figure 8: A company model, edited with the generated editor
  3. Now, create a new file (File / New / File) inside the project and call it project.tasks. The Xtext generated editor is opened automatically and we can now edit the task model instance.
  4. In order to "simulate" a project, we create a simple UML use case model. Simply create a new use case diagram via File / New / Other... / UML 2.1 Diagrams / Use Case Diagram, give it a name (e.g. project_usecase) and draw some use cases, a sample is shown in Figure 9.
    Figure 9: A sample use case diagram
  5. Switch back to project.tasks and "import" our company model and the sample use cases. Add a task using content assist (Ctrl-Space) just as shown in Figure 10. Just play around, add three or four tasks in order to be able to follow the next steps.
    Figure 10: Content assist, demonstrating access to imported models
  6. Just as at the beginning, we want to execute some OCL queries on our model, but this time on our project.task model instance. For OCL, we always need a context, but unfortunately we cannot select a context (or model element) in the text editor. So we have to reopen the project.task with the generic EMF editor: Choose from its context menu Open With / Other... / Generic EMF Form Editor. You will now see the very same model as in the text editor, but this time you see a tree-based version of the model.
  7. Open the OCL console just as above, select an element (here Tasks) and enter a query. For example, we want to know how much tasks are assigned to a specific developer: self.tasks->select(developer.name='Jens')->size(). You can see that in Figure 11
    Figure 11 A little more sensible OCL query

Conclusion

IMHO it is impressing what you can do with all these cool Eclipse modelling tools, without writing a single line of (Java) code. We saw how to
  • create an EMF ecore model with the Ecore Tools diagram editor
  • open the same ecore model with the Generic EMF Form Editor
  • create instances of a model without the need to generate code and without starting a new runtime instance, using the "Create Dynamic Instance" feature (and the generic EMF editor again)
  • generate a text editor and a model without a single line of Java code with Xtext
  • generate a tree based editor and a Java based model implementation with EMF
  • create a UML diagram with the UML Tools diagram editor
  • query your models with OCL from the OCL project
We didn't bothered about how to store our models or model intances (it's all XMI), we were using OMG standards like UML (the EMF based implementation is provided by the UML2 project, EMF's ecore is an EMOF-like model, we were using OCL, other implementations are available, too (e.g., BPMN2, SBVR, or SPEM used by EPF). And if you do not find an Eclipse project, you probably will find a third party project providing an EMF based implementation ;-) With tools like GEF or GMF you can create (or generate) diagram editors for your models, and (well, I couldn't resist) with GEF3D you can even create 3D diagram editors, e.g. for visualizing inter-model connections. And there are many more tools out there, partially for simplifying the use of existing tools, for model transformations and code generation and so on. And of course you can adapt and modify the code generated above to suit your need.

Warning: Thin Ice!

While it is very easy to do impressive things (with the help of a tutorial), it is very hard to get into all these frameworks and tools. There are a lot of traps hidden everywhere! Just two examples from the tutorial:
  • In the Xtext grammar we used here, the ecore model was imported (see Fig. 6). I tried the very same using the UML2 model, and I got a weird error when generating the code (actually I couldn't generate in that case).
  • A NullPointer-Exception is thrown (you can see that in the console of the original Eclipse instance) when opening the project.tasks file with the Generic EMF Form Editor. Fortunately it still is possible to select an element (for the OCL query), but it's a little bit weird.
Well, I probably should file a bug report at least for the first problem... So be warned: Even if the tutorial gives you the impression as if modelling with Eclipse tools is very easy, it sometimes isn't. In general, modifying generated code as well as using tools like GMF is not that simple. But at least there are the newgroups, tutorials, and even books (e.g. the EMF book (2nd edition!) or the GMF book).

Disclaimer

This is a quick and dirty tutorial article. I simply documented an example modelling session by taking some screenshots, and then wrote some explanations. So, there probably are some things I've missed to tell you or errors in my text. Please leave me a comment if you find a bug here ;-) (or if you like the tutorial). May the Model be with you!

8 comments:

Lars Vogel said...

Hi Jens,

thanks, this is very good. One improvement suggestion: it would be nice if you could make the xtext grammer available as text and not only as screenshot.

Jens v.P. said...

@Lars: Yes, of course! Grammar is now available as text (see below Fig. 6).

Unknown said...

"""In Xtext, a model is called a DSL."""

The language is not the model. A model is an instance of a language.

"""Experts like to use different names for "model", sometimes it's cooler to call a model "meta-model" (or, even cooler, "meta-meta-model"),"""

:-) Sometimes it's even cooler not to mix everything up ;-)

""" sometimes they call it "domain specific language", abbreviated with a nice TLA (three letter acronym): DSL."""

We never call a meta-model (or a model) a DSL. The meta-model defines the abstract syntax of a language.

""" Of course, there are good (and sometimes not so good) reasons for doing so, but usually it's easier to simply call a model a model (and an "instance of a model" an "instance of a model") ."""

It's simple to say that, but it's not simple to understand. When discussing such abstract things like programming languages and their infrastructure it is vital to have good names which are understood by all participants. If we call everything a model you can't tell the different concepts apart.

Chris Aniszczyk (zx) said...

Thanks for this post Jens. This is one of the best introductions I've seen to the benefits of the Eclipse Modeling ecosystem.

Jens v.P. said...

@Sven: Oh, I'm so sorry. I didn't meant to offend you (or the Xtext team).

Of course, a DSL is not a model. Hmm... according to the metamodel hierarchy, a model is an instance of a meta-model. So, if a model is an instance of a language (according to your comment), it seems as if a language is a meta-model, isn't it? And a meta-model is a model, so it seems as if a DSL is a model....

No, it isn't (or is a DSL a meta-model but not vice-versa)? But why is a model an instance of a language (or is a model a word?). But what is a language, anyway. Why is UML called a language and not UMMM (for unified modeling meta model)? And what is the difference between a DSL and a formal language? And if I write an Xtext-based editor for a simple configuration file, does that mean my configuration file becomes a model? Actually, I wouldn't say that a model is an instance of a language ;-) (IMHO "instance-of" is simply the wrong concept for language).

When I create an Xtext grammar, an ecore model is created (if we call the thing stored as .ecore-file a model and not simply XML/XMI). But the ecore model is not an instance of the grammar, is it? Is it more or less the same (actually, less)?. I'd rather say that the grammar -- and it is not a pure EBNF but an annotated Xtext grammar -- describes both: the concrete and the abstract syntax. So an Xtext grammar is a grammar and a meta-model (uh... but a grammar is not the language...). Thus, editing an Xtext grammar is very similar to drawing an ecore diagram: I define my model. And besides I do some other things, like setting the positions of a rectangle in the latter case. In the first case, I define a concrete syntax as well, which has some advantages. This is why I added the remark: Instead of defining the model using an ecore tree or diagram editor, I use an Xtext grammar. Mainly because I didn't want to enter the field of compiler construction and formal languages here.

Models (and metamodels) describe structures (abstract syntax). Grammars describe concrete syntax, and a language is the set of all words which are well-formed. But since a model cannot be described in a pure abstract way (I cannot speak without using a language), we need a grammar to express it. So, the model becomes a word (in terms of a formal language) due to the need to write it down. But a word is not a model necessarily. A word (or language?) only becomes a model if we can interpret it as such. The problem (or I would say the benefit) of Xtext is to do that automatically (I'd assume that this is where the DS-part in DSL comes from?). So, if you want to discuss the infrastructure of languages, you need grammars, languages etc. (do you need the DS-part then?). In my case, I wanted to focus on the modeling things, and then an Xtext grammar becomes a kind of meta-model (and since I wanted to avoid the meta-discussion, I ended up in a saying DSL is a model..).

Unknown said...

You didn't offend me. I just wanted to correct your statement. And I also didn't want to be nitpicky.

I actually think we should have at little terms as possible and as much as needed. And especially in the modeling world there are a lot of unimportant terms.

But :-) I talk to people about this stuff day by day, and you wouldn't believe how often we confuse the different models. Therefore it's really helpful to have a different name for the model (which is the instance) and the model (which describes how the models (which are instances) look like).
Really. Believe me.

I don't give a shit whether we call them meta-model, ecore model or semantic model. I only want to understand people.

Forgive me, that I don't answer all the questions, you listed ;-)

Darie Moldovan said...

Hi Jens, I read your article, nice indeed.

Now a question: please take a look at Figure 5 in your article. Let's say we want to write an OCL query which returnes all the "Skill" elements of the company. We would probably write Skill.allInstances() in the OCL console and would get as a result 2 Skill-objects, namely modelling and hacking. Is there a way to automatically highlight/select these returned objects in the generic editor? Or, just think you would make an OCL query on a GMF diagram - the question stands. Is it possible to manipulate the instances of the objects, which are returned by an OCL query in the OCL console? If yes, what kind of approach would you recommend?

Thank you.

Jens v.P. said...

@darie17: I don't think highlighting the results of an OCL query is built in. The OCL console is "only" an example rather then a real tool. However, OCL is also built in EMFSearch, which maybe comes closer to the tool you are looking for -- or at least it is a good starting point for your own development. I tutored a master thesis once, which integrated the UML2 tools editor, GEF3D, and EMFSearch. By selecting a search result, the element got highlighted in the (3D-fied UML2 tools) editor, and a camera track automatically positioned the camera accordingly. This project demonstrated that it is not too hard to implement this highlight feature (if it is not even built in today, the thesis was written 3 years ago).