openArchitectureWare.org

oAW has moved to Eclipse.

At this site you will find information about the outdated version openArchitectureWare 4, only.
Please read our letter of intent for further information.

 
   

HowTo: Instantiating EMF metamodels from arbitrary XML/XMI models

This article will demonstrate how to utilize openArchitectureWare's Classic module to instantiate an Ecore based metamodels from arbitrary XML documents. The main idea is to reuse the tool mapping facilities that are used to adapt the XMI export files from UML tools, but instantiate dynamic Ecore instead of 'classic style' metaclasses. The necessary extension to openArchitectureWare Classic exists since version 4.1.

NOTE: The described proceeding is not recommended any more. openArchitectureWare has now an XSD adapter, which allows handling of schema-based XML files smarter. The adapter will be included from openArchitectureWare 4.3.1 onwards and can be downloaded seperately before this release. See this page for further information on the XSD adapter.

Example XML format

As an example we take an XML format that is used by a tool suite called Echo Studio. Echo Studio is a RAD tool for creating web applications using the Echo2 OpenSource framework. This tool stores the structure of web pages in .form XML files.

Target Metamodel

The structure of these files are easy. Each file represents a Form element. A form contains components, which can contain also components in a child relationship. Components have properties, depending on the type of component. The metamodel for this structure looks like this:

Suppose you have the appropriate Ecore model representing this metamodel. We have created it using MagicDraw, exported the metamodel in UML2 format and used Eclipse's UML2 feature to convert this model into an Ecore model. This Ecore metamodel looks like this:

Tool Adapter

So far so good. Now things get interesting. We have a XML based input format and an Ecore metamodel. Now we want to instantiate the Ecore metamodel from XML data. We need the following:
  • A tool adapter class
  • A tool specific mapping configuration
  • Workflow configuration
The most effort is to provide the tool mapping, therefore we will treat this in the next paragraph.

Writing the Tool Adapter

First of all you'll need to configure plugin-dependencies to at least the following plugins:
  • org.openarchitectureware.core.emftools
  • org.openarchitectureware.classic.core
  • org.openarchitectureware.classic.xmiInstantiator
The adapter itself is easy. It needs to derive from org.openarchitectureware.core.frontends.xmi.toolsupport.AbstractToolAdapter. Since we want to instantiate Ecore we have to provide the EMFInstantiatorService to the adapter during initialization. This is the main difference to other Classic tool adapters! The adapter looks like follows:

package echo2.adapter;

import org.openarchitectureware.core.ConfigurationException;
import org.openarchitectureware.core.frontends.InstantiatorEnvironment;
import org.openarchitectureware.core.frontends.xmi.Constants;
import org.openarchitectureware.core.frontends.xmi.EMFInstantiatorService;
import org.openarchitectureware.core.frontends.xmi.InstantiatorServiceInterface;
import org.openarchitectureware.core.frontends.xmi.mapping.MappingContext.InstantiatorElement;
import org.openarchitectureware.core.frontends.xmi.toolsupport.AbstractToolAdapter;

public class EchoAdapter extends AbstractToolAdapter {
	@Override
	/**
	 * Set up for EMF instantiation
	 */
	public void init(InstantiatorEnvironment env) throws ConfigurationException {
		super.init(env);
		InstantiatorServiceInterface instService = new EMFInstantiatorService();
		instService.init(env);
		setInstantiatorService(instService);
	}

	/**
	 * Set 'name' attribute of current processed form file.
	 * @param element
	 */
	public void processForm (InstantiatorElement element) {
		// Instantiator sets this System property with the current processed file
		String modelFile = System.getProperty(Constants.DESIGNFILE);
		// extract the name from the file
		int start = modelFile.indexOf('/')>0 ? modelFile.lastIndexOf('/'+1) : 0;
		String formName = modelFile.substring(start, modelFile.lastIndexOf(".form"));
		// set property 'name' for the instantiator
		element.addProperty("name", formName);
	}
}
You may wonder why there is a processForm() method. The reason is that the name attribute of the Form element cannot be derived from the input model directly, since it is not contained within. But it can be derived from the current processed model file.

The tool adapter wíll try to call a prepare and a process method for each type to instantiate dynamically. In this method the instantiation information can be manipulated. In our case we add a "name" property to elements of type Form.

Workflow Configuration

[[[[
p;lt;?xml version="1.0" encoding="windows-1252"?>
<workflow>
	<bean id="Setup" class="org.openarchitectureware.emf.Setup">
		<EPackageFile value="metamodel/echo2.ecore"/>
	</bean>
	
	<cartridge file="org/openarchitectureware/workflow/oawclassic/classicstart.oaw">
		<log4jConfigFile value="log4j.properties"/>
	    <metaEnvironmentSlot value="me"/>
		<instantiatorEnvironmentSlot value="ie"/>
	</cartridge>

    <component class="org.openarchitectureware.core.frontends.xmi.workflow.XMIInstantiator">
		<instantiatorEnvironmentSlot value="ie"/>
		<modelFile value="DefaultForm.form, MessageDialog.form"/>
		<xmlMapFile value="echo2/adapter/echostudio2.xml"/>
		<metaMapFile value=""/>
		<toolAdapterClassname value="echo2.adapter.EchoAdapter"/>
		<outputSlot value="model"/>
	</component>
	
	<component id="generator" class="oaw.xpand2.Generator" skipOnErrors="true">
		<metaModel class="oaw.type.emf.EmfMetaModel">
			<metaModelFile value="metamodel/echo2.ecore"/>
		</metaModel>
		<expand value="Root::Root FOREACH model"/>
   		<outlet path="src-gen">
	       <postprocessor class="oaw.xpand2.output.JavaBeautifier"/>
	    </outlet>
	</component>
	
</workflow>
[[[[
st of all the workflow must be initialized. The Setup Bean will register the echo2.ecore within the Registry. This is needed later on by the EMFInstantiatorService, since it will look for the Echo2 package in the registry.

The classicstart.oaw cartridge needs to be included everytime you use oAW classic.

The XMIInstantiator component is the same like for UML tool adaption, but with different configuration:

  • The modelFile property is a comma separated list of XML files to process
  • The xmlMapFile property specified the tool specific mapping. We will come to this file right after.
  • The metaMapFile property is left empty, since it is only evaluated by UML adapters
  • The toolAdapterClassname specifies our tool adapter class from above.

Writing the Tool Mapping

In this chapter we will explain how to provide a tool mapping file suitable to map form elements to metaclasses. The tool mapping is a XML document that allows to map any XML information to classes. The EMFInstantiatorService allows to instantiate EMF model classes dynamically.

Mapping Header

Let's begin with the file header. The document must declare the following DTD: [[[[
!DOCTYPE MetaModel PUBLIC "-//openArchitectureWare//DTD openArchitectureWare XML2Meta 4.0//EN" 
                           "http://www.openarchitectureware.org/dtds/xml2meta_4_0.dtd">
[[[[
root element is MetaModel.

Next with RefSettings the attribute names are declared that will identify or reference elements. The mapping file format was designed to parse XMI, where this is important. We don't need it for this mapping, but it must be there anyway.

The ResultSet declaration specifies a Xpath expression that filters a part from the model document containing model information. This is also not of importance for our mapping here, so we select 'all'. Background: In XMI exports from UML tools huge parts of the document contain only layout information, which is irrelevant for instantiation. Then it is better to filter only th parts of the document that contain model information.

Mapping nodes to classes

For each element to map the mapping file contains one ModelElement node. The ModelElement node has the attributes name and id, which are both specified with the name of the element to map.

Now we want to map nodes with name form (from the input model) to the Form class from our Ecore model. Therefore we have to specify a Source of type Node with the name of the node to select, form. The Target is of type Class, which means that a class should be instaniated.

The target class name has now a format specific for instantiating EMF elements: The prefix is the NS URI of the Package element in the Ecore model. Then two colons follow to separate the classname from the NS URI. Also subpackages can be specified, also using "::" as a separator. See the next screenshot for using subpackges.

Attribute Filter

Each component in our input model is specified by a tag. The type of the component is specified by a type attribute. But we want to instantiate different element types for each component type. To achieve this we make use of filtering.

The screenshot shows that we want to map the ContentPane element type. A filter is defined that specifies that the type attribute of the selected node () must be nextapp.echo2.app.ContentPane.

Inheritance

Tool mappings support inheritance. This must not be the same inheritance hierarchy like the model classes, it is a separate one. In our example the type Component is a base class for all types of components. So it makes sense to specify all common things in a mapping for Component and all aspects specific to one type of element only for this element.

Element inheritance is specified by a container and in the derived element declaration by a container. Both directions are necessary. The element types are referenced by idref.

Mapping attributes

Next we want to map node attributes to attributes of our EMF classes. This is done by specifying mappings within the section of an element declaration. The following example shows how to map the resource-bundle attribute from the input model to the resourceBundle attribut of our target model.

Aggregations

In our example the Form element is a container for Component elements. The components are referenced by the EReference named component within Form.

To map this we need to specify a property for the Form element with a Source of type Aggregate and a Target of type Association. The Source node references Component to assure that only elements that are components are selected. The body of the Source node contains a Path to the aggregating element. Since this is the current element the path is here simply '.'.

Another example for this type of mapping is aggregating properties for components. In the input model properties are contained within a container.

Summary

We have demonstrated to use the tool adapter framework from openArchitectureWare's Classic module to instantiate a custom EMF metamodel from a specific XML document format. Basically this is the same as the instantiation of XMI models from UML tools, and the UML tool mappings look rather similar to the mapping demonstrated here. To learn more about tool mappings it is useful to study the UML tool mappings from the XmiInstantiator package. Further a draft of a Tool Mapping Reference documentation exists.

About the author

Karsten Thoms is a consultant from itemis. His main interest is on improving software development processes to be more efficient, faster and with more quality. He is convinced that model-driven software development is the best way to achieve this. In special he thinks that openArchitectureWare is the most flexible and powerful framework for realizing model-driven software projects. Therefore he is an active developer of openArchitectureWare. His main responsibilities are support of oAW's Classic module, online support, bug fixing, documentation, website administration. For itemis he does trainings, workshops and coaching on openArchitectureWare for their customers.