[Seminarthemen WS08/09] [ < ] [ > ] [Übersicht]


4 Beispiel – Stammdatenp&64258;ege mit openArchitectureWare

Nachdem in den vorhergehenden Kapiteln bereits die modellgetriebene Softwareentwicklung im Allgemeinen sowie die Nutzung des Frameworks openArchitectureWare im Besonderen vorgestellt wurde, soll nun eine Beispielanwendung mit Hilfe von openArchitectureWare umgesetzt werden. Nach der Identi&64257;zierung der zu erzeugenden Artefakte in den ersten beiden Abschnitten die wird anschließend die Modellierung sowie die Entwicklung von geeigneten Generatoren beschrieben. Abschließend werden mögliche Weiterentwicklungen des Beispiels vorgestellt.


Zweck und Ziel des Beispiels

Zweck dieses Beispiels ist die Demonstration der prinzipiellen Vorgehensweise und des Zusammenspiels der einzelnen Framework-Komponenten bei der Entwicklung mit openArchitectureWare. Ziel des Beispiels ist die Erstellung einer Stammdatenp&64258;ege für eine Seminarverwaltung, deren vereinfachte Datenstruktur in Abbildung 12 angegeben ist. In der Seminarverwaltung sind Personen gespeichert, von denen persönliche Angaben wie Vorname, Nachname, Geburtsdatum sowie das Geschlecht erfasst sind. Einer Person ist jeweils eine Adresse zugeordnet. Des Weiteren werden Seminare verwaltet, die eine Namen haben und optional als Kooperation durchgeführt werden. Einem Seminar können beliebig viele Dozenten zugeordnet werden.


PIC

Abbildung 12: Beispieldatenmodell Seminarverwaltung

Eine Stammdatenp&64258;ege zeichnet sich in der Regel dadurch aus, dass ihre Struktur weitgehend von den zu bearbeitenden Daten und damit von dem zugrundeliegenden Datenmodell bestimmt werden. Spezielles Verhalten sowie besondere fachliche Logik werden hingegen nur selten benötigt. Dadurch eignet sich ein modellgetriebenes Vorgehen insbesondere aus den folgenden Gründen:

Der Einsatz von Modellen dient folglich der Abstraktion von der fachlichen Domäne des zugrundeliegenden Datenmodells. Hinsichtlich der aus den Modellen zu erzeugenden Funktionalität werden die folgenden Anforderungen an die Anwendung gestellt:

Die Implementierung des Beispiels basiert auf einer Stammdatenp&64258;ege von Peemöller [Pee07], die im folgenden Abschnitt beschrieben wird. Diese Anwendung wurde insbesondere deshalb ausgewählt, weil sie das oben genannte Standardverhalten vorgibt. Dies führt dazu, dass der Fokus zunächst auf das Datenmodell gelegt werden kann und die Anwendung ohne manuelle Implementierung lau&64256;ähig ist. Die Realisierung von abweichendem Verhalten wird in diesem Beispiel nicht adressiert, jedoch in den möglichen Weiterentwicklungen aufgegri&64256;en.


Modellgesteuerte Stammdatenp&64258;ege

Die bestehende Anwendung zur Stammdatenp&64258;ege ist als modellgesteuerte Anwendung realisiert, in der von dem fachlich umzusetzenden Datenmodell abstrahiert wird. So sind diejenigen Softwareanteile, die unabhängig von den zu bearbeitenden Daten sind, bereits vollständig implementiert. Dies betri&64256;t beispielsweise die Kon&64257;guration der verwendeten Frameworks sowie das grundlegende Architekturgerüst. Die fachlichen Anteile hingegen werden durch Modelle beschrieben und zur Laufzeit von der Anwendung interpretiert. Somit wird das Verhalten der Anwendung zur Laufzeit von den interpretierten Modellen gesteuert, was Teurich-Wagner [TW04] folgend als modellgesteuert bezeichnet werden soll.

Architektur Die Anwendung ist als klassische 3-Schichten-Architektur realisiert (vgl. Abbildung 13):


PIC

Abbildung 13: 3-Schichten-Architektur der Stammdatenp&64258;ege

Modellsteuerung Um die Fachlichkeit der resultierenden Anwendung zu beschreiben, werden vier unterschiedliche Modelltypen verwendet (vgl. Abbildung 14). Sämtliche Modelle sind als XML-Dokumente realisiert und werden zur Laufzeit eingelesen und verarbeitet.


PIC

Abbildung 14: Modelle in der Stammdatenp&64258;ege

Somit sind für eine vollständige fachliche Parametrierung der Anwendung die folgenden vier XML-Modelle zu generieren:

Die in den Anforderungen genannte Möglichkeit, eigene Aufzählungstypen zu de&64257;nieren, ist in der Anwendung nicht über Modelle möglich. Hierzu ist zusätzlich eine Java-Klasse zu generieren, die eine entsprechende Zuordnung auf Hibernate-spezi&64257;sche Persistenzaspekte realisiert.


Umsetzung mit openArchitectureWare

4.3.1 Entwicklung des Metamodells

Nachdem in dem vorangegangenen Abschnitt die Artefakte analysiert wurden, die später zu generieren sein werden, folgt nun die Entwicklung des Metamodells. Das Metamodell soll es erlauben, die folgenden Bestandteile zu modellieren:

Für die spätere Modellierung soll eine textuelle DSL verwendet werden, da damit eine schnelle Modellerstellung gewährleistet ist, bei der zusätzlich die Möglichkeit des Kopierens ähnlicher Modellteile möglich ist.

In Listing 26 ist ein Xtext-Modell der Sprache SDDSL angegeben, die die oben genannten Anforderungen erfüllt. Die Sprachelemente leiten sich direkt aus den Anforderungen ab und sind somit weitgehend selbsterklärend. Für die Vorgabe von Datentypen für einfache Attribute wurde in den Zeilen 23–28 ein Aufzählungstyp de&64257;niert. Da einfache und selbstde&64257;nierte Datentypen von Attributen teilweise unterschiedlich behandelt werden müssen, werden diese durch unterschiedliche Klassen repräsentiert.

 
1// Root-Element 
2Model: (types+=Type)*; 
3 
4// Mögliche Typen des Modells 
5Type: EnumType | Entity; 
6 
7// Selbstdefinierter Aufzählungstyp 
8EnumType : 
9    "enum" name=ID "{" 
10    (values+=Value) ("," values+=Value)* 
11  "}"; 
12 
13// Einzelner Aufzählungswert 
14Value : name=ID "(" description=STRING ")"; 
15 
16// Entitätstyp 
17Entity: 
18  "entity" name=ID "{" 
19    (fields+=Field)+ 
20  "}"; 
21 
22// Vordefinierte Datentypen 
23Enum PredefinedType : 
24  big_decimal="BigDecimal"|  boolean="Boolean"       | 
25  byte="Byte"             |  character="Character"   | 
26  date="Date"             |  double="Double"         | 
27  float="Float"           |  integer="Integer"       | 
28  long="Long"             |  string="String"         ; 
29 
30// Feld eines Entitätstyps: Attributtyp oder Relationshiptyp 
31Field: Attribute |  Relationship; 
32 
33// Attribute 
34Attribute : NormalAttribute | EnumAttribute; 
35NormalAttribute: type=PredefinedType name=ID ";"; 
36EnumAttribute : type=[EnumType] name=ID ";"; 
37 
38// Relationships 
39Relationship: type=[Entity] (toMany?="[]")? name=ID "<->" backRel=[Relationship] ";";
Listing 26: Xtext-Modell für das Datenmetamodell

Somit sind die abstrakte und konkrete Syntax des Metamodells spezi&64257;ziert. Durch die Verwendung von Kreuzreferenzen (eckige Klammern in den Zeilen 36 und 39) wird zudem sichergestellt, dass die referenzierten Aufzählungs-, Entitäts- oder Beziehungstypen existieren. Diese Bedingungen bilden bereits einen Teil der zu spezi&64257;zierenden Kontextbedingungen. Des Weiteren sind noch folgende Bedingungen zu erfüllen, um ein in der Anwendung valides Modell zu erhalten:

Zur Überprüfung dieser Kontextbedingungen wird die Sprache Check verwendet. Listing 27 de&64257;niert die oben angegebenen Kontextbedingungen als Check-Constraints.

 
1import sddsl; 
2extension fhw::sd::Extensions; 
3 
4/* Eindeutiger Name von Aufzählungs-/ Entitätstypen */ 
5context Type ERROR "Name of Type must be unique : " + this.name : 
6  allElements().typeSelect(Type).select(e|e.name == this.name).size == 1; 
7 
8/* Eindeutige Elemente eines Aufzählungstyps */ 
9context Value ERROR "EnumType values must be unique : " + this.name : 
10  allElements().typeSelect(EnumType).select(e|e.values. contains(this)).values.select(e|e.name == this.name).size == 1; 
11 
12/* Eindeutiger Name von Eigenschaften */ 
13context Field ERROR "Name of Field of an Entity must be unique : " + this.name : 
14  allElements().typeSelect(Entity).select(e|e.fields. contains(this)).fields.select(e|e.name == this.name).size == 1; 
15 
16/* Inverse Relationships muss richtigen Entitätstyp referenzieren */ 
17context Relationship ERROR "Inverse Relationship " + this.backRel.name + " refers to a different entity " + this.backRel.type.name + "" : 
18  this.backRel.type.fields.contains(this); 
19 
20/* Inverse Relationship muss this als inverse Relationship haben */ 
21context Relationship ERROR "Inverse Relationship " + this.backRel.name + " refers to a different inverse Relationship " + this.backRel.backRel.name + "" : 
22  this.backRel.backRel == this;
Listing 27: Check-Constraints für das Datenmetamodell

Nachdem nun das Metamodell als Xtext-Modell mit Check-Constraints spezi&64257;ziert ist, kann durch das openArchitectureWare-Framework ein entsprechender Editor generiert werden, der die Sprache SDDSL unterstützt. Abbildung 15 zeigt den erzeugten Editor bei der Bearbeitung eines Beispielmodells. Hervorzuheben ist die zur Verfügung gestellte Funktionalität wie Syntax-Highlighting (Schlüsselwörter wie enum), Code Completion (Vorschlagssystem; hier entity, enum) und die Überprüfung von Kontextbedingungen (Problems-View). Während der erste Fehler durch die Verwendung von Kreuzreferenzen gefunden wird, entsprechen die restlichen vier Fehler Bedingungen, die soeben mit Hilfe von Check formuliert wurden.


PIC

Abbildung 15: Generierter Editor für die Sprache SDDSL

4.3.2 Entwicklung des Generators

Für die Entwicklung des Generators kommen die Template-Sprache Xpand für die Codeerzeugung (Javaklassen sowie XML-Dokumente) sowie Xtend für eine Modellmodi&64257;kation zum Einsatz. Aus dem Modell sind die folgenden Artefakte zu generieren:

Einen Überblick über die verwendeten Templates bietet Abbildung 16. Die einzelnen Templates traversieren das vom Work&64258;ow instanziierte Modell und erzeugen entsprechend der Expansionsregeln spezi&64257;schen Code.


PIC
Abbildung 16: Work&64258;ow-Struktur

Der Aufruf der Templates erfolgt dabei von oben nach unten. Nach dem Aufruf des Templates „DialogDeclaration.xpt“ wird zudem eine Modellmodi&64257;kation durchgeführt. An dieser Stelle wird in dem Modell zu jedem Entitätstyp ein technisches Attribut „id“ hinzugefügt (vgl. Listing 28), um dieses in der Ober&64258;äche anzeigen zu können. Dieses Attribut ist in den Hibernate-Mappings bereits automatisch als Schlüsselattribut vorgesehen.

 
1import sddsl; 
2 
3transform(Model this) : 
4  types.typeSelect(Entity).addIdAttribute(); 
5 
6addIdAttribute(Entity this) : 
7  fields.add(idAttribute(this)); 
8 
9create NormalAttribute idAttribute(Entity e) : 
10  setName("id") -> setType(PredefinedType::integer);
Listing 28: Modell-Modi&64257;kation: Hinzufügen eines ID-Attributs

Anschließend wird die Ober&64258;äche über das Template „DialogDe&64257;nition.xpt“ erzeugt, das auf fünf Subtemplates aufgeteilt ist. Die Erzeugung von String-Identi&64257;katoren, die von den Subtemplates gemeinsam genutzt werden, ist in die Extension „DialogHelper.ext“ ausgegliedert.

Aus der Generatoraufteilung wird ersichtlich, wie einzelne Domänen durch das modellgetriebene Vorgehen getrennt werden können:

Als Beispiel für eine technische Domäne ist in Listing 29 ein Teil des Templates für die Erzeugung der Hibernate-Mappings angegeben. Die bei der Metamodellierung eingeführte Unterscheidung von Attributen in NormalAttribute und EnumAttribute erlaubt durch parametrische Polymorphie eine saubere Trennung der jeweiligen Subtemplates.

 
1« IMPORT sddsl » 
2« EXTENSION fhw::sd::hbm::entityHelper » 
3 
4« DEFINE Root FOR Model » 
5  « EXPAND HbmMappingFile FOREACH types.typeSelect(Entity) » 
6« ENDDEFINE » 
7 
8« DEFINE HbmMappingFile FOR Entity » 
9« FILE name+".hbm.xml" »<?xml version="1.0" encoding="UTF-8"?> 
10<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
11<hibernate-mapping> 
12  <class entity-name="« name »" table="« tableName() »"> 
13    <id name="id" column="« getId() »" type="integer"> 
14      <generator class="increment" /> 
15    </id> 
16    « EXPAND Attribute FOREACH fields.typeSelect(Attribute) » 
17    « EXPAND Relationship(this) FOREACH fields.typeSelect(Relationship) » 
18  </class> 
19</hibernate-mapping> 
20« ENDFILE » 
21« ENDDEFINE » 
22 
23« DEFINE Attribute FOR NormalAttribute » 
24    <property column="« name.toUpperCase() »" name="« name »" type="« type.toString() »"/> 
25« ENDDEFINE » 
26 
27« DEFINE Attribute FOR EnumAttribute » 
28    <property name="« name »"> 
29      <column name="« name.toUpperCase() »" default="« type.values.first().name.toUpperCase() »" /> 
30      <type name="fhw.sd.persistence.type.enums.EnumUserType"> 
31        <param name="class">fhw.sd.common.type.enums.« name.toFirstUpper() »Typ</param> 
32      </type> 
33    </property> 
34« ENDDEFINE »
Listing 29: Template zur Erzeugung der Hibernate-Mappings (Ausschnitt)

Auf eine detaillierte Vorstellung der weiteren Templates soll an dieser Stelle verzichtet werden, da diese jeweils spezi&64257;sche technische Aspekte behandeln und dies für das grundsätzliche Verständnis nicht essentiell ist. Der interessierte Leser sei an dieser Stelle auf die entsprechende Literatur ([Pee07], [Red07], [sof07]) verwiesen.

4.3.3 Modellierung der Seminarverwaltung

Mit der De&64257;nition der domänenspezi&64257;schen Sprache SDDSL und der Erstellung geeigneter Generatoren kann nun eine Stammdatenp&64258;ege zur Seminarverwaltung generiert werden. Dazu müssen zunächst im generierten Editor die zu bearbeitenden Daten modelliert werden, wie in Listing 30 angegeben.

 
1enum Geschlecht { 
2  WEIBLICH("weiblich"), 
3  MAENNLICH("männlich") 
4} 
5 
6entity Person { 
7  String vorname; 
8  String nachname; 
9  Date geburtsdatum; 
10  Geschlecht geschlecht; 
11 
12} 
13 
14entity Adresse { 
15  String strasse; 
16  String ort; 
17  String hausnummer; 
18  Integer plz; 
19} 
20 
21entity Seminar { 
22  String name; 
23  Boolean kooperation; 
24}
Listing 30: Datenmodell der Seminarverwaltung als SDDSL-Modell

Anschließend ist ein Work&64258;ow zu erstellen, der lediglich den bereits beim Generator erstellten Work&64258;ow „generator.oaw“ aufruft und die gewünschten Ausgabeverzeichnisse angibt. In dem in Listing 31 angegebenen Fall werden die Generierungsergebnisse der Projektstruktur der Anwendung folgend auf einzelne Ordner aufgeteilt.

 
1<workflow> 
2  <component file="fhw/sd/generator.oaw"> 
3    <modelFile value="model.sddsl"/> 
4    <hbmTarget value="../sd-persistence-impl/res-gen/fhw/sd/persistence/"/> 
5    <ucTarget value="../sd-awk-impl/res-gen/fhw/sd/awk/"/> 
6    <dialogConfigTarget value="../sd-client/res-gen/fhw/sd/client/"/> 
7    <dialogDefTarget value="../sd-client/res-gen/fhw/sd/client/dialogdefinition/"/> 
8    <standaloneTarget value="../sd-test/src-gen/fhw/sd/standalone/"/> 
9    <enumTarget value="../sd-common/src-gen/fhw/sd/common/type/enums/"/> 
10  </component> 
11</workflow>
Listing 31: Work&64258;ow der Seminarverwaltung

Nun können der Work&64258;ow ausgeführt und eine der generierten Standalone-Klassen gestartet werden. Abbildung 17 zeigt den soeben erzeugten Dialog für die P&64258;ege der Personendaten. Neben den hier abgebildeten Attributen ist es ebenso möglich, über die entsprechenden Reiter die Beziehungen zu bearbeiten. Bei den Attributen ist zusätzlich, wie in Abbildung 17 bei dem Geburtsdatum ersichtlich, eine einfache Eingabeüberprüfung generiert worden. Lassen sich die Eingaben nicht in den vorgesehenen Typ konvertieren, so wird die Eingabe farblich hervorgehoben.


PIC

Abbildung 17: Dialog zur Personenp&64258;ege


Mögliche Weiterentwicklungen

Obwohl die in diesem Kapitel erstellte Stammdatenp&64258;ege bereits den grundlegenden Funktionsumfang realisiert, sind doch einige stillschweigende Konventionen getro&64256;en worden, die Anlass zu Weiterentwicklungen bieten:


[Seminarthemen WS08/09] [ < ] [ > ] [Übersicht]