modTransf tutorial

Introduction

This document is a tutorial which objective is to teach how to use modTransf, Cedric Dumoulin's MDA tool. To read this document you need to have some basic knowledge about: You should also not be afraid of TLA (Three Letter Acronym) since this document is full of those :)

To use modTransf you will need:

And for this tutorial you will need: You will also need a tool to write your models, unless you want to write everything by hand (which is a bad idea IMHO). There are lots of programs that can help you, most are proprietary (Enterprise Architect, ...), some are free software (Umbrello, ...). The main point is that the tool should be able to export diagrams to XMI 1.1 or 1.2, with UML 1.4, and thus some programs (Rational Rose, ...) should be avoided. In this tutorial we will be using Poseidon for UML from Gentleware.

Our objective in this tutorial will be to transform a graph described in UML into our own metamodel while introducing modTransf concepts.

General concepts

Metamodel

A metamodel is simply a model of a model. But what for? This is useful when you want your models to target different platforms. That is the case in the MDA approach, where platform-indepent models (PIM) are transformed into various platform-specific models (PSM).

Here is the example of a graph metamodel, we will be using it though our tutorial.

Our graph metamodel
Here are two examples of graph models which respect our metamodel. The following will be used for our example.

UML profile

An UML profile is simply an extension to UML with additional semantic thanks to some elements. The most important is probably the stereotype, and especially stereotypes that applies to classes. The tagged values are attributes of the metaclasses that are not already present in UML classes. In our example we will use the following :

Some programs provide means to describe a profile and work with it. For instance Enterprise Architect allows one to create a profile and put it inside a toolbox. Since our example is rather simple, we will not be using such a program; we will simply apply the stereotypes by hand.

MOF and XMI

MOF (Meta-Object Facility) is a meta-language, which means that it is a language to describe languages, including UML. MOF looks like a simplified UML. The nice thing with this is that it is simple to translate an UML model into a MOF metamodel. We will do this later using uml2mof within modTransf.

XMI is an XML DTD to save MOF-based metamodels. One consequence is that UML models can be saved in XMI format. The main idea of XMI is to put an identifier to every element (xmi.id attribute) which allows to put references to elements (xmi.idref attribute).

XMI is supposed to make it easy share models between applications. Still, there are so many possible combinations of XMI (1.0, 1.1, 1.2) and UML (1.1, 1.3, 1.4, 2.0) that sometimes importing a model does not work. When working with modTransf you should stick to XMI 1.2 and UML 1.4.

ModTransf targets

Model transformation

At first, the transformation can seem to be tricky. However we will se below that it can be simpler. Basically, on needs the following to make a transformation:

With all this modTransf will generate the output model. It can also write a log file if you ask for this in the transformation rules.

Generic transformation

In our case, we will use a MOF-based metamodel. The helper already exists and is based on MDR. MDR is an implementation of JMI, a specification for accessing metadata of MOF-based metamodels. To make it short, we don't have to write any helper, we can concentrate on writting the metamodels and the transformation rules.

A metamodel for UML is provided with modTransf. This will greatly help us since we will only have to write the output metamodel.

Our transformation

UML to MOF

One important target is uml2mof. It will transform an UML file describing a metamodel into a MOF file.
UML to MOF transformation

Our transformation

Files description

Unzip the transfExample. It creates a transfExample directory with an examples and lib subdirectories. The latter contains modTransf and other important files. We will be working inside the first one. Here are the important files:

Through our tutorial we will:

Graph metamodel

Since UML and MOF look very similar, we describe the graph metamodel with an UML tool and then turn it into a MOF file with uml2mof. Note that the metamodel is defined inside a package with the <<metamodel>> stereotype.
Graph metamodel described in UML

The metamodel must be exported to XMI 1.2 without any diagram data. We save it as model/graph-metamodel-uml.xmi.

The next step is to transform the UML into MOF using uml2mof. We do this by editing build.xml and creating a new target (copy-paste of uml2mof one):

<target name="graph-uml2mof" depends="compile.examples"
        description="Generate mof from uml">
	<java classname="org.netbeans.lib.jmi.uml2mof.Main" fork="yes">
		<classpath path="."/>
		<classpath refid="run.example.classpath"/>
		<classpath refid="mdr.metamodels"/>
		<arg line="model/graph-metamodel-uml.xmi"/>
		<arg line="model/graph-metamodel-mof.xmi"/>
	</java>
</target>

We run this target with ant:

ant graph-uml2mof
This should generate the mof file.

When this is done, we have to add the metamodel to the list in metamodel-config.xml:

<metamodel publicId="graph"
           classname="modTransf.model.factory.MDRModelFactory"
           xmiFile="model/graph-metamodel-mof.xmi"
           mainExtent="Graph"
           engineAdaptor="modTransf.model.jmi.JmiModelHelper"
           />
This states that the metamodel helper is JmiModelHelper and that the name of the outermost package is Graph. The publicId is the name of the metamodel as it will be referenced by from build.xml.

Input model

The input model is rather simple. We simply draw a <<start>> node and two <<node>> nodes, and we add the correct associations as pictured below. Please note that this complies with the graph metamodel. We save this file as model/graph-model-uml.xmi.

Transformation rules

Now that we have an input model, its metamodel (which is UML) and an output metamodel, we only lack the transformation rules.

The transformation is done by manipulating concepts, and properties inside the concepts.

Here are the transformation rules to be put inside rules/uml2graph.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE transformation PUBLIC "-//Model Transformation XML Rules 3.0//EN"
"http://www.lifl.fr/~dumoulin/mdaTransf/resources/modtransf-rules_3_0.dtd">
<transformation models="uml14,graph" defaultTargets="graph">
<ruleSet>

<topRule name="start">
<domain model="uml14" varName="c1" type="Core.Class"> 
    <primitiveProperty name="name" varName="n" type="String"/>
    <collProperty name="associatedElements" varName="nodesSrc"/>
    <collProperty name="stereotypes" varName="stereotypes"/>
    <enterGuards>
            <script script="stereotypes.contains('start')" language="groovy"/>
    </enterGuards>
</domain>
<domain model="graph" varName="c2" type="Start">
    <primitiveProperty name="name" varName="n" type="String"/>
    <collProperty name="next" varName="nodesDst"/>
</domain>
<actions>
    <call rule="node">
            <arg expr="nodesSrc"/>
            <arg expr="nodesDst"/>
    </call>
</actions>
</topRule>
    
<rule name="node">
<domain model="uml14" varName="c1" type="Core.Class">
    <primitiveProperty name="name" varName="n" type="String"/>
    <collProperty name="stereotypes" varName="stereotypes"/>
    <enterGuards>
            <script script="stereotypes.contains('node')" language="groovy"/>
    </enterGuards>
</domain>
<domain model="graph" varName="c2" type="Node">
    <primitiveProperty name="name" varName="n" type="String"/>
</domain>
</rule>

</ruleSet>
</transformation>

The file is divided in a set of rules. Each one is responsible for transforming one concept. In our example we have two concepts (start and node) and therefore we have two rules.

Let us have a closer look at the second rule. The <domain> tags allow to select the concepts. The following means to select UML classes with the <<node>> stereotype. The value of the name attribute is stored inside a variable named n.

<domain model="uml14" varName="c1" type="Core.Class">
    <primitiveProperty name="name" varName="n" type="String"/>
    <collProperty name="stereotypes" varName="stereotypes"/>
    <enterGuards>
            <script script="stereotypes.contains('node')" language="groovy"/>
    </enterGuards>
</domain>

Likewise, the following means to select Node objects and put the name in an n variable.

<domain model="graph" varName="c2" type="Node">
    <primitiveProperty name="name" varName="n" type="String"/>
</domain>

When the same variable name is used for input and output concepts, the attribute value is to be copied from the input to the output concept. In our example, this is how the name from the UML model will be copied to the graph model.

Running modTransf

In the build.xml file we add a new target that will run the transformation.
<target name="graph" depends="compile.examples"
            description="Transform an UML file into a graph">
        <java classname="modTransf.example.engine.Transform" fork="yes">
            <classpath path="."/>
            <classpath refid="run.example.classpath"/>
            <arg line="-mmFile metamodel-config.xml"/>
            <arg line="-src model/graph-model-uml.xmi uml14"/>
            <arg line="-dst model/graph-model.xmi graph"/>
            <arg line="-r rules/uml2graph.xml"/>
        </java>
</target>
First we do a clean-up. Run this command whenever you change a metamodel. This is beacause MDR uses some cache files that depend on the metamodels.
ant clean

Finally we can launch the transformation:

ant graph

This is the result we get (model/graph-model.xmi):

<?xml version = '1.0' encoding = 'ISO-8859-1' ?>
<XMI xmi.version = '1.2' timestamp = 'Wed Jun 22 16:50:54 CEST 2005'>
  <XMI.header>
    <XMI.documentation>
      <XMI.exporter>Netbeans XMI Writer</XMI.exporter>
      <XMI.exporterVersion>1.0</XMI.exporterVersion>
    </XMI.documentation>
  </XMI.header>
  <XMI.content>
    <Graph.Start xmi.id = 'a1' name = 'Node1'>
      <Graph.Node.Next>
        <Graph.Node xmi.idref = 'a2'/>
        <Graph.Node xmi.idref = 'a3'/>
      </Graph.Node.Next>
    </Graph.Start>
    <Graph.Node xmi.id = 'a2' name = 'Node2'/>
    <Graph.Node xmi.id = 'a3' name = 'Node3'/>
  </XMI.content>
</XMI>
As expected we have one Start object and two Node objects. The Next collection of links is stored within the Start object with references to the Node objects.

Somme errors

Here are some errors you may run into.

uml2mof related problems

[java] [org.netbeans.lib.uml2mof.Logger] Ignoring instance of org.omg.uml.foundation.core.Method$Impl; container instance of javax.jmi.model.MofClass$Impl
This is just a warning that might be printed when running uml2mof. You get this message when there are some unexpected elements within your model. In this case, a method has been found inside a class, but MOF does not allow methods (they are not in the standard). The message indicates that the method has been ignored. It is safe to go on, the build should not fail because of this.

Some other elements which may generate such warnings are:

[java] *********** Exception occurred ************ at Tue Jun 14 10:44:54 CEST 2005
[java] java.lang.NullPointerException
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.getName(Transformer.java:626)
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.transformAssoc(Transformer.java:336)
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.transform(Transformer.java:434)
This error message is printed by uml2mof when an association has no name. So the solution is simply to give it a name.
[java] *********** Exception occurred ************ at Tue Jun 14 16:25:13 CEST 2005
[java] java.util.NoSuchElementException
[java] at java.util.AbstractList$Itr.next(AbstractList.java:427)
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.resolveFQNs(Transformer.java:165)
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.transform(Transformer.java:129)
[java] at org.netbeans.lib.jmi.uml2mof.Transformer.execute(Transformer.java:117)
[java] at org.netbeans.lib.jmi.uml2mof.Main.main(Main.java:57)
You might get this message when using <<Reference>> stereotyped attributes. I suggest that you take away this attribute and that you use implicit references instead, by removing the tagged value org.omg.uml2mof.hasImplicitReferences='false' from the packages.

Transformation problems

Do not forget to run ant clean whenever you change a metamodel file.

[java] [ERROR] PropertyUtils - Method invocation failed. java.lang.IllegalArgumentException: argument type mismatch
[java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[java] at java.lang.reflect.Method.invoke(Method.java:585)
[java] at org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(PropertyUtilsBean.java:1773)
[java] at org.apache.commons.beanutils.PropertyUtilsBean.setSimpleProperty(PropertyUtilsBean.java:1759)
[java] at org.apache.commons.beanutils.PropertyUtilsBean.setNestedProperty(PropertyUtilsBean.java:1648)
[java] at org.apache.commons.beanutils.PropertyUtilsBean.setProperty(PropertyUtilsBean.java:1677)
[java] at org.apache.commons.beanutils.PropertyUtils.setProperty(PropertyUtils.java:559)

You might get this error when you have errors in the types definition. Your types must be defined as data types. In Poseidon, a <<dataType>> stereotyped class will not do, you have to use the appropriate icon.

Useful links