Metamodeling with EMF: Generating concrete, reusable Java snippets

How to extend the Eclipse Ecore metamodel


EMF is an integral part of the Eclipse platform, as well as a cornerstone of related technologies and frameworks, such as the Eclipse Visual Editor, SDO, XSD, and UML — many of which are integrated into IBM platforms like Rational® Application Developer and WebSphere® Business Modeler. Today, EMF has grown to encompass Java technology features, such as enumerated types, annotations, and generics. If you are new to EMF, see Related topics for articles that will help you get started.

In most documents and tutorials, EMF is used to model data and interfaces (e.g. Library and Books in the EMF release documentation), and not behavior. Of course, there are some default method implementations generated for data objects, but these concern relationships between model elements. Moreover, there are very few documented examples of EMF being used as a "meta-metamodel" — with the exception of the Eclipse Foundation article "Modeling Rule-Based Systems with EMF" (see Related topics) — but not a single example showing how to extend the Ecore metamodel.

Finally, the process of using and extending the EMF JET templates is not well documented. In addition, the JET Editor project has moved to another Eclipse project (M2T). This article aims to clarify these issues and enable you to do more with dynamic templates in the context of EMF. As such, it assumes basic familiarity with EMF.

Why extend the Ecore metamodel?

The Ecore metamodel is a powerful tool for designing Model-Driven Architecture (MDA), which can be used as a starting point for software development. Typically, we would define the objects (of type EClass) in our domain of application, their attributes, and their relationships. We would also define the specific operations that belong to those objects using the EOperation model element. By default, EMF will generate skeletons, or method signatures, for those operations, but we have to go back and implement those operations, often recoding similar logic time and again.

But what if we want to specify some sort of arbitrary implementation behavior right in the model? One approach is to add text-based annotations (of type EAnnotation) to model objects and to interpret those annotations in templates during code generation. For a good example of this, see the Eclipse Foundation article "Implementing Model Integrity in EMF with MDT OCL" (see Related topics). However, our object is not to validate model elements as described in the article but rather to model implementation itself, in order to reuse those metamodel elements with any concrete model. To do this, we need to extend the Ecore metamodel.

The extended metamodel

Accompanying this article is a highly simplified programmatic model that extends Ecore. It is not a complete or coherent metamodel or framework; it is strictly a prototypical set of elements for illustrating the potential of metamodeling code implementation with EMF. Figure 1 shows a snapshot of our sample extended metamodel called EcoreX, followed by a brief description of each element.

Figure 1. EcoreX model
EcoreX model
EcoreX model

EcoreX elements

EPackageX extends EPackage
This is a simple "marker" extension of Ecore element EPackage with no additional attributes. This element is necessary because by default, the EMF Editor Plug-in for element EPackage will not allow a subclass of EClass to be added as a child element (see EClassX, below). By providing a model element that extends EPackage, code will be automatically generated to allow adding an EClassX child element to an EPackageX.
EClassX extends EClass
Again, this is a simple marker extension of Ecore element EClass with no additional attributes. Similar to above, this element is necessary because by default, the Editor Plug-in for EClass will not allow adding subclasses of EOperation— which is our objective in this article.
EOperationImpl extends EOperation
This is the principal entity and entry point for adding concrete metafunctionality to the Ecore model. This element is conferred with attributes that do not exist in the base EOperation element of Ecore. All other elements described below belong to EOperationImpl and are used to compose programmatic implementation. For example, an EOperationImpl has variables and statements, and can return a reference or value.
LocalVariable extends ETypedElement
LocalVariable is a locally scoped variable. A variable has a name and a Java type (e.g., String, Integer, Object), and since these attributes exist already in its super-superclass EParameter, LocalVariable doesn't need additional attributes.
Statement extends EClass
In our simplified logic model, an EOperationImpl contains a number of statements that will be evaluated in a given order. Statement is an abstract super-class.
LiteralAssignment extends Statement
A LiteralAssignment refers to a variable and has a String attribute allowing the user to enter a value to be parsed and assigned to a variable (e.g., "hello," "4.5" could be assigned to a String or float, respectively).
Access extends Statement
An Access represents the act of referencing a Java field or operation.
FieldReferenceAssignment extends Access
Accesses a field in order to assign a value (e.g., var1 =
Invoke extends Access
Invokes an operation (Java method). The result of an Invoke can be assigned to a variable (e.g., myVar = obj.toString()).

Figure 2 offers a more UML-like representation of the EcoreX metamodel.

Figure 2. Ecorex model diagram
Image shows Ecorex model diagram
Image shows Ecorex model diagram

Getting started

There are six high-level steps to this article:

  1. Extend the Ecore metamodel, adding new semantics
  2. Create a genmodel for the extended metamodel.
  3. Generate an EMF Editor for the metamodel, and install its plug-in.
  4. Using the new editor, build a concrete model describing programmatic behavior.
  5. Create and configure a genmodel for the concrete model.
  6. Generate concrete Java code, based on the concrete model.

You can create or import the metamodel described above. In both cases, you should start with an existing EMF project or create a new one (New > Other > Eclipse Modeling Framework > Empty EMF Project). Ours is called EMFX, and it should contain a folder named model. You can either copy the EcoreX.ecore model (see Related topics) into the model directory and skip to Build and launch the Editor Metamodel plug-in or follow the steps below to recreate a metamodel from scratch.

Extending the Ecore metamodel — Starting from scratch

Right-click the project and from the context menu, select New > Other > Example EMF Model Creation Wizards > Ecore Model. (On Eclipse V3.5+ [Galileo, Helios], the selection is New > Other > Eclipse Modeling Framework > Ecore Model.) Choose the model folder and the name EcoreX.ecore.

By default, we call the model package ecorex. Right-click in the model window and choose Load Resource > Browse Registered Packages. Choose the Ecore Model with namespace

Once you have imported the Ecore metamodel, you are ready to extend it. To recreate the ecorex.ecore model, start by right-clicking over the package element ecorex and select New Child EClass. Call this element EPackageX (see model element descriptions, above). You then need to add the base element EPackage as the ESuper Type for this new element.

Use the same procedure for creating new element EClassX by designating EClass as the ESuperType. Continue defining the other EClasses in the EcoreX model by subclassing Ecore objects when necessary. Use Figure 1 and the EcoreX.ecore file to know which attributes to create for which EClass.

Build and launch the Editor Metamodel plug-in

In the build step, we will create the metamodel genmodel and build the model and the editor projects. Right-click the EcoreX model and select New > Other > Eclipse Modeling Framework > EMF Model. (For EMF V2.5+ [Galileo, Helios], the selection is New > Other > Eclipse Modeling Framework > EMF Generator Model.) You may supply a name or accept the default name EcoreX.genmodel. The EcoreX model should be pre-selected as a base model for the genmodel. Click Load to validate the EcoreX.ecore metamodel.

Figure 3. New EMF model
New EMF model
New EMF model

When asked to specify which packages to generate and which to reference from other generator models, you will select the EcoreX package under Root packages and Ecore under Referenced generator models.

At this point, the wizard will create a genmodel for the metamodel. You can now auto-generate the associated code by selecting Generate All from the context menu after highlighting the top-level element in the genmodel. This will generate four Eclipse projects, according to the behavior configured in the genmodel. This article will not be concerned with the .test project, so you may wish not to generate that plug-in.

Now we move on to the launch step. In most Eclipse tutorials, you are asked to launch your developed plug-ins in a separate Eclipse process. In this section, we will take a different approach: We will activate our plug-ins within the current Eclipse and workspace. This makes it easier to integrate the pre-built metamodel with the concrete model development in the next section. To do this:

  1. Double-click the EMFX plugin.xml to open the plug-in configuration editor.
  2. Click the Export Wizard under the Exporting tab.
  3. Select the principal modeling plug-in and the two editor plug-ins.
  4. Under the Destination tab, choose your Eclipse installation directory, or host repository (if available).
Figure 4. Export
Image shows Export method
Image shows Export method

When you click Finish, the generated plug-in JARs are built and copied to the plugins directory automatically. At this time, you need to restart Eclipse to activate the new plug-ins. Now we are ready to launch the editor plug-in by creating a new project to hold our concrete model (ours will be named Test2).

Within this new project, navigate to New > Other Example EMF Model Creation Wizards > Ecorex Model and provide a model name. Note: In recent versions of EMF (V2.5+), the file extension of the concrete model must be set to .ecore and not .ecorex; otherwise, the concrete genmodel cannot be created in a later step.. Select the EPackageX element. You now have an empty concrete model. The following sections explain how to build the programmatic model elements; the finished file, My.ecore can be found in Related topics.

Model the concrete test model

In this section, we will model a concrete Java class (instance of EClassX) having two concrete methods whose implementation we will model. The first example method takes a String parameter message and prints the message with a timestamp — useful for debugging messages, for example. The following is a representation of the desired outcome.

Listing 1. printTimestampMessage
void printTimestampMessage(String message) {
	System.out.print("; Timestamp= ");

The second example takes three date-based parameters and returns a numerical value representing the day of the week upon which that date falls.

Listing 2. getDayOfWeek
int getDayOfWeek(int year, int month, int date) {
	int result;
	Calendar calendar = Calendar.getInstance();
	calendar.set(year, month, date);
	result = calendar.get(Calendar.DAY_OF_WEEK);
	return result;

The first step is to fill in the three required attributes under the new EPackageX element we created in the last step of the last section. If you don't see the Properties tab below the modeling window, you can choose Show Properties View from the context menu. In this example, our package is called mypackage.

Figure 5. EPackageX properties
Image shows EPackageX properties
Image shows EPackageX properties

Next, add a new EClassX to mypackage. You can do this using the context menu while mypackage is highlighted. Fill in the name attribute to give the class a name (e.g., MyClass), add two EOperationImpl elements to the new class, and give them the method names printTimeStampMessage and getDayofWeek, above. Then, for each operation, add Ecore parameters.

Figure 6. EOperationImpl getDayOfWeek()
Image shows EOperationImpl getDayOfWeek()
Image shows EOperationImpl getDayOfWeek()
Figure 7. getDayOfWeek() properties
Image shows getDayOfWeek() properties
Image shows getDayOfWeek() properties

Above, the operation printTimestampMessage() takes one parameter of type EString, while getDayOfWeek() takes three parameters of type EInt. In addition, operation getDayOfWeek returns an EInt, configured under property attribute EType (see Figure 7).

Anatomy of an EOperationImpl

Until this point, we have only dealt with inherited Ecore elements and attributes. Now it's time to build the Java implementation using our extended metamodel elements.

Looking at Figure 8, the printTimestampMessage() will require two LocalVariable elements — one of type EString and one of type ELong.
Figure 8. printTimestampMessage()
Image shows printTimestampMessage()
Image shows printTimestampMessage()
Figure 9. LiteralAssignment
Image shows LiteralAssignment
Image shows LiteralAssignment

In Figure 9, the string for attribute Value is in-lined into the LiteralAssignment. You could imagine a different metamodel in which literal values (constants) are modeled as separate elements.

Next, we insert an element of type LiteralAssignment, which allows us to choose a LocalVariable and assign a value to it. In this case, we choose the String variable and supply the text value from the prototype method above (remember to include quotes to enclose the text).

Again looking at the figure above, notice that there is an Ecore DataType called SystemType that is a wrapper for java.lang.System. This must be added to our mypackage package because it will be referenced by the Invoke elements that follow.
The first Statement added to this operation is an Invoke of static method currentTimeMillis() on SystemType, defined above.
Figure 10. Invoke currentTimeMillis() properties
Image shows Invoke currentTimeMillis() properties
Image shows Invoke currentTimeMillis() properties

According to our metamodel (once we provide the code templates in the next section), the above Invoke will translate into the Java statement: timestamp = java.lang.System.currentTimeMillis();.

The next Invoke is subtly different from the previous. First of all, there is no Assignment. Second, we make reference to the message parameter as an argument for the property called Args.

Figure 11. Invoke out.print properties
Image shows Invoke out.print properties
Image shows Invoke out.print properties

Also notice that the value of the Access Name property is out.print— we are cheating somewhat in that we are really de-referencing field out from Java System, then invoking method print. As it stands, we use this shortcut instead of using a FieldReferenceAssignment together with a LocalVariable of type PrintStream.

The third and final Invoke in the operation is a println() using the LocalVariabletimestamp as a single argument. This completes the model of concrete operation printTimestampMessage().

Let's look at the complete model of the second EOperationImplgetDayOfWeek().

Figure 12. getDayOfWeek()
Image shows getDayOfWeek()
Image shows getDayOfWeek()
At the bottom of the model, there is an additional DataType we created called CalendarType that is required by this operation.
Among the three LocalVariables in the operation model, we are particularly concerned with the one called result, since it will hold the value to be returned after the last statement of the operation. Among the EOperationImpl properties is one called Return Ref, and in our implementation, we use the pull-down menu to select the LocalVariable result.
In Figure 12, following the three LocalVariables are three Statements. The first is an Invoke using getInstance() on element CalendarType, which assigns a value to variable calendar, analogous to what was done in Figure 10.

Next, there is an Invoke of method set() on variable calendar, this time passing three Args corresponding to the EOperationImpl parameters: year, month, and date.

Figure 13. set() with parameters
Image shows set() with parameters
Image shows set() with parameters
Figure 14. FieldReferenceAssignment
Image shows FieldReferenceAssignment
Image shows FieldReferenceAssignment

According to our metamodel, this element will yield Java code similar to DAY = Calendar.DAY_OF_WEEK;.

In Figure 15, the DAY variable is used in the last Invoke of this EOperationImpl: a get() whose return value is assigned to variable result (the Return Ref of our implementation).

Figure 15. Return Ref
Image shows Return Ref
Image shows Return Ref

Implementing dynamic templates

We have now designed an extended metamodel and used it to describe a concrete model called My.ecore (see EMF V2.5+ file name note, above). It is finally time to generate some code implementation using JET. In order to see JET template syntax highlighting, you will need to install the JET Editor Plugin (see Related topics and "Jet Editor Limitations").

By default, EMF does not use dynamic templates when generating code for models. It uses pre-built Java classes. To get started customizing JET templates, we need to copy some files from a plug-in JAR org.eclipse.emf.codegen.ecore_2.3.0.XYZ.jar, where XYZ is the time stamp of your EMF build in the Eclipse plugins folder. This article uses org.eclipse.emf.codegen.ecore_2.3.0.v200706262000.jar. To copy the files, open the JAR file with any ZIP utility and:

  1. Extract the templates directory from this JAR into the Java project for your concrete model.
  2. Create a directory called Class in templates/model.
  3. Create new empty file in the Class folder called implementedGenOperation.TODO.override.javajetinc or copy from Related topics.

As the name suggests, the new file in step three is the JET template where we will put our code-generation logic for model objects EOperationImpl. By default, this file does not exist because EMF simply provides an empty method signature for each EOperation. Once we activate the dynamic templates facility, our new file will be automatically included as the body of the Java method as defined by an EOperationImpl.

The following is the complete code of implementedGenOperation.TODO.override.javajetinc.

Listing 3. implementedGenOperation
// created by implementedGenOperation.TODO.override.javajetinc
<%if ( ! (genOperation.getEcoreOperation() instanceof EOperationImpl) ) { %>
	// TODO: implement this method
	// Ensure that you remove @generated or mark it @generated NOT
		thrownew UnsupportedOperationException();
<% } else { %>
	// ** EOperationX implementation **
<% EOperationImpl opx = (EOperationImpl)genOperation.getEcoreOperation();
Statement stm = null;
Iterator iterator = null;

EList<LocalVariable> pList = opx.getLocalVariables();
LocalVariable lvar = null;

String iname = null;
StringBuffer paramsString = null;
StringBuffer varString = null;
for (int i = 0;i < pList.size(); i++) {
	lvar = pList.get(i);
	iname = lvar.getEType().getInstanceClassName();%><%=iname%><%=lvar.getName()%><%if (iname.startsWith("java")) { %> = null<% } %>;
<% }

iterator = opx.getStatements().iterator();

while (iterator.hasNext()) {

	paramsString = new StringBuffer();
	varString = new StringBuffer();
	iname = null;
	stm = (Statement);
	if (stm instanceof LiteralAssignment) {%><%= stm.getAssignment().getName()%> = <%=\
	<%} else 
	if (stm instanceof FieldReferenceAssignment) {
		Access ax = (Access)stm;	
		if (stm.getAssignment() != null) {
		varString.append(" = ");
			if ( ax.getStaticType() != null) {
			// STATIC
			iname = ax.getStaticType().getInstanceClassName();
			} else {
			iname = ax.getTarget().getName();
			} %><%=varString.toString()%><%=iname%>.<%=ax.getAccessName()%>;
		<% } elseif (stm instanceof Invoke) {
		Invoke iv = ((Invoke)stm);
		if (stm.getAssignment() != null) {
			varString.append(" = ");
		for (int p = 0; p < iv.getArgs().size(); p++) {\
			if ( p + 1 < iv.getArgs().size() ) {
			paramsString.append(" , ");
				if (iv.getStaticType() != null) {
				// STATIC
				iname = iv.getStaticType().getInstanceClassName();
				} else {
				iname = iv.getTarget().getName();
				} %><%=varString.toString()%><%=iname%>.<%=iv.getAccessName()\
	<% }
	if (opx.getReturnRef() != null) { %>return<%=opx.getReturnRef().getName()%>;
	<% }	


The specifics of JET are beyond the scope of this article. However, since the JET template is clearly crucial to our process, we will review the content of the template in terms of pseudo-code, keeping in mind that the first variable, genOperation, is pre-initialized by Ecore/JET before the template is processed.

Listing 4. genOperation is pre-initialized by Ecore/JET
Is this GenOperation is an EOperationImpl?If false, emit default UnsupportedOperationException

Else, cast it to EOperationImpl;

Find and declare all elements of type LocalVariable, initializing Java Objects to null;

Iterate through all Statements;
	Emit Java code according to the subtype;

Does the implementation return something?
	If yes, emit the return statement;

There are a few actions to perform before building the concrete model. First, at the top of templates/model/Class.javajet, we must add to the list of imports (the first two marked in bold):

imports="ecorex.*  org.eclipse.emf.common.util.* \
java.util.* org.eclipse.emf.codegen.ecore.genmodel.*"…

The package EcoreX is, of course, the extended metamodel. Next, we need to create and configure an EMF (GenModel) for our concrete model (My.ecore, of type '.ecorex'). To do this, right-click on the model and choose New > Other > Eclipse Modeling Framework > EMF Model. (With EMF V2.5+ [Galileo, Helios], the selection is New > Other > Eclipse Modeling Framework > EMF Generator Model.) Once created, three properties need to be configured under property group Templates & Merge, and the fourth under Model.

Figure 16. GenModel— Templates & Merge
Image shows GenModel
Image shows GenModel
  1. Set Dynamic Templates to true.
  2. Specify the Template Directory.
  3. Add EMFX (extended metamodel plug-in ID) to Template Plug-in Variables.
  4. Recent versions: under the Model group property, set Suppress Interfaces to true.

At this point, you're ready to build by right-clicking on the GenModel and choosing Generate Model Code. If all goes well, in the source folder (src) of your concrete Test project (ours is called Test2), you should see the generated Java source packages and classes, including one called Opening the file, you should see your two generated methods.

Listing 5.
publicvoid printTimestampMessage(String message) {
	// created by implementedGenOperation.TODO.override.javajetinc
	// ** EOperationX implementation **
	java.lang.String timestampStr = null;
	long timestamp;
	timestampStr = "; Timestamp = ";
	timestamp = java.lang.System.currentTimeMillis();
publicint getDayOfWeek(int year, int month, int date) {
	// created by implementedGenOperation.TODO.override.javajetinc
	// ** EOperationX implementation **
	int result;
	int DAY;
	java.util.Calendar calendar = null;
	calendar = java.util.Calendar.getInstance();
	calendar.set(year , month , date);
	DAY = java.util.Calendar.DAY_OF_WEEK;
	result = calendar.get(DAY);
	return result;

You can test this class by adding a main method.

Caveats and troubleshooting

Ecore file naming (EMF V2.5+)

Prior to EMF V2.5, as shown in several screenshots above, a concrete model produced from an extended Ecore model could keep the file extension '.ecorex' (as proposed by the wizard at creation time). This helped to distinguish an extended model from a 'first level' Ecore model. However, in recent versions of EMF, the genmodel wizard (as explained just before Figure 16) will not accept file extensions other than .ecore.

JET Editor limitations

To obtain color-coded syntax highlighting of JET templates, you need to have the Eclipse JET Editor installed (the JET Editor Plugin has recently moved from EMF to M2T).

However, at the time of this writing, the most recent version of the JET Editor does not properly handle Java content-assist or on-the-fly compilation for nested JET includes, such as .javajetinc files. Additionally, imports can only be specified in the parent file (e.g., Class.javajet, above) and not in the included file in order for the build to succeed.

You can, in fact, turn an EMF dynamic templates project (Test2 in our example) into a JET project, with some additional configuration (i.e., using the context menu for the project). In practice, the above-mentioned limitations, combined with the lack of integration between EMF and M2T/JET, makes this impractical today.

As a result, it can be difficult to catch and correct errors in included template files. Since JET templates are first compiled into an intermediate Java file (located by default in a hidden Java project called JETEmitter) before the final code is emitted, you can see these compilation errors by removing the filter from the Package Explorer view in Eclipse. If it's purely a formatting error in the template file, you will get an Eclipse pop-up window during build. Perhaps we can look forward to more evolved functionality in a future release of JET.

No model validation

The examples in this article do not use the EMF Validation Framework or the OCL facility. As such, inconsistencies in the model will cause build failures. For example, an EOperationImpl may declare a certain return type, but the Return Ref property may refer to a different type or be null. These errors would not be caught during model build, and the generated code would not compile. A more evolved metamodel would enforce integrity and constraints using OCL (see Related topics).


We have seen how to extend the Ecore metamodel to conceptualize simple programmatic behavior within a synthetic Java method. We extended several Ecore model elements — most notably EOperation— by importing Ecore itself. We then built our metamodel, and then used the editor to design a concrete test model, including two Java methods modeled in the form an EOperationImpl. We configured and built JET templates for generating code for the EOperationImpl.

Downloadable resources

Related topics


Sign in or register to add and subscribe to comments.

Zone=Open source
ArticleTitle=Metamodeling with EMF: Generating concrete, reusable Java snippets