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 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.check: 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 project's classpath 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}"/>
</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 -- (c) 2005, 2006 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's 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 new 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.
TODO: Make correct references to other chapters! We will not cover the syntax of expressions very deep, so if you are interested to get more information look at the reference manuals r25_extendReference.pdf and r10_expressionsReference.pdf. 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 be 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 in 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 metamodel's class 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 super class,
if a class has one (in our example model this is not the case yet). If no super class 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 there exists a m:n association between Author and Book.
There 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 seperate 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 don't 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 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 specifix 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.
oAW4 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 reference manual of the Check language r30_checkReference.pdf for more information. TODO: Reference to Check chapter
The example t35_oclExample.pdf shows how to integrate an OCL library for model validation. This is out of scope for this example. TODO: Reference to OCL Adapter
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 must be found on the classpath.
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 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