JavaBean template from
Root.xptThis example shows the usage of openArchitectureWare 4 with integration of an UML tool. In openArchitectureWare 4, we call this "Classic" style, as the underlying metamodel has to be the "Classic" UML metamodel that was introduced by oAW 3. For the example, Magic Draw 11.5 Community Edition is used, but the example can easily be adapted to any supported UML tool. It is strongly recommended to work through this tutorial with MagicDraw in order to minimize environmental problems!
Make sure that you installed the openArchitectureWare feature properly in your Eclipse environment.
openArchitectureWare depends on EMF, so check that you have installed it. If you need further information on oAW installation please look at http://www.openarchitectureware.org/staticpages/index.php/documentation.
Instead of working through this tutorial, you can also install the
packaged example by downloading the
oaw-samples-classic-uml-4.x.x package. It contains
one Eclipse project, which you have to import into your workspace. To make
the projects compile and run, you may have to define to use the
oAW-Classic Metamodel in the project
properties:
The purpose of this tutorial is to demonstrate the very simplest way to use oAW4 in combination with an (non EMF UML2 capable) UML tool to create code from a model that contains some classes. The project is really simple, so it is the right place to start when you are new to openArchitectureWare 4 and want to use UML tools like MagicDraw, Poseidon, Rational Rose etc.
In this example, we want to generate code from this model:
As a result, we want to create some JavaBean style classes which have properties with getter/setter methods.
Create a new Java Project called
oaw4.demo.classic.uml and select the option to create
seperate source and output folders.
Afterwards, select from the context menu → , since we want to define our dependencies via Eclipse Plug-In dependencies.
Alternatively, you could create a Plug-In project instead of these both steps.
In the new project, there is now a
META-INF/MANIFEST.MF. Open it, go to the Dependencies
page and add the following dependencies:
org.openarchitectureware.classic.umlMetamodel
: The classic UML metamodel classes
org.openarchitectureware.classic.core
: Framework classes for oAW classic
org.openarchitectureware.classic.workflow
: oAW classic workflow components and cartridges
org.openarchitectureware.classic.xmiInstantiator
: Parser component for UML tools
org.openarchitectureware.classic.libraries
: Required 3rd party libraries
org.openarchitectureware.core.xpand2
: The Xpand template engine
org.openarchitectureware.core.expressions
: oAW language Check for defining constraints
Create two folders model and
templates in the project root (not in
src!) and add this folders as classpath folders in
the project properties dialog, Libraries tab. By doing this, the model and
the templates can be found in the classpath of the project without placing
the files in the source folder.
This section explains how to create the model from scratch using MagicDraw 10/11. If you are using another UML tool, this is a guideline to create the model, but the tool can differ in some details. You can skip this section if you have MagicDraw, downloaded the sample project and use the model from the sample project.
Start your UML Tool and create the model from the screenshot above.
You have to create the Stereotypes Entity and
Key.
To define the model above follow these steps:
Entity
with base class
Class
.
Key
with base class
Property
.
DAO
with base class
Class
.
oaw4 / demo / classic / uml / entityentityAuthorEntityid of type
String. Please select for String the
datatype from your model, not from UML Standard
Profile! Assign the stereotype
Key for this attribute.name of
type String.Book
with stereotype
Entity
. The
Key
attribute is
isbn
of type
String
. The second attribute is the
title
.
Author is named
author, is navigable and the multiplicity
is 1..*.writtenBook with multiplicity
0..* and is navigable.Copy
of stereotype
EntityKey attribute is
inventoryNumber of type
String.location of type
String.Copy
to
BookBook
is named book, is navigable, with
multplicity 1.Copy is
unnamed, not navigable and multiplicity
0..*Library
of stereotype
EntityKey attribute is
id of type
String.String
attribute nameLibrary
to
CopyLibrary is named
owner, is navigable, the multiplicity is
1.Book
is named ownedBook, is navigable and the
multiplicity is 1..*.Save your model packed format in the model folder of your project
and give the file the name
AuthorBookExampleMD11.mdzip. The tool adapter will
automatically recognize that it is zipped and read the appropriate ZIP
entry.
Create a subfolder model/md11. From the
profiles directory of your MagicDraw installation
copy the UML_Standard_Profile.xml to there.
Models are instances of a Metamodel. In order to get openArchitectureWare to do something useful it needs to know the used metamodel. Using oAW "Classic" the metamodel is implemented by metaclasses. In UML, they are represented in the model by stereotypes.
The recently defined model already uses the stereotypes
DAO, Entity and
Key. Entities are some kind of business objects,
which have some attributes. They are represented in UML as classes.
Exactly one attribute is a special one: a Key
attribute. DAOs are classes which manage
Entities. We want to express this relationship by
using a dependency from DAO to
Entity in our model.
In UML, this metamodel looks like this:
We have to provide the metaclasses that make up our DSL. The base
metaclasses Class and
Attribute are provided by openArchitectureWare
within the package org.openarchitectureware.meta.uml
and its subpackages.
Create a package oaw4.demo.classic.uml.meta.
This package will contain our metaclasses which realize the UML profile
we want to use. As you can see from the model above we use the
Stereotypes Entity, Key
and DAO. For these Stereotypes we need
metaclasses.
Metaclasses are derived from UML metaclasses. oAW4 provides
implementation classes for UML metaclasses. For example, an
Entity is represented by classes, so, the right
metaclass to extend is Class, while
Keys are a specialization from
Attribute.
In the simplest case, you will only have to create the classes
Entity, Key and
DAO and derive them from the base metaclasses
Class and Attribute
defined in package
org.openarchitecture.meta.uml.classifier:
package oaw4.demo.classic.uml.meta;
public class Entity extends org.openarchitectureware.meta.uml.classifier.Class {
// nothing to do in the simplest case
}package oaw4.demo.classic.uml.meta;
public class Key extends org.openarchitectureware.meta.uml.classifier.Attribute {
}package oaw4.demo.classic.uml.meta;
public class DAO extends org.openarchitectureware.meta.uml.classifier.Class {
}By now, the generator will not know that the stereotype
<<Entity>> has to be mapped to the
metaclass oaw4.demo.classic.uml.meta.Entity. By
default these elements are mapped to their UML base classes, in case of
Entity this is
Class.
To map stereotypes to metaclasses a xml file has to be created, called the metamapping file.
Create the file metamappings.xml in your
source folder:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE MetaMap SYSTEM "http://www.openarchitectureware.org/dtds/metamap.dtd"> <MetaMap> <Mapping> <Map>Entity</Map> <To>oaw4.demo.classic.uml.meta.Entity</To> </Mapping> <Mapping> <Map>DAO</Map> <To>oaw4.demo.classic.uml.meta.DAO</To> </Mapping> <Mapping> <Map>Key</Map> <To>oaw4.demo.classic.uml.meta.Key</To> </Mapping> </MetaMap>
Later when running the generator you would like to see some output
messages. Therefore you have to define a
log4j.properties. Create this file in the source
folder:
# Set root logger level to INFO and its only appender to A1. log4j.rootLogger=INFO, A1 # A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender # A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %m%n
The next step is to create a workflow script. The workflow has to accomplish the following tasks:
For all these tasks, pre-defined workflow-scripts and components can be included in the workflow. The following workflow script does all these tasks and should fit for the first steps. Later on, you may need to customize the workflow script to integrate further components like model modifiers. Therefore, the script itself is not bundled as a cartridge.
<?xml version="1.0" encoding="UTF-8"?>
<workflow>
<property file="workflow.properties"/>
<cartridge file="org/openarchitectureware/workflow/oawclassic/classicstart.oaw">
<metaEnvironmentSlot value="me"/>
<instantiatorEnvironmentSlot value="ie"/>
</cartridge>
<component class="org.openarchitectureware.core.frontends.xmi.workflow.XMIInstantiator">
<instantiatorEnvironmentSlot value="ie"/>
<modelFile value="${model.xmi}"/>
<xmlMapFile value="${toolMappingFile}"/>
<metaMapFile value="${metaMapFile}"/>
<toolAdapterClassname value="${toolAdapterClassname}"/>
<moduleFile value="${moduleFile}"/>
<outputSlot value="model"/>
</component>
<cartridge file="org/openarchitectureware/workflow/oawclassic/classicinit.oaw">
<metaEnvironmentSlot value="me"/>
</cartridge>
<component id="dirCleaner"
class="org.openarchitectureware.workflow.common.DirectoryCleaner">
<directories value="${srcGenPath}"/>
</component>
<component id="generator" class="org.openarchitectureware.xpand2.Generator">
<metaModel class="org.openarchitectureware.type.impl.java.JavaMetaModel">
<typeStrategy
class="org.openarchitectureware.type.impl.oawclassic.OAWClassicStrategy"
convertPropertiesToLowerCase="false"/>
</metaModel>
<expand value="Root::Root FOREACH me.getElements('Model')"/>
<genPath value="${srcGenPath}/"/>
<srcPath value="${srcGenPath}/"/>
<beautifier class="org.openarchitectureware.xpand2.output.JavaBeautifier"/>
<beautifier class="org.openarchitectureware.xpand2.output.XmlBeautifier"/>
<fileEncoding value="ISO-8859-1"/>
</component>
<cartridge file="org/openarchitectureware/workflow/oawclassic/classicfinish.oaw">
<instantiatorEnvironmentSlot value="ie"/>
<dumpfile value="${dumpfile}"/>
</cartridge>
</workflow>
The workflow includes a properties file, in which the concrete
configuration is stored. Create the file
workflow.properties like this:
# Note: all paths must be found in the classpath! # the metamappings file metaMapFile = metamappings.xml # model.xmi: name of the XMI export # toolMappingFile: tool mapping file to use # toolAdapterClassname: tool adapter implementation # moduleFile: profile files # MagicDraw 10 model.xmi = AuthorBookExampleMD10.mdzip toolMappingFile = magicdraw_xmi21_all.xml toolAdapterClassname = org.openarchitectureware.core.frontends.xmi.toolsupport.uml.magicdraw.MagicDrawAdapter21 moduleFile = magicdraw/md10/UML_Standard_Profile.xml #model.xmi = AuthorBookExampleMD11.mdzip #toolMappingFile = magicdraw_xmi21_all.xml #toolAdapterClassname = org.openarchitectureware.core.frontends.xmi.toolsupport.uml.magicdraw.MagicDrawAdapter21 #moduleFile = magicdraw/md11/UML_Standard_Profile.xml # path to create the generated output to srcGenPath = src-gen # path where the dump file is created dumpfile = bin/dump
This configuration file is designed for use with MagicDraw.
Other UML tools can be easily configured by changing the properties
model.xmi,
toolAdapterClassname,
toolMappingFile and
moduleFile. The property
moduleFile specifies additional modules to load
and merge, which is currently only evaluated by the MagicDraw
adapter.
The existing tool adapter classes and mapping files can be found beneath the package
org/openarchitectureware/core/frontends/xmi/toolsupport/uml/<TOOL>
For example, to set up this project for Poseidon 4/5 the appropriate settings are:
toolAdapterClassname = org.openarchitectureware.core.frontends.xmi.toolsupport.uml.poseidon.PoseidonAdapter toolMappingFile = poseidon40_xmi12_all.xml
In the workflow the generator has been configured to start with the this definition:
<expand value="Root::Root FOREACH me.getElements('Model')"/>This means that the generator will look for a template file
Root.xpt in the classpath where a definition named
Root is found for all elements that are selected
by the expression me.getElements('Model'). In
case of UML models there exists solely one element of type
Model that will be selected.
Now, create the file Root.xpt in the templates folder. Remember: We have configured our Eclipse project that the folder templates is a classpath folder. You will see that the new file is recognized as an oAW template file, as it has a template file icon:

For the beginning the template will be simple:
«IMPORT org::openarchitectureware::core::meta::core» «DEFINE Root FOR Model» «ENDDEFINE»
This template contains only an empty definition for Model
elements. The namespace of the Model metaclass
must be known for the generator, so we import the corresponding package.
Otherwise, we would have to fully qualify Model.
As you could expect, this template does not really do anything
yet.
At this time, your project structure should look like this:

We have not defined anything useful in our template yet. However, we
now execute the workflow to prove that everything is right configured.
Execute the workflow by selecting workflow.oaw and
select Run As -> oAW Workflow from the context
menu. The output should be like this:
0 INFO ---------------------------------------------------------------------------------- 0 INFO openArchitectureWare v4.3 -- (c) 2005, 2008 openarchitectureware.org and contributors 0 INFO ---------------------------------------------------------------------------------- 0 INFO running workflow: C:/dev/ide/workspace/oaw-v4-projects/oaw4.demo.classic.uml/src/workflow.oaw 0 INFO 540 INFO Starting: workflow org/openarchitectureware/workflow/oawclassic/classicstart.oaw 540 INFO Starting: org.openarchitectureware.workflow.oawclassic.ClassicOAWSetup 561 INFO classic oAW environment is set up; instantiator environment in: ie, meta environment in me 561 INFO Starting: org.openarchitectureware.core.frontends.xmi.workflow.XMIInstantiator 561 INFO Loading XMI from: AuthorBookExampleMD10.mdzip using map: magicdraw_xmi21_all.xml and metamap: metamappings.xml 571 INFO Starting mapping instantiator ... 571 INFO 581 INFO Parsing metamap metamappings.xml ... 721 INFO Parsing of metamap took 0.14s 721 INFO 1692 INFO Parsing design AuthorBookExampleMD10.mdzip ... 1702 INFO Loading model... 2163 INFO Initializing XMI support for XMI version 2.1 2163 INFO Scanning for referenced modules... 2183 INFO Found reference to module 'UML_Standard_Profile.xml' 2193 INFO Model loaded in 0.491s 2193 INFO 2193 INFO Loading modules... 2984 INFO Scanning for referenced modules... 3064 INFO Modules loaded in 0.871s 3064 INFO 3074 INFO Merging... 3114 INFO Modules merged in 0.04s 3114 INFO 3234 INFO Parsed design in 1.542s 3234 INFO 3234 INFO Loading extensions... 3304 INFO Extensions loaded in 0.07s 3304 INFO 3304 INFO Loading design... 3314 WARN ignoring node 65973 (ownedMember): cannot coerce to ModelElement 3314 WARN ignoring node 66017 (ownedMember): cannot coerce to ModelElement 3314 WARN ignoring node 66061 (ownedMember): cannot coerce to ModelElement 3314 WARN ignoring node 66119 (ownedMember): cannot coerce to ModelElement 3314 WARN ignoring node 68759 (ownedMember): cannot coerce to ModelElement 3314 WARN ignoring node 70438 (ownedMember): cannot coerce to ModelElement 3324 WARN ignoring node 65945 (ownedMember): cannot coerce to ModelElement 3334 WARN ignoring node 65952 (ownedMember): cannot coerce to ModelElement 3334 WARN ignoring node 65959 (ownedMember): cannot coerce to ModelElement 3334 WARN ignoring node 65966 (ownedMember): cannot coerce to ModelElement 3405 INFO Design loaded in 0.101s 3405 INFO 3405 INFO Applying tool specific design modifications... 3415 INFO Design modified in 0.01s 3415 INFO 3415 INFO Instantiating metamodel... 3485 WARN <<Model>> [no Name@27940994]: ignoring value for nonexisting property Documentation = [Author:Karsten. Created:27.02.06 08:47. Title:. Comment:. ] 3525 INFO Metamodel instantiated in 0.1s 3525 INFO 3525 INFO Instantiated design in 2.954s 3525 INFO 3525 INFO MetaModel Summary 3525 INFO ----------------- 3525 INFO 3525 INFO 3x Association (org.openarchitectureware.meta.uml.classifier.Association) 3525 INFO 6x AssociationEnd (org.openarchitectureware.meta.uml.classifier.AssociationEnd) 3525 INFO 4x Attribute (org.openarchitectureware.meta.uml.classifier.Attribute) 3525 INFO 1x DAO (oaw4.demo.classic.uml.meta.DAO) 3525 INFO 4x Entity (oaw4.demo.classic.uml.meta.Entity) 3525 INFO 4x Key (oaw4.demo.classic.uml.meta.Key) 3525 INFO 1x Model (org.openarchitectureware.core.meta.core.Model) 3525 INFO 7x Package (org.openarchitectureware.meta.uml.classifier.Package) 3525 INFO 12x PrimitiveType (org.openarchitectureware.meta.uml.classifier.PrimitiveType) 3525 INFO 3525 INFO Starting: workflow org/openarchitectureware/workflow/oawclassic/classicinit.oaw 3525 INFO Starting: org.openarchitectureware.workflow.oawclassic.ModelInitializer 3525 INFO initializing model elements (calling initializeModelElements) 3535 INFO Starting: org.openarchitectureware.workflow.oawclassic.ModelChecker 3535 INFO checking model elements (calling checkConstraints) 3545 INFO Starting: org.openarchitectureware.check.CheckComponent 3765 INFO Starting: dirCleaner [org.openarchitectureware.workflow.common.DirectoryCleaner] 3765 INFO Cleaning C:\dev\ide\workspace\oaw-v4-projects\oaw4.demo.classic.uml\src-gen 3875 INFO Starting: generator [org.openarchitectureware.xpand2.Generator] 3915 INFO Starting: workflow org/openarchitectureware/workflow/oawclassic/classicfinish.oaw 3915 INFO Starting: org.openarchitectureware.workflow.oawclassic.MessageOutput 3915 INFO Starting: org.openarchitectureware.workflow.oawclassic.DumpWriter 3915 INFO writing dump to: bin/dump 3925 INFO workflow completed in 3385ms!
Congratulations! You have just set up the whole environment to get openArchitectureWare running. Now, let us do the interesting stuff!
Later on, we want to do something with the elements contained in our
model. A common pattern in the Root template when using UML models is to
loop through the model to find the elements that should be expanded. A
model is an instance of Namespace and all elements
directly contained by the model can be accessed through the association
OwnedElement. The owned elements can be of
various types: Classes, Packages, Datatypes and so on. Packages are also
instances of Namespace, so they also have a
OwnedElement association.
We use the feature to recursively resolve any element
contained in the model tree. Now extend the Root.xpt
template file:
«IMPORT org::openarchitectureware::core::meta::core» «IMPORT org::openarchitectureware::meta::uml::classifier» «DEFINE Root FOR Model» «EXPAND Root FOREACH OwnedElement» «ENDDEFINE» «DEFINE Root FOR Package» «EXPAND Root FOREACH OwnedElement» «ENDDEFINE» «DEFINE Root FOR Object»«ENDDEFINE»
Note, that we have added a new IMPORT statement,
because the metaclass Package is in package
org.openarchitectureware.meta.uml.classifier.
First, the definition Root FOR
Model will be called by the generator. This will
call the definition named Root for all elements,
that the Model instance contains. Look at the last
definition: This is a catcher for all elements that are found while
traversing through the tree that are not of any handled type.
When the model contains Package instances the
definition Root FOR Package will be called for
these instances. This is polymorphism at work! When the package contains
subpackages, the definition is called recursively.
As we want to create JavaBean style classes from the entities in our
model, we now want to create the template file for this. Create a new
folder java within the folder templates create the
file JavaBean.xpt there.
For a first step, this template will simply create a file with the name of the class.
«IMPORT org::openarchitectureware::meta::uml::classifier»
«DEFINE BeanClass FOR Class»
«FILE NameS+".java"»
public class «Name» {
}
«ENDFILE»
«ENDDEFINE»Note, that we have named the definition
BeanClass and it is defined for elements of type
Class. So, this template will not only fit for
elements of type Entity, but can be used for any
class.
In the FILE statement, we access the name of the
current element by property NameS. This is
specific to the oAW classic metamodel. The property
NameS returns the name of the element as String.
The property Name itself is of type
Object for backward compatibility. But in templates
we usually want the name as a String object.
By now, this template is not called. We have to extend the
Root.xpt template file. Add a definition
Root defined for Entity.
We also have to add a new IMPORT, so that oAW can
resolve Entity.
«IMPORT oaw4::demo::classic::uml::meta» ... «DEFINE Root FOR Entity» «EXPAND java::JavaBean::BeanClass» «ENDDEFINE»
As you remember, while looping through the model for each element in
the model tree a definition called Root is
called. Before adding this, we had a definition for Packages, Models, and
all other objects. Now, when evaluating elements of type
Entity this definition matches. In this definition,
we call the definition named BeanClass from the
template file JavaBean.xpt. As the template
JavaBean.xpt is defined in the package
java, we also have to qualifiy this namespace. As an
alternative, we could import the namespace java. The
definition is (implicitely) called for the current element, which is of
course an Entity.
Run the generator again (as a shortcut you could type Ctrl+F11). Now, your project should contain the folder
src-gen, which contains four files:
Author.java, Book.java,
Copy.java and
Library.java.
Open Author.java. It does only contain the
class definition, since the template was that simple.
public class Author {
}Now, we want to extend the JavaBean template to create instance variables, property getters and setters. Generating declarations for instance variables as well as their getter and setter methods is a very recurring task for attributes, so we want this in a central template file.
Create the file Attribute.xpt in folder
java.
«IMPORT org::openarchitectureware::meta::uml::classifier»
«DEFINE PropertyDeclaration FOR Attribute»
private «Type.NameS» «NameS»;
«ENDDEFINE»
«DEFINE Getter FOR Attribute»
public «Type.NameS» get«NameS.toFirstUpper()» () {
return this.«NameS»;
}
«ENDDEFINE»
«DEFINE Setter FOR Attribute»
public void set«NameS.toFirstUpper()» («Type.NameS» «NameS») {
this.«NameS» = «NameS»;
}
«ENDDEFINE»Of course, we also have to call these templates from our
JavaBean template. We want to call the templates
PropertyDeclaration,
Getter and Setter for
each attribute a class has. So, open your
JavaBean.xpt template and extend it like
follows:
«IMPORT org::openarchitectureware::meta::uml::classifier»
«DEFINE BeanClass FOR Class»
«FILE NameS+".java"»
public class «Name» {
«EXPAND Attribute::PropertyDeclaration FOREACH Attribute»
«EXPAND Attribute::Getter FOREACH Attribute»
«EXPAND Attribute::Setter FOREACH Attribute»
}
«ENDFILE»
«ENDDEFINE»Once again, run the generator. Now, your generated files have properties!
public class Author {
private String id;
private String name;
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}A very powerful feature of openArchitectureWare 4 are extensions. With extensions the Xpand template engine can be extended with functions without the need to modify the metamodel.[9] A very common use of extensions are the use of naming conventions, navigation, computation of package, path and filenames for artifacts etc.
openArchitectureWare extensions are declared in files ending with
.ext. The declaration of extension functions can be
by means of oAW expressions or by calling Java functions. The latter have
to be declared public and static.
We will not cover the syntax of expressions very deep, so if you are interested to get more information look at the reference core reference chapters Xtend and Expressions. Also, in the other tutorials you can see more examples for the usage of expressions.
For our example, we want to use extensions to introduce some naming conventions for instance variables, parameters, and the computation of the package and path for classes.
Now, create a file NamingConventions.ext in
the templates/java folder. In this extension we
declare some functions that are helpful for name conversions:[10]
import org::openarchitectureware::meta::uml; import org::openarchitectureware::meta::uml::classifier; String asParameter (ModelElement elem) : "p"+elem.NameS.toFirstUpper(); String asSetter (ModelElement elem) : "set"+elem.NameS.toFirstUpper(); String asGetter (ModelElement elem) : "get"+elem.NameS.toFirstUpper(); String asInstanceVar (ModelElement elem) : elem.NameS.toFirstLower();
In extension files, import statements for used types are necessary, too. The function declarations are single-lined and can use any expressions, calls of other extension functions included.
Open Attribute.xpt and make use of these
functions. In template files, the usage of extensions must be declared
by the «EXTENSION» keyword. The modified
template file will look like this:
«IMPORT org::openarchitectureware::meta::uml::classifier»
«EXTENSION java::NamingConventions»
«DEFINE PropertyDeclaration FOR Attribute»
private «Type.NameS» «asInstanceVar()»;
«ENDDEFINE»
«DEFINE Getter FOR Attribute»
public «Type.NameS» «asGetter()» () {
return this.«asInstanceVar()»;
}
«ENDDEFINE»
«DEFINE Setter FOR Attribute»
public void «asSetter()» («Type.NameS» «asParameter()») {
this.«asInstanceVar()» = «asParameter()»;
}
«ENDDEFINE»Run the generator and open Author.java. The
file looks almost the same, since the replacements for instance
variables, getter and setter method names are equivalent. The only
difference are the parameters for setter methods. They are now prefixed
with the character p.
public void setId(String pId) {
this.id = pId;
}More complex computations are often easier and more understandable
by means of the Java programming language. So, the
Xtend language allows to define functions by
referencing a Java function. The methods that should be called, must be
declared public static.
In our example, we miss the declaration of packages, and files are written into the root directory. We now want to declare some functions that help us to compute the full package name of classes, their corresponding path and the fully qualified name.
Create a new package
oaw4.demo.classic.uml.extend and a class
ClassUtil. In this class we define a function
getPackageName() that computes the full package
of a Class (remember that we operate on the class of the metamodel named
(org.openarchitectureware.meta.uml.classifier.)Class,
not java.lang.Class).
package oaw4.demo.classic.uml.extend;
import org.openarchitectureware.meta.uml.classifier.Class;
import org.openarchitectureware.meta.uml.classifier.Package;
public class ClassUtil {
public static String getPackageName (Class cls) {
String result = "";
for (Package pck=cls.Package(); pck!=null; pck=pck.SuperPackage()) {
result = pck.NameS() + (result.length()>0 ? "."+result : "");
}
return result;
}
}We declare this function in
NamingConventions.ext and two more functions that
use it. Add these function declarations:
String packageName (Class cls) :
JAVA oaw4.demo.classic.uml.extend.ClassUtil
.getPackageName(org.openarchitectureware.meta.uml.classifier.Class);
String packagePath (Class cls) :
packageName(cls).replaceAll("\\.", "/");
String fqn (Class cls) :
packageName(cls).length>0 ? packageName(cls)+"."+cls.NameS : cls.NameS;The additional functions are used to compute the path to files
based on a class and to get the full qualified name of a class. We want
to make use of these functions to declare the package, compute the
desired file path and to extend from the superclass, if a class has one
(in our example model this is not the case yet). If no superclass
exists, the Bean class should declare to implement the
Serializable interface.
Open the JavaBean.xpt template file and
modify it like follows:
«IMPORT org::openarchitectureware::meta::uml::classifier»
«IMPORT java»
«EXTENSION java::NamingConventions»
«DEFINE BeanClass FOR Class»
«FILE packagePath()+"/"+NameS+".java"»
package «packageName()»;
public class «Name»
«IF hasSuperClass»extends «SuperClass.fqn()» «ENDIF»
implements java.io.Serializable {
...Once again, run the generator and refresh your
src-gen folder. Now, the classes are generated in
the expected directory structure:

The generated classes now contain the correct
package statement:
package oaw4.demo.classic.uml.entity;
public class Author implements java.io.Serializable {
...
In the model, we have defined some associations of different kind.
In the next step, we evaluate these associations and create references,
accessor methods and modification methods. As you can see, an m:n
association between Author and
Book exists. There also exists an association from
Library to Copy with the
multiplicity 1:n. The association from Book to
Copy is not navigable. These associations must be
handled differently.
Ordinary associations in UML consist of three elements: Two association ends and one association containing them. From the view of a class, a referenced class is at the opposite end. Normally it must be considered whether the association, i.e. the opposite association end, to the referenced class is navigable.

Also, the templates for associations between classes are very
common, so we define them in a central template file
Association.xpt in the
templates/java folder.
We want to keep the usage of the association template for the
calling class template simple and hide this complex stuff about
association ends. So, we define simple entry points for
Class elements. We also make use of further
extensions to facilitate template development.
The extensions for functions used for associations are put into a
separate file. Create the file
templates/java/Associations.ext with this
content:
import org::openarchitectureware::meta::uml::classifier; extension java::NamingConventions; String fqn (AssociationEnd ae) : !ae.isMultiple ? ae.Class.fqn() : "java.util.Collection<"+ae.Class.fqn()+">"; String iterator (AssociationEnd ae) : "java.util.Iterator<"+ae.Class.fqn()+">";
As you can see, we will create Java 5 style collections for to-many associations. If you do not want to use generics, you could easily replace or modify the extension file and all associations will change to your style.
We see in this extension file, that other extensions can be
referenced using the extension keyword. In this
case, we need the function fqn() that was
defined for classes. For associations, another function
fqn() is defined, but now for
AssociationEnds. For to-one associations, the
fully qualified name of the associated class is returned, for to-many
references, we return a generic collection for that class. In the
template code, classes and association ends will both use the
fqn() function to print out the referenced
type.
Now, it is time to write the template for associations. Create the
file Association.xpt. Now fill in the content. We
will explain some statements right after.
«IMPORT org::openarchitectureware::meta::uml::classifier»
«EXTENSION java::NamingConventions»
«EXTENSION java::Associations»
«DEFINE ReferenceVariables FOR Class»
«FOREACH AssociationEnd.Opposite.select(ae|ae.isNavigable) AS ae»
private «ae.fqn()» «ae.asInstanceVar()»;
«ENDFOREACH»
«ENDDEFINE»
«DEFINE AccessorMethods FOR Class»
«EXPAND ToOneAccessorMethods FOREACH
AssociationEnd.Opposite.select(ae|!ae.isMultiple && ae.isNavigable)»
«EXPAND ToManyAccessorMethods FOREACH
AssociationEnd.Opposite.select(ae|ae.isMultiple && ae.isNavigable)»
«ENDDEFINE»
«DEFINE ToOneAccessorMethods FOR AssociationEnd»
public void «asSetter()» («Class.fqn()» «asParameter()») {
this.«asInstanceVar()» = «asParameter()»;
}
public «Class.fqn()» «asGetter()» () {
return this.«asInstanceVar()»;
}
«ENDDEFINE»
«DEFINE ToManyAccessorMethods FOR AssociationEnd»
public void add«NameS.toFirstUpper()» («Class.fqn()» «asParameter()») {
this.«asInstanceVar()».add(«asParameter()»);
}
public void remove«NameS.toFirstUpper()» («Class.fqn()» «asParameter()») {
this.«asInstanceVar()».remove(«asParameter()»);
}
public «iterator()» «asGetter()» () {
return this.«asInstanceVar()».iterator();
}
«ENDDEFINE»At first, we see that this template uses both extensions,
NamingConventions and
Associations.
Next, a template ReferenceVariables is
declared. The template makes use of a FOREACH
loop of a different kind than we saw before. One more specific thing
here is the statement
«FOREACH AssociationEnd.Opposite.select(ae|ae.isNavigable) ...
This returns all navigable opposite end of each association end a class has.
The body of the loop declares a reference variable for an
association. For both alternatives (to-one and to-many), the function
fqn() (the one defined for association ends) is
called to determine the right type. In our example, this should result
in the following both declarations:
// to-one association (Copy.java) private oaw4.demo.classic.uml.entity.Library owner; // to-many association (Author.java) private java.util.Collection<oaw4.demo.classic.uml.entity.Book> writtenBook;
Both types are now expressed by this statement – simple, isn't it?
private «ae.fqn()» «ae.asInstanceVar()»;
The next template definition is also interesting. The definition
AccessorMethods FOR Class dispatches to the
definitions ToOneAccessorMethods or
ToManyAccessorMethods, depending on the
cardinality of the association. To distinguish both types, we make use
of the select expression which is defined for expression. The
statement
«EXPAND ToOneAccessorMethods FOREACH AssociationEnd.Opposite.select(ae|!ae.isMultiple && ae.isNavigable)»
means that the definition
ToOneAccessorMethods should be expanded for
each opposite association end which is not to-many and navigable. So for
unnavigable associations no accessor method will be created.
The definitions for the accessor methods have nothing new, so we do not explain them in detail now.
Now, we want to use the new templates from the JavaBeans template
so that the classes get instance variables for referenced classes and
appropriate accessor methods. Modify the template file
JavaBeans.xpt by adding these two statements to the
class definition:
public class «Name» «IF hasSuperClass»extends «SuperClass.fqn()» «ENDIF»{
«EXPAND Attribute::PropertyDeclaration FOREACH Attribute»
«EXPAND Attribute::Getter FOREACH Attribute»
«EXPAND Attribute::Setter FOREACH Attribute»
«EXPAND Association::ReferenceVariables»
«EXPAND Association::AccessorMethods»
}Run the generator again. After successful generation, open
Book.java. This file should have the following
contents:
package oaw4.demo.classic.uml.entity;
public class Book implements java.io.Serializable {
private String isbn;
private String title;
private java.util.Collection<oaw4.demo.classic.uml.entity.Author> author;
public String getIsbn () {
return this.isbn;
}
public String getTitle () {
return this.title;
}
public void setIsbn (String pIsbn) {
this.isbn = pIsbn;
}
public void setTitle (String pTitle) {
this.title = pTitle;
}
public void addAuthor (oaw4.demo.classic.uml.entity.Author pAuthor) {
this.author.add(pAuthor);
}
public void removeAuthor (oaw4.demo.classic.uml.entity.Author pAuthor) {
this.author.remove(pAuthor);
}
public java.util.Iterator<oaw4.demo.classic.uml.entity.Author> getAuthor () {
return this.author.iterator();
}
}In this file, we have a to-many association named
author to entity Author. No
code exists for the association to the Copy
class, since this association is not navigable.
An example for to-one associations can be seen in the
Copy class:
public class Copy implements java.io.Serializable {
...
private oaw4.demo.classic.uml.entity.Library owner;
...
public void setOwner (oaw4.demo.classic.uml.entity.Library pOwner) {
this.owner = pOwner;
}
public oaw4.demo.classic.uml.entity.Library getOwner () {
return this.owner;
}
...
}The template code is really small, but now you could only have to
model classes and their associations and you get the right JavaBeans
code from the model. Our JavaBeans template is not specific for
entities, you could use it to generate JavaBeans code for just any
modelled class. But we only call this template for classes stereotyped
with <<Entity>> for now. You remember
that the model contains another class <<DAO>>
LibraryDAO for which no code is generated yet.
It is very important that a generator produces correct output. Therefore, the input information must be valid, i.e. the model must be consistent. To prove this, the metamodel is enriched by constraints that check the consistence of the model.
Constraints can be provided in three different ways:
checkConstraints()
method of the metaclasses.
The checkConstraints() method is
only available for metaclasses based on the "classic" metamodel.
It is not recommended to use this alternative. This alternative is
for backward compatibility; in oAW3 metaclasses usually used this
method to check model constraints.
oAW 4 has a new language named Check that can be used to check model constraints. In this example, we will focus on this method. You may want to read the chapter Check language in the core reference for more information.
We want to implement constraint checks by using the
Check language. The syntax for check files is
similar to those for Extensions, as it uses also the oAW Expressions
framework. Check files have the file extension .chk
and have be on the classpath in order to be found.
In our example, we want to implement the following constraint:
Constraint 1 must be fulfilled, otherwise an error message should be printed and the generation process should not be started. If constraint 2 is not fulfilled a warn message should be printed, but the generation process should not be stopped.
As the constraints that should be implemented are specific for
associations, the check file should be named
AssociationChecks.chk. Create this file in
templates/java.
import org::openarchitectureware::meta::uml::classifier; context AssociationEnd ERROR Class.NameS+"->"+Opposite.Class.NameS+": Navigable association ends must have a role name" : isNavigable ? !isUnnamed : true; context AssociationEnd WARNING Class.NameS+"->"+Opposite.Class.NameS+": Not navigable association ends should have no role name": isNavigable ? true : isUnnamed;
The syntax is rather simple. Both constraints should be checked
for the metaclass AssociationEnd, so the
appropriate namespace has to be imported. The expression following the
colon is the constraint. If it is not fulfilled, the message is printed.
When error messages are printed, the workflow will be
interrupted.
To execute the constraint checks, the generator workflow has to be
extended. The constraint check is configured by a workflow component
CheckComponent. Insert this code into
workflow.oaw, right between the cartridge
classicinit.oaw and the
dirCleaner component.
<component class="org.openarchitectureware.check.CheckComponent">
<metaModel class="org.openarchitectureware.type.impl.java.JavaMetaModel">
<typeStrategy class="org.openarchitectureware.type.impl.oawclassic.OAWClassicStrategy"
convertPropertiesToLowerCase="false"/>
</metaModel>
<checkFile value="java::AssociationChecks"/>
<expression value="me.getElements('ModelElement')"/>
<abortOnError value="true"/>
</component>Also, the constraint checker has to know which metamodel it should
use. It is the same configuration as for the Generator
component.[11]The checkFile property specifies the
qualified name of the check file.
Finally, the expression property defines for
which elements to constraints should be applied. In our case, we select
all elements in the
MetaEnvironment. However, for our special case it
would be satisfactory to select only all elements of type
AssociationEnd. For larger projects, it could be
of advantage to select the right subset of elements for improving
performance.
Start the generator. The generator should run without complaining, because all constraints are fulfilled in the example model for now.
Now open the model; we will change the model in a way that both constraints are not fulfilled.
First, edit the association between Book
and Author. Take the association end named
author and remove the name. Set the
navigable flag to false for the
opposite end named writtenBooks.
Save the model and re-run the generator. The generator now produces the expected messages and stops execution, because there is one constraint error and one warning.
4316 INFO Starting: org.openarchitectureware.check.CheckComponent 4667 ERROR Workflow interrupted. Reason: Errors during validation. 4667 WARN Book->Author: Not navigable association ends should have no role name [<<AssociationEnd>> writtenBook] 4667 ERROR Author->Book: Navigable association ends must have a role name [<<AssociationEnd>> ]
This tutorial is not finished. I plan to extend it in order to show more basic things about oAW usage. If you have any suggestions let me know them. Some features that will be handled in the future are:
[9] With openArchitectureWare3 it was needed to code functionality of the metamodel in metaclasses
[10] The functions in the Extension file could be defined for type
Attribute instead of
ModelElement, but we want to use these
functions for other types later on, too.
[11] It is possible to declare the used metamodel once and reference it the second time.