Skip to main content

skip to main content

developerWorks  >  Open source | Java technology  >

Model with the Eclipse Modeling Framework, Part 1: Create UML models and generate code

developerWorks
Document options

Document options requiring JavaScript are not displayed


Learn and share!

Exchange know-how with your peers -- try our new Pass It Along beta app


Rate this page

Help us improve this content


Level: Intermediate

Adrian Powell, Senior Software Developer, IBM 

15 Apr 2004

The Eclipse Modeling Framework (EMF) is an open source framework for developing model-driven applications. It creates Java™ code for graphically editing, manipulating, reading, and serializing data based on a model specified in XML Schema, UML, or annotated Java. EMF is the basis for many of the tools within IBM® WebSphere® Studio and Eclipse projects. This article will guide you through the process of creating a model, generating code, using the generated applications, and customizing the editor.

Just what is EMF, anyway?

EMF is an open source framework targeting Model-Driven Architecture (MDA) development. For the few of us lucky enough to get a UML model, it can help us turn the documentation into code. For the rest of us, it is another tool to convince your boss that spending the time to model your solution can really pay off. In addition to generating Java code with all sorts of bells and whistles, EMF can also generate Eclipse plug-ins and graphical, customizable editors. When you change your model (it happens, really), the EMF can keep the code synchronized to your model at the click of a button.

The EMF-generated code is no throw-away solution, either. It supports the standard create, retrieve, update, and delete operations, and it also supports cardinality constraints, complex relationships and inheritance structures, containment definitions, and a suite of attribute descriptions. The generated code provides notification, and referential integrity. All you have to do is create an object model, which you probably wanted to do, anyway.

EMF is relatively new, but it shows promise, and there are good signs for continued support. It is an implementation of a public standard — the Object Management Group's Meta-Object Facility (MOF) — and it now supports an enhancement of V2. Further, EMF is the basis of the Eclipse projects EMF:XSD and Hyades, and is used by most of the IBM WebSphere Studio products. V2 development has begun, and development builds should be out soon. The plans include better XML Schema support, more flexible code generation, and mapping between models.



Back to top


Let the tools do the talking

Enough marketing spiel. Let's jump right into the code to see what EMF can really do. These examples are all done using Eclipse V3.0M7 and EMF V2.0.0 with a matching XSD tool kit. There are four separate streams of EMF development, one for each version of Eclipse, so be sure to pick the right EMF version for your version of Eclipse (see Resources for these plug-ins).

We'll use a simple example of a Web forum to show off the significant features. The root of our model will be Forum, which will have a list of Members and Topics. Each Topic will have a TopicCategory (enumerated type), and Member, and Topic will be related indirectly via the Post class, and directly, as a Member can create a Topic.

Creating an EMF model with UML and Omondo

Omondo's UML plug-in is a handy and responsive plug-in for creating UML documents within Eclipse. It looks like the skinny, neglected brother of Rational® Rose, but unless you need the extra power, it works perfectly well. Unfortunately, they don't support Eclipse V3 yet, so I used Eclipse V2.1 to create the UML class diagram.

To start, create a new Java project called UMLForum and a new com.ibm.example.forum package. Create a new EMF class diagram called forum.ucd in src/com/ibm/example/forum. Two files are created: forum.ecd and forum.ecore. Add a new class called Forum and click Finished. Add a description attribute to your Forum class with a type of EString (Ecore types are available for all simple Java types), as shown in Figure 1. For features, select only changeable and set the bounds from 0 to 1.

If you change your mind about these features later, open the Properties view and select the class or attribute.


Figure 1. New Forum class with attribute properties
Eclipse screenshot with Forum and attribute properties

Repeat these steps for the following interfaces:


Table 1. Interfaces
Interface Attribute Type
Membernickname EString
Topictitle EString
Postcomment EString

To define the associations, select the Association button and click the source (Forum) and the target (Member). This brings up the Association Properties dialog. Set the name members, make sure only changeable and containment are selected, and set the upper bound to -1. On the second Association End tab, unselect Navigable and click Ok. Do the same for Forum and Topic, with an attribute name of topics, instead of members. Deselecting the navigable box creates unidirectional associations, but we want our other attributes to be bidirectional.

Complete the list of associations as follows:


Table 2. Associations
Source Target Association Name Features Bounds
MemberTopicFirst association topicsCreated changeable0 to 1
Second association creator changeable0 to 1
TopicPostFirst association posts Containment, changeable0 to -1
Second association topic changeable0 to 1
MemberPostFirst association posts changeable0 to -1
Second association author changeable0 to 1

Finally, we'll define an enumeration of the different types of topics available. Create a new enumeration called TopicCategory. For Literals, add the following:

  • ANNOUNCEMENT, value = 0
  • GUEST_BOOK, value = 1
  • DISCUSSION, value = 2

Then define a new attribute of Topic called category with type TopicCategory as changeable with bounds 0-1. You can change the default value in the property sheet if you wish, but we'll accept ANNOUNCEMENT as a default.


Figure 2. Complete UML class model
Eclipse screenshot with complete class model

Once you have completed the UML as shown in Figure 2, the next step is to create an EMF Model. To do this, create a new EMF Project (File > New > Project... > Eclipse Modeling Framework > EMF Project), and use com.ibm.example.forum as the project name (this will become the basis for the plug-in name, so we follow the naming conventions for Eclipse plug-ins). On the next page, select Load from an EMF core model and click Next. Browse your file system to load your ecore file, which should automatically fill in the generator model name. On the last page, click the checkbox beside your package and click Finish. This will create the EMF model: forum.genmodel. To see what this is and how to use it, skip to Using the generated EMF model.

Creating an EMF model with XML Schema

XSD isn't as expressive as UML or annotated Java code. It can't express bidirectional references, for example. But as the default serialization uses your schema, it is the fastest way to customize the serialization. If you wish to generate very specific XML from your model, XSD is the way to go.


Listing 1. Snippet of forum.xsd
<xsd:simpleType name="TopicCategory">
   <xsd:restriction base="xsd:NCName">
      <xsd:enumeration value="Announcement"/>
      <xsd:enumeration value="GuestBook"/>
      <xsd:enumeration value="Discussion"/>
   </xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="Post">
   <xsd:sequence>
      <xsd:element name="comment" type="xsd:string"/>
      <xsd:element name="author" type="xsd:anyURI" ecore:reference="forum:Member"/>
      <xsd:element name="topic" type="xsd:anyURI" ecore:reference="forum:Topic"/>
   </xsd:sequence>
</xsd:complexType>

You can see how to express an enumeration, and how to define a type with elements and references to other types. For the Forum example, we're only using string attributes, "xsd:string", but all simple Java types are supported. For more information, see Resources.

Once you have completed the XSD, the next step is to create an EMF Model. Similar to the UML model, create a new EMF Project (File > New > Project... > Eclipse Modeling Framework > EMF Project), with com.ibm.example.forum as the project name (this will become the basis for the plug-in name, so we follow the naming conventions for Eclipse plug-ins). On the next page, select Load from an XML Schema and click Next. Browse your file system to load your XSD file, which should automatically fill in the generator model name. On the last page, click the checkbox beside your package and click Finish. This will create the EMF model: forum.genmodel. To see what this is and how to use it, skip to Using the generated EMF model.

Creating an EMF model with annotated Java code

To define an EMF model using Java code, we use Interfaces to list the attributes of each class and relationships between them. This isn't rich enough to specify all the information that we desire, so EMF uses special JavaDoc tags. Each attribute or class that is a part of the EMF model must have a @model tag in its JavaDoc, and may have an optional list of additional attributes. For example, to build the object model as defined in Figure 2, our definition of Forum will look like Listing 2.


Listing 2. Annotated Forum.java
package com.ibm.example.forum;

import java.util.List;

/**
 * @model
 */
public interface Forum {
	
	/**
	 * @model type="Topic" containment="true"
	 */
	List getTopics();
	
	/**
	 * @model type="Member" containment="true"
	 */
	List getMembers();
	
	/**
	 * @model
	 */
	String getDescription();

}

Listing 2 will declare an object called Forum with a String description and two children, a list of Topics, and a list of Members. Both of these children are contained within Forum.

For the simple attributes, like description, the @model tag is enough, but for lists, you need to specify the type, as well. The containment attribute is optional, but if an object is contained, it gets serialized along with its container. To simplify serialization, we'll make sure all objects are directly or indirectly contained by Forum. Some other useful optional attributes include:

  • opposite (for bidirectional properties)
  • default (the default value for an attribute)
  • transient (the attribute won't be serialized)

For a complete list, see the EMF user's guide in Resources.

The only other thing to watch out for is that an enumerated type is defined using a Class and not an Interface like other model classes. To make it clear, Listing 3 shows how to implement the TopicCategory enumerated type.


Listing 3. Enumerated type, TopicCategory.java
package com.ibm.example.forum;

/**
 * @model
 */
public class TopicCategory{
	/**
	 * @model name="Announcement"
	 */
	public static final int ANNOUNCEMENT = 0;
	
	/**
	 * @model name="GuestBook"
	 */
	public static final int GUEST_BOOK = 1;
	
	/**
	 * @model name="Discussion"
	 */
	public static final int DISCUSSION = 2;
}

To complete the model, generate the final three interfaces as follows:


Table 3. Final interfaces
Interface Method Model tags
Member List getPosts() type="Post" opposite="author"
List getTopicsCreated() type="Topic" opposite="creator"
String getName()
Topic List getPosts() type="Post" opposite="author"
Member getCreator() opposite="topicsCreated"
String getTitle()
TopicCategory getCategory()
Post Member getAuthor opposite="posts"
Topic getTopic() opposite="posts"
String getComment()

When the model has been defined, generate an EMF model (File > New > Other > Eclipse Modeling Framework > EMF Models). Set the parent folder to com.ibm.example.forum/src/model and the File name to forum.genmodel. On the next page, select Load from annotated Java and then select the checkbox next to the package "forum." Then click Finish. This will create the EMF model, forum.genmodel.



Back to top


Using the generated EMF model

You should now have a generated EMF model in your workspace: forum.genmodel. This model contains all of the information you entered into your model. Open the model with the default editor (see Figure 3) and open the Properties view, then examine the properties of each node in the model tree. All of the attributes that were entered earlier may be customized, but there are also properties to customize the code generation. To experiment, try changing some properties such as "Copyright Text" or "Generate Schema" and see what happens.


Figure 3. Generated EMF model open in default editor
Eclipse screenshot with generated EMF model

If you make changes to your model description (UML, XSD, Annotated Java), you may reload this model by right-clicking the model in the Package Explorer and selecting Reload. This will synchronize the EMF-generated model with the model description. Properties that you change in the generated model won't change after a reload.



Back to top


Generating Java code

When you are satisfied with the model description, or if you just want to see what all of this means, it's time to generate your code. Right-click on the root node and select one of the generate options: Model, Edit, or Editor code. Generate Model will create the Java implementation of the EMF model in the current project. It will contain the following:

  • com.ibm.example.forum -- Interfaces and the Factory to create the Java classes
  • com.ibm.example.forum.impl -- Concrete implementation of the interfaces defined in com.ibm.example.forum
  • com.ibm.example.forum.util -- The AdapterFactory

Generate Editor Code will create the com.ibm.example.forum.edit project. This contains only one package, com.ibm.example.forum.provider, which is used to control the way each model object appears within an editor. Generate Editor Code will create a sample plug-in editor in the com.ibm.example.forum.editor project, which contains com.ibm.example.forum.presentation. These classes provide a series of simple JFace editors that interact with your model.

To test your generated plug-in, go Run > Run... > Run Time Workbench > New. Give it a descriptive name, and under the plug-ins tab, select launch with all workspace and enabled external plug-ins. Under Common, click Display in favorites menu > Run, and Launch in background. Save this configuration and run.

A new Eclipse workbench will come up, and you can verify under Help > About Eclipse Platform > Plug-in Details that your plug-in is enabled, as shown in Figure 4.


Figure 4. Plug-in details with Forum
Eclipse screenshot with custom plug-in list

To test the generated plug-in, create a new Simple project named Forum Demo, then go to New > Other... > Example EMF Model Creation Wizards > Forum Model. Give it a filename of sample.forum, and pick Forum as the Model Object. This will bring up a window where you can add new model elements to the root. There are several views: Selection, Parent, List, Tree, Table, and TreeTable. All will display the same data and are synchronized with the Outline view. While all views will display the New Sibling/New Child right-click menu options, I have found that some of the views respond improperly when children or siblings are added. If you experience this, us the TableTree view or create new nodes in the Outline view. Figure 5 shows the generated plug-in editor.


Figure 5. Generated plug-in editor
Eclipse screenshot with custom plug-in



Back to top


Customizing the generated code

All of this generated code is nice, but it is just a starting point for real-world applications. To fit our requirements, we must make adjustments and customizations, from changing the implementation of the generated model classes to extending and customizing the editors. Fortunately, EMF doesn't let us down. We can make all of the customization we wish and not lose any of our changes when we regenerate. All we have to do is remove the @generated JavaDoc tag, and EMF's jmerge will make sure that the method, attribute, or class is left alone.

To highlight some of the changes you can make, let's go through a simple example. In the Table view of the generated editor, two columns appear with the same values. This isn't terribly useful. To make it a bit better, we'll change the second column to display the Author when a Topic is selected, then add a third column that will give the number of posts in the Topic.

The first step is to add the extra column to the Table view. This is done in the com.ibm.example.forum.editor project, as com.ibm.example.forum.presentation.ForumEditor in the createPages() method. Remove the @generated tag to make our changes persistent, then locate the section for the table viewer. Modify the code as shown in Listing 4.


Listing 4. Modified createPages()
TableColumn selfColumn = new TableColumn(table, SWT.NONE);
layout.addColumnData(new ColumnWeightData(2, 100, true));
selfColumn.setText("Author");
selfColumn.setResizable(true);

TableColumn numberColumn = new TableColumn(table, SWT.NONE);
layout.addColumnData(new ColumnWeightData(4, 100, true));
numberColumn.setText("Number of Posts");
numberColumn.setResizable(true);

tableViewer.setColumnProperties(new String [] {"a", "b", "c"});

This will add the extra column, but now all three columns will display the same data. To customize the data for each column, we need to provide some ITableItemLabelProvider implementations. Open com.ibm.example.forum.provider.TopicItemProvider and add ITableItemLabelProvider to the implements list. We need to add two methods, getColumnText(Object, int) and getColumnImage(Object, int) as shown in Listing 5.


Listing 5. TopicItemProvider addition
public String getColumnText(Object obj, int index) {
	if( index == 0 ){
		return getText(obj);
	}
	else if( index == 1 ) {
		return ((Topic)obj).getCreator().getNickname();
	} else if( index == 2 ) {
		return " + ((Topic)obj).getPosts().size();
	}
	return "unknown";
}
public Object getColumnImage(Object obj, int index) {
	return getImage( obj );
}

Finally, we need to register this provider. Do this by editing the constructor of com.ibm.example.forum.provider.ForumItemProviderAdapterFactory to add ITableItemLabelProvider to the list of supported types, as shown in Listing 6.


Listing 6. ForumItemProviderFactory constructor
public ForumItemProviderAdapterFactory() {
	supportedTypes.add(ITableItemLabelProvider.class);
	supportedTypes.add(IStructuredItemContentProvider.class);
	supportedTypes.add(ITreeItemContentProvider.class);
	supportedTypes.add(IItemPropertySource.class);
	supportedTypes.add(IEditingDomainItemProvider.class);
	supportedTypes.add(IItemLabelProvider.class);
}

Now when we run the plug-in and go to the table view, we see Figure 6. Notice that the elements that don't implement ITableItemLabelProvider will display the same text in all columns.


Figure 6. Modified Table editor
Modified Table editor



Back to top


Manipulating the model in Java

The generated model code looks just like other Java code with a few helpful additions. There is a flexible, customized reflection API that is useful for tools. This is the eGet() and eSet() methods you may notice. This doesn't concern us for the most part, so we'll leave it for the interesting bits: how to create, save, and load a model. Let's start at the beginning: loading an EMF model.


Listing 7. Loading the Forum
// Register the XMI resource factory for the .forummodel extension
Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
Map m = reg.getExtensionToFactoryMap();
m.put("forummodel", new XMIResourceFactoryImpl());
ResourceSet resSet=new ResourceSetImpl();
Resource res = resSet.getResource(URI.createURI("model/forum.forummodel"),true);

Forum forum = (Forum)res.getContents().get(0);

Listing 7 shows how to associate files with an extension of forummodel to be in XMI format, then use the EMF's ResourceSet to parse and load our forum model. We know that Forum will be the only root element, so res.getContents().get(0) can be assumed to return the one and only Forum object. If this were not the case, we could extract an Iterator from getContents().iterator() and examine each element separately.

Alternately, we could create a new Forum and populate it programmatically as shown in Listing 8.


Listing 8. Initializing the Forum
// initialize model and dependencies
ForumPackageImpl.init();
			
// retrieve the default Forum factory singleton
ForumFactory factory = ForumFactory.eINSTANCE; 
		
Forum forum = factory.createForum();
forum.setDescription("programmatic forum example");
		
Member adminMember = factory.createMember();
adminMember.setNickname("Administrator");
forum.getMembers().add( adminMember );

Topic noticeTopic = factory.createTopic();
noticeTopic.setTitle("Notices");
noticeTopic.setCategory(TopicCategory.ANNOUNCEMENT_LITERAL);
noticeTopic.setCreator(adminMember);
forum.getTopic().add( noticeTopic );

In this example, we first initialize the package and then create a ForumFactory that is used to create all of the subobjects. Once created, they are accessed like any standard JavaBean. However, since we have declared the creator/topicsCreated relationship between Topic and Member to be bidirectional, when we call noticeTopic.setCreator(adminMember), the list of topicsCreated of adminMember will now include noticeTopic.

Once we have created and manipulated the EMF model, it is a simple matter to save it to the format of our choosing (see Listing 9).


Listing 9. Saving the Forum
URI fileURI = URI.createFileURI("model/forum.ecore");
Resource resource = new XMIResourceFactoryImpl().createResource(fileURI);
resource.getContents().add( forum );
try {
	resource.save(Collections.EMPTY_MAP);
} catch (IOException e) {
	e.printStackTrace();
}

In this example, we give the filename that we wish to save to with URI.createFileURI() and the target format. In this example, because we're saving to XMI we use the XMIResourceFactoryImpl. Once this has been created, we add all of the model objects we wish to persist. In our case, as each object is contained in another class except for Forum, we only need to add this to the root to have all of its children. If some objects don't have a contains relationship, then they too must be explicitly added through resource.getContents().add(). If you don't, you'll get an exception when you call resource.save().



Back to top


Summary

The Eclipse Modeling Framework provides the tools to do model driven development. It contains the elements necessary to keep your development focus on the model and not on the implementation details. The main areas of focus are: generation of models that support customization, notification, referential integrity, and other essential features; generation of customizable model editors; and default serialization. As shown in the examples, generation is simple and straightforward, and all of the customized code supports customization. Individual tools, like the serialization or the graphical editor, may be pulled out and used independently, but the full power comes when all areas are used together. Already, EMF is used within many successful projects and is continuing to grow.



Resources

Learn

Get products and technologies

Discuss


About the author

Adrian Powell started work with Java tooling at IBM when he joined the VisualAge for Java Enterprise Tooling team where he spent two misguided years manually writing a code generator. Since then, he has developed tools and plug-ins for almost every version of Eclipse and VisualAge for Java. Adrian is currently working in the Vancouver Centre for IBM e-Business Innovation, where he is writing his replacement.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top