Einführung in die modellgetriebene Entwicklung von Sprachen mit

Werbung
Humboldt-Universität zu Berlin
Institut für Informatik
Lehrstuhl Systemanalyse
Einführung in die modellgetriebene
Entwicklung von Sprachen
mit AMOF2 und MAS
Andreas Blunk
Berlin, 19. Oktober 2008
Inhaltsverzeichnis
1. Einleitung
2. Beispielsprache
2.1. Einleitung . . . . . . .
2.2. Struktur . . . . . . . .
2.3. Notation . . . . . . . .
2.4. Ausführungssemantik .
5
.
.
.
.
11
11
11
12
12
3. Spezifikation und Implementierung
3.1. Einleitung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. Metamodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1. Zusätzliche Einschränkungen . . . . . . . . . . . . . . . . . . . . .
3.3.2. Metamodelle erstellen . . . . . . . . . . . . . . . . . . . . . . . . .
3.4. Programmieren mit CMOF-basierten Modellen in Java . . . . . . . . . . .
3.4.1. Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4.2. Aufbau und Fähigkeiten von AMOF2 . . . . . . . . . . . . . . . .
3.4.3. Die CMOF-nach-Java-Abbildung am Beispiel von CoSAL . . . . .
3.4.4. Codegenerierung für das metamodellabhängige CoSAL-Repository
3.4.5. Programmieren mit CoSAL-Modellen . . . . . . . . . . . . . . . .
3.5. Laufzeitstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5.1. Laufzeitklassen für das CoSAL-Metamodell . . . . . . . . . . . . .
3.6. Ausführungssemantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.6.1. Operationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.6.2. Verhaltensimplementierung . . . . . . . . . . . . . . . . . . . . . .
3.6.3. Implementierungsmanager . . . . . . . . . . . . . . . . . . . . . . .
3.7. Modellierung der Ausführungssemantik mit MAS . . . . . . . . . . . . . .
3.7.1. MAS-Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.7.2. MOF-Modell-Browser . . . . . . . . . . . . . . . . . . . . . . . . .
3.7.3. MAS-Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.8. Modellausführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.8.1. Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.8.2. MAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
15
15
16
16
17
18
18
19
20
21
22
23
25
25
26
27
31
32
32
32
34
34
34
35
4. Verwandte Arbeiten
37
5. Zusammenfassung und Ausblick
39
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
A. Anhang
41
A.1. Metamodell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
A.2. Ant-Script für die Codegenerierung . . . . . . . . . . . . . . . . . . . . . . 42
A.3. Ausführungsverhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
1. Einleitung
1. Einleitung
Einführung
Die modellbasierte Entwicklung von Sprachen ist ein neuer Sprachbeschreibungsansatz,
der zur Zeit erforscht wird und viele Vorteile gegenüber grammatikbasierten Ansätzen
bietet. Das Neue an dieser Vorgehensweise ist die Spezifikation einer Sprache mit Hilfe
von Modellen in speziellen Sprachbeschreibungssprachen (Sprachen, mit denen Sprachen
beschrieben werden können). Diese Sprachen besitzen eine formale Basis und ermöglichen
eine abstrakte Spezifikation der verschiedenen Aspekte einer Sprache, wie zum Beispiel
Syntax, Notation und Semantik.
Ein weiterer Vorteil gegenüber Grammatiken besteht in der objektorientierten Beschreibung der Struktur einer Sprache. Diese ermöglicht, im Gegensatz zur Baumstruktur von Grammatiken, das Arbeiten auf beliebigen Graphen. Somit vereinfacht diese Art
der Beschreibung die Verwendung von objektorientierten Programmiersprachen für die
Implementierung von Sprachwerkzeugen, da die Konzepte der Sprachbeschreibung und
die Konzepte der Programmiersprache von der gleichen Art sind.
Des Weiteren erlaubt eine formale Spezifikation die automatische Bereitstellung von
Sprachwerkzeugen auf der Basis von Sprachmodellen. Bei der Entwicklung dieser Werkzeuge unterscheidet man generative und generische Ansätze. Der generative Ansatz basiert auf dem Generative Programming [CE00]. Dabei wird ein Generator erstellt, der aus
einer formalen Spezifikation automatisch fertige Software (zum Beispiel Sprachwerkzeuge) generieren kann. Im Gegensatz dazu verzichtet man bei der generischen Entwicklung
auf den Generierungsschritt und entwickelt eine variable Software, deren konkretes Aussehen und Verhalten durch ein Eingabemodell festgelegt wird. Diese Software wird als
Interpreter bezeichnet, der zusammen mit dem Eingabemodell das fertige Softwareprodukt darstellt.
Die Kombination aus modellbasierter Entwicklung und der automatischen Bereitstellung von Software mit generativen oder generischen Ansätzen wird als modellgetriebene
Entwicklung bezeichnet. Diese eignet sich besonders gut für die prototypische Entwicklung von domänspezifischen Sprachen (DSL), die gezielt für kleine Gruppen von Anwendern entwickelt werden und daher auf eine günstige Bereitstellung von Tools angewiesen sind. Die einmal entwickelten Interpreter und Generatoren sollen dabei für die
Erzeugung von Sprachwerkzeugen für möglichst viele Sprachen wiederverwendet werden.
Sie müssen daher auf der Basis einer allgemeinen Beschreibung der jeweiligen Anwendungsdomäne (für einen bestimmten Sprachaspekt) entwickelt werden. Diese allgemeine Beschreibung kann jedoch zu Einschränkungen im Funktionsumfang der generierten
Werkzeuge führen. Da Sprachen verschiedenartig sind, ist es häufig nicht einfach Beschreibungsmöglichkeiten für alle Eigenheiten von Sprachen bereitzustellen. Man versucht daher in diesen Fällen einen Kompromiss zu finden und konzentriert sich auf
die Konzepte, die in vielen Sprachen vorhanden sind. Wenn eine bestimmte Sprache
zusätzliche Besonderheiten aufweist, müssen die generierten Sprachwerkzeuge in nachfolgenden Entwicklungsphasen von Hand angepasst werden. Trotz dieser Einschränkungen
ergibt sich der Vorteil einer schnellen Verfügbarkeit von Prototypen, mit denen eine
5
1. Einleitung
Sprache unmittelbar eingesetzt und getestet werden kann.
Diese Arbeit beschäftigt sich mit der Vorstellung eines Frameworks für die modellgetriebene Entwicklung von Sprachen, das im Rahmen des Graduiertenkolleg METRIK
[MET] an der Humboldt-Universität zu Berlin entstanden ist. Das Framework besteht
aus den Werkzeugen AMOF2 und MAS und wird im Folgenden als das AMOF2/MASFramework bezeichnet.
Die nächsten Abschnitte geben eine Einführung in die Entwicklung von Sprachen und
beschreiben die grundlegenden Ideen der modellgetriebenen und generativen Entwicklung von Sprachen. Diese Erklärungen bilden die Voraussetzung für die anschließende
Vorstellung des AMOF2/MAS-Framework.
Sprachen
Eine Sprache definiert auf einer abstrakten Ebene Konzepte, die es erlauben eine Lösung
für ein Problem zu beschreiben. Die Konzepte beschreiben die grundlegenden Funktionen der Sprache und bilden die Bausteine für die Erstellung von Sprachinstanzen. Als
Sprachinstanz bezeichnet man allgemein die Beschreibung einer Lösung für ein Problem
mit den Konzepten einer Sprache (in ausführbaren Sprachen auch als Programm bezeichnet). Die Definition der Konzepte und ihrer Beziehungen wird als die Syntax einer
Sprache bezeichnet. Sie definiert Regeln, die die syntaktische Struktur von Sprachinstanzen festlegt. Man unterscheidet zwischen konkreter Syntax und abstrakter Syntax.
Die konkrete Syntax beschreibt eine zulässige Notation für Sprachinstanzen, während
die abstrakte Syntax ein Datenmodell unabhängig von einer konkreten Darstellung definiert, das mit Notationen in Beziehung gesetzt werden kann. Die Syntax ist der zentrale Aspekt einer Sprachbeschreibung, da sie die Konzepte definiert, die in Instanzen
der Sprache verwendet werden können. Damit Sprachinstanzen aber auch eine Bedeutung erhalten, muss die Semantik als ein weiterer Sprachaspekt beschrieben werden.
Die Semantik legt die Bedeutung der Konzepte fest. Es existieren verschiedene Formen
von Semantik. Eine Form von Semantik, die in Programmiersprachen verwendet wird,
ist die sogenannte Ausführungssemantik. Sie beschreibt die Semantik einer Sprache als
das Ausführungsverhalten von Sprachinstanzen, die in diesen Sprachen als Programme bezeichnet werden. Ein Programm beschreibt dann einen Plan für die Lösung eines
Problems, zum Beispiel durch einen Algorithmus. Die Ausführung dieses Plans liefert
ein Ergebnis, durch das das Problem gelöst wird. Damit Programme ausgeführt werden
können, muss die Ausführungssemantik in Form eines Interpreters oder eines Compilers
für die Sprache implementiert werden. Neben diesen entscheidenden Ausführungstools,
sind weitere Tools, wie zum Beispiel Debugger, Simulatoren, Editoren oder komplette
Entwicklungsumgebungen, für die Benutzbarkeit einer Sprache von großer Bedeutung.
Probleme existierender Sprachbeschreibungen
Sprachen werden traditionell auf der Basis von Grammatiken entwickelt. Grammatiken stellen eine formale und gut erforschte Methode für die Definition der Syntax einer
Sprache dar. Sie beschreiben in der Regel die konkrete Syntax von textuellen Sprachen,
6
1. Einleitung
können aber auch für die Beschreibung der abstrakten Syntax von grafischen Sprachen
verwendet werden. Der gravierende Nachteil von Grammatiken besteht in der Abbildung einer Sprachinstanz auf einen Baum als Graphen, was die Implementierung von
Sprachwerkzeugen erschwert.
Im Gegensatz dazu definiert der modellbasierte Beschreibungsansatz die abstrakte
Syntax einer Sprache durch ein objektorientiertes Metamodell. Das Metamodell beschreibt die Konzepte der Sprache und wird selbst mit den Konzepten einer Metasprache
beschrieben. Bevor ich auf die Metamodellierung näher eingehe, möchte ich zunächst die
Vorteile gegenüber Grammatiken erklären. Im Gegensatz zu Grammatiken können für
die Beschreibung von Metamodellen objektorientierte Konzepte verwendet werden. Damit können Sprachbeschreibungen von den Vorteilen der Objektorientierung, wie zum
Beispiel Vererbung und Wiederverwendbarkeit, profitieren. Sprachen können auf diese
Weise abstrakt beschrieben werden und Sprachbeschreibungen können für die Definition
vieler gleichartiger Sprachen wiederverwendet werden. Diese Beschreibungsmöglichkeiten
sind Kernkonzepte der Metamodellierung und in Grammatiken nicht vorhanden. Neben
der Beschreibung von Sprachen kann auch die Entwicklung von Sprachwerkzeugen von
der Metamodellierung profitieren. Grammatiken erlauben lediglich die automatische Erzeugung von Scannern und Parsern für die Analyse von Sprachinstanzen. Die Metamodellierung bietet dagegen bessere Möglichkeiten. Sie erlaubt die Programmierung mit objektorientierten Modellen in objektorientierten Programmiersprachen. Dadurch können
Sprachwerkzeuge effizienter implementiert und sogar generativ oder generisch entwickelt
werden.
Modellgetriebene Entwicklung
Die modellgetriebene Entwicklung von Sprachen verwendet die Ideen der Model Driven
Architecture (MDA) und erweitert diese um Methoden für die automatische Erzeugung
von Sprachwerkzeugen aus Sprachmodellen. Die Spezifikation einer Sprache erfolgt dabei mit spezifischen Sprachbeschreibungssprachen, die die einzelnen Aspekte von Sprachen beschreiben. Dabei ist es wichtig, Beziehungen zwischen Sprachen herstellen zu
können, da einzelne Aspekte im Allgemeinen nicht isoliert voneinander beschrieben werden können. Die Metamodellierung eignet sich dafür besonders gut, da alle Sprachen eine
gemeinsame Basis, die Metasprache, besitzen. Metamodelle bilden außerdem die Grundlage für die Implementierung von Sprachwerkzeugen. Die objektorientierten Strukturen
eines Metamodells werden dabei in eine Implementierungssprache abgebildet und können
so direkt für die Programmierung von Werkzeugen benutzt werden. Auf diese Weise kann
man zum Beispiel einen Interpreter für eine Sprache generisch auf der Basis eines Metamodells programmieren und dann für die Ausführung beliebiger Modelle, die Instanzen
des Metamodells sind, verwenden. Die Modelle dienen dabei als Eingabe, die zusammen
mit dem generischen Interpreter, einen vollständigen Interpreter für Sprachinstanzen
darstellen.
7
1. Einleitung
Metamodellierung
Für die Beschreibung von Metamodellen existieren verschiedene Metasprachen. Das
AMOF2/MAS-Framework verwendet die Meta Object Facility (MOF) der Object Management Group (OMG), die auch bei MDA und vielen anderen Ansätzen metamodellbasierter Sprachbeschreibungen eingesetzt wird. MOF ist nicht nur eine Metasprache,
sondern definiert auch ein plattformunabhängiges Framework für die Arbeit mit Daten
und Metadaten, zum Beispiel Modellen und Metamodellen. Es stellt Funktionen für die
Entwicklung und Integration metadatenbasierter Systeme zur Verfügung, zum Beispiel
Sprachen mit metamodellbasierter Syntaxdefinition. Dazu regelt es unter anderem den
Zugriff auf Modelle in Modell-Repositorys und den Austausch von Modellen über das
Format XMI (XML Metadata Interchange).
Ein Metamodell definiert in MOF die abstrakte Syntax einer Sprache. Es beschreibt
also die Konzepte der Sprache und deren Beziehungen. Für die Sprache UML beschreibt
das Metamodell zum Beispiel die Konzepte Class, Attribute und Operation und ihre
Beziehungen. In metamodellbasierten Sprachen sind die Sprachinstanzen Modelle, die
Instanzen des Metamodells der Sprache sind. Während man die Sprachinstanzen von
ausführbaren Sprachen als Programme bezeichnet, werden diese in metamodellbasierten
Sprachen häufig nur als Modelle bezeichnet.
MOF unterteilt die Modellwelt in vier Schichten Mi mit i = 0, 1, 2, 3. Die M0 -Schicht
enthält reale Daten und Objekte, die durch ein Modell in M1 beschrieben werden. Die
Modellschicht M1 wird durch eine Metamodell in M2 definiert. Ein Metamodell wird
wiederum durch ein Metametamodell in der Schicht M3 beschrieben. Das Metametamodell definiert sich schließlich selbst. In einer metamodellbasierten Sprachbeschreibung
befindet sich die Metasprache, mit der Sprachen beschrieben werden, in der Schicht M3 .
Die Schicht M2 enthält das Metamodell der Sprache. In M1 befinden sich Modelle, also
Sprachinstanzen. Die Schicht M0 enthält reale Daten und Objekte eines Modells während
seiner Ausführung.
Bei der Verwendung von MOF1 müssen zwei Probleme gelöst werden.
1. Damit Modelle tatsächlich ausgeführt werden können, muss ihre Ausführungssemantik beschrieben werden. Die Definition des Ausführungsverhaltens von metamodellbasierten Modellen wird aber durch MOF nicht geregelt und muss deshalb
mit externen Sprachen erfolgen.
2. MOF beschreibt ein plattformunabhängiges Framework. Dieses muss für die Entwicklung von Werkzeugen für konkrete Plattformen implementiert werden.
Zusammenfassend lässt sich sagen, dass MOF die Grundlage für die Beschreibung von
Metamodellen und die Integration, die Verwaltung und den Austausch von Metamodellen
und Modellen bildet. MOF definiert also eine gemeinsame Basis für die Beschreibung von
Sprachen und ermöglicht den Zusammenschluss verschiedener Sprachen in modellbasierten Sprachdefinitionen. Die Modelle, die eine Sprache beschreiben, dienen als Grundlage
für die Entwicklung von Sprachwerkzeugen oder für deren Generierung.
1
Die Diskussion alternativer Metametamodell-Konzepte ist nicht Teil dieser Arbeit.
8
1. Einleitung
AMOF2/MAS
AMOF2 und MAS sind Werkzeuge, mit denen Sprachen modellbasiert entwickelt werden können. Sie bilden ein Framework für die modellgetriebene Entwicklung von Sprachen und die Entwicklung von Sprachwerkzeugen in Java. Die Spezifikation einer Sprache besteht hier nun aus der Beschreibung der abstrakten Syntax mit einem Metamodell, das auf dem CMOF-Modell von MOF 2.0 basiert, und einer Beschreibung der
Ausführungssemantik mit Aktivitäten und einfachen Aktionen in der Sprache MAS
(MOF Action Semantics) [SF07]. Diese Sprachspezifikation bildet die Grundlage für die
Entwicklung von Sprachwerkzeugen unter Verwendung von AMOF2.
AMOF2 ist ein Metamodellierungs-Framework für CMOF-Modelle in Java. Es implementiert das CMOF-Modell von MOF 2.0 und ermöglicht die Programmierung mit
CMOF-basierten Modellen in Java. Zusätzlich erlaubt es die Generierung von Interfaces für die Elemente eines Metamodells und damit eine typsichere Programmierung mit
Modellen. Metamodelle definieren aber lediglich die Struktur von Modellen und nicht
deren Ausführungssemantik. AMOF2 erlaubt die Erweiterung eines MOF-Metamodells
mit Ausführungsverhalten in der Sprache MAS. Die Sprache MAS besitzt eine metamodellbasierte Sprachdefinition. Für die Ausführung von MAS-Modellen existiert ein
Interpreter, der AMOF2 verwendet und in Java implementiert ist. Der Interpreter wurde im Rahmen des Graduiertenkolleg METRIK entwickelt und wird in dieser Arbeit für
die Ausführung von MAS-Modellen verwendet. Des Weiteren gibt es einen grafischen
Editor für die Bearbeitung von MAS-Modellen, der vom Autor in einer früheren Arbeit
entwickelt wurde. Die vorliegende Arbeit beschäftigt sich aber nur mit der Verwendung
dieses Editors für die Erstellung und Bearbeitung von MAS-Modellen und nicht mit
seiner Entwicklung.
Die Beschreibung der Ausführungssemantik mit MAS basiert auf operationaler Semantik nach Plotkin [Plo81]. Operationale Semantik beschreibt die Ausführungssemantik
einer Sprache mit Hilfe der elementaren Operationen einer abstrakten oder konkreten
Maschine, die für die Ausführung von Programmen verwendet wird. Das Ausführungsverhalten einer solchen Beschreibung besteht aus einer Sequenz von Ausführungsschritten,
die mit Hilfe der elementaren Operationen den Zustand eines Programms in der Maschine verändern. Die Operationen arbeiten dabei auf der syntaktischen Struktur des
Programms und seinen Daten. Durch die Veränderung der Struktur und der Daten
wird ein Programm ausgeführt. Dieser Zusammenhang wird im Plotkin-Kalkül durch
Konfigurations-Transitions-Systeme formalisiert. Eine Konfiguration ist der Zustand eines Programms während seiner Ausführung, also seine syntaktische Struktur und seine
momentane Datenbelegung. Transitionen sind die Ausführungsschritte, die eine Konfiguration in eine andere überführen. Sie werden durch die elementaren Operationen des
Systems realisiert.
Die Ideen von Plotkin wurden von den Entwicklern von MAS für die Beschreibung des
Ausführungsverhaltens von metamodellbasierten Sprachen in der Sprache MOF Action
Semantics (MAS) übernommen. Konfigurationen sind hier Modelle, die durch ein Metamodell beschrieben werden. Die elementaren Operationen in den Transitionen werden
als Aktionen oder englisch Actions bezeichnet. Sie werden auf ein Modell angewendet
9
1. Einleitung
und verändern den Modellzustand. Die erforderlichen Aktionen für die Ausführung von
MOF-Modellen, wie zum Beispiel das Erzeugen oder das Löschen von Modellelementen, werden bereits durch den MOF-Standard definiert. Die Ausführungsreihenfolge von
Aktionen wird mit Aktivitätsdiagrammen beschrieben. Diese enthalten unter anderem
Elemente für die Modellierung des Kontroll- und des Objektflusses. Die Verbindung zwischen Aktivitäten und Metamodell erfolgt über die Operationen der Metamodellklassen.
Aktivitäten beschreiben das Ausführungsverhalten dieser Operationen. Ein Modell wird
ausgeführt, indem an einem bestimmten Modellelement eine initiale Operation aufgerufen wird, die die Ausführung startet. Die Ausführung der MAS-Aktivitäten wird dabei
durch einen in Java implementierten MAS-Interpreter durchgeführt, der ein MAS-Modell
analysiert und die entsprechenden Aktionen auf dem Modell-Repository durchführt.
MAS erlaubt damit eine plattformunabhängige Beschreibung der Ausführungssemantik
von Metamodellen.
Ziele der Arbeit
Diese Arbeit verfolgt zwei Ziele.
1. Die Vorstellung der Technologien, die im AMOF2/MAS-Framework für die modellbasierte Spezifikation von Sprachen verwendet werden. Die Erklärungen erfolgen
an Hand einer Beispielsprache, die zu Beginn der Arbeit beschrieben wird.
2. Die Vorstellung jener Werkzeuge, mit denen die Sprachbeschreibung erstellt werden kann und solcher, mit denen weitere Sprachwerkzeuge implementiert werden
können.
Aufbau der Arbeit
Die Arbeit beginnt mit einer informalen Beschreibung der verwendeten Beispielsprache
(Kapitel 2). Dazu werden zunächst die Konzepte, das Ausführungsverhalten und die
Notation der Sprache vorgestellt. Der Hauptteil der Arbeit (Kapitel 3) besteht aus der
formalen Spezifikation der Sprache mit MOF und MAS und zeigt die Implementierung
eines Programms für die Ausführung von Modellen in Java. Im Anschluss (Kapitel 4)
wird der AMOF2/MAS-Ansatz mit anderen metamodellbasierten Ansätzen verglichen.
Die Arbeit endet in mit einer kurzen Zusammenfassung und einem Ausblick (Kapitel 5).
10
2. Beispielsprache
2. Beispielsprache
2.1. Einleitung
Dieses Kapitel stellt die Beispielsprache vor, die im weiteren Verlauf der Arbeit verwendet wird. Die Struktur und die Semantik der Sprache werden hier zunächst informal
beschrieben und danach im Kapitel 3 formal mit Hilfe von MOF und MAS spezifiziert.
Die Beispielsprache sollte in ihrer Spezifikation von wichtigen Beschreibungsmitteln
Gebrauch machen, die in komplexeren Sprachen erforderlich sein werden. Auf diese Weise kann sie als Demonstrationsbeispiel für die Beschreibung echter Sprachen dienen. Sie
darf aber auch nicht zu komplex und mächtig sein, da sie hier sonst nicht vollständig
beschrieben werden könnte. Die Wahl fiel daher auf eine bekannte Sprache aus dem Bereich der theoretischen Informatik, die diese Anforderungen erfüllt. Es handelt sich dabei
um eine Sprache für die Modellierung und Ausführung von deterministischen endlichen
Automaten (auch bekannt als DFAs), die zum Beispiel in [Sch03] vorgestellt werden.
Ein DFA beschreibt eine reguläre Sprache und erkennt die Wörter, die zu dieser Sprache
gehören. Neben den einfachen Zuständen, die für die Beschreibung von DFAs benutzt
werden können, verfügt die Beispielsprache zusätzlich über zusammengesetzte Zustände.
Diese Zustände werden durch einen Unterautomaten verfeinert, der sobald er einmal definiert wurde in beliebig vielen zusammengesetzten Zuständen benutzt werden kann.
Ein Unterautomat beschreibt also eine Klasse gleichartiger Automaten. Instanzen dieser Klasse sind die zusammengesetzten Zustände. Diese enthalten einen Unterautomat,
der während der Ausführung eines Programms in der Sprache unabhängig von anderen
Unterautomaten in anderen zusammengesetzten Zustände einen inneren Zustand annehmen kann. Damit enthält die Beispielsprache das Klassifizierer/Instanz-Konzept, das in
vielen Sprachen vorhanden ist. Ob diese erweiterten DFAs immer noch reguläre Sprache erkennen oder nicht, ist für diese Arbeit nicht von Bedeutung. Die Sprache wird im
Folgenden als Composite State Automaton Language oder kurz CoSAL bezeichnet.
Das Kapitel beschreibt zuerst die Struktur der Sprache und präsentiert danach eine
Notation für Modelle. Es endet mit einer Beschreibung der Ausführungssemantik.
2.2. Struktur
Die Konzepte der Sprache sind Automat, Zustand und Transition. Ein Automat ist
ein Zustandsgraph, der aus Zuständen als Knoten und Transitionen als Kanten besteht. Ein Wort wird von einem Automaten erkannt, indem er eine Folge von Zuständen
durchläuft und dabei die Zeichen des Wortes entsprechend der Transitionen konsumiert.
Es wird zwischen einem Startzustand und beliebig vielen Endzuständen unterschieden.
Ein Wort wird erfolgreich erkannt, falls der Automat die Abarbeitung in einem Endzustand beendet. Automaten werden unterteilt in äußere Automaten, die ein vollständiges
Wort erkennen, und Unterautomaten, die Teilwörter erkennen. Unterautomaten verfeinern Zustände von Automaten. Diese Zustände werden als zusammengesetzte Zustände
bezeichnet. Falls sich ein Automat in einem zusammengesetzten Zustand befindet, so
befindet er sich zeitgleich im entsprechenden Unterautomaten in einem Unterzustand.
11
2. Beispielsprache
Ein Automat darf als Unterautomat in beliebig vielen zusammengesetzten Zuständen benutzt werden. Auf diese Weise ist es möglich, Klassen von Automaten zu definieren und
Instanzen dieser Klassen in Form von Unterautomaten in zusammengesetzten Zuständen
einzusetzen.
2.3. Notation
CoSAL-Modelle werden mit einer grafischen Notation dargestellt. Ein Beispielmodell ist
in Abbildung 1 zu sehen.
Abbildung 1: CoSAL-Beispielmodell
Das Modell besteht aus einem Automaten X, der neben einem Start- und einem Endzustand, zwei zusammengesetzte Zustände A und B enthält. Diese werden jeweils durch
eine Instanz des Unterautomaten Y verfeinert. Damit können sich A und B unabhängig
voneinander in den Zuständen C und D von Y befinden. Dieser Automat erkennt zum
Beispiel das Wort bdeaf.
2.4. Ausführungssemantik
Ein Wort wird durch die Ausführung eines Automaten erkannt. Die Semantik wird daher
als Ausführungssemantik für Modelle der Sprache beschrieben. Bei der Ausführung eines
Automaten wird ein Wort durch die Konsumierung einer festen Sequenz von Eingabezeichen erkannt. Der Automat wechselt dabei seinen Zustand, wobei Transitionen als
Zustandsübergänge dienen. Sie legen fest, durch welches Eingabezeichen ein bestimmter Folgezustand erreicht werden kann. Ein Zustandsübergang wird vollzogen, wenn im
aktuellen Zustand eine Transition für das zu verarbeitende Eingabezeichen vorhanden
ist. Dabei wird der durch die Transition festgelegte Folgezustand angenommen. Falls
der aktuelle Zustand ein zusammengesetzter Zustand ist, wird der Zustandsübergang
vollzogen, falls eine Transition für (1) den zusammengesetzten Zustand oder (2) den
Unterzustand aktiv ist. Dabei entspricht die Semantik der zusammengesetzten Zustände
der von History-States in UML [OMG07]. Das bedeutet, dass sich ein zusammengesetzter
Zustand seinen Unterzustand beim Verlassen merkt und diesen beim erneuten Betreten
wieder annimmt.
Betrachten wir zur besseren Veranschaulichung die Ausführung des Beispielmodells
mit der Eingabesequenz ddbdeaf. Zu Beginn befindet sich der Automat X im zusam-
12
2. Beispielsprache
mengesetzten Zustand A und im Unterautomaten Y im Unterzustand C. Zur Vereinfachung bezeichnen wir den aktuellen Zustand mit Zustand[Unterzustand[...]]. Der
Startzustand ist somit der Zustand A[C]. Das erste Eingabezeichen d ändert den Zustand in A[D]. Für das nächste d existiert keine Transition und es wird ignoriert. Der
Zustand wird danach durch das Eingabezeichen b verlassen und es wird der Zustand
B[C] angenommen. Die Sequenz de überführt den Unterautomaten Y des zusammengesetzten Zustandes B in den Endzustand, also B[-]. Danach ändert a den aktuellen
Zustand zu A[D]. Der Unterzustand D wird deshalb angenommen, da der Automat sich
in diesem Zustand zuletzt befand. Die Ausführung endet durch die Verarbeitung von f
im Endzustand des Automaten X.
13
3. Spezifikation und Implementierung
3. Spezifikation und Implementierung
3.1. Einleitung
Dieses Kapitel beschäftigt sich mit der formalen Spezifikation der Sprache CoSAL und
zeigt, wie Sprachwerkzeuge mit AMOF2 implementiert werden können. Das Ziel ist die
Implementierung eines Java-Programms, mit dem man CoSAL-Modelle ausführen kann.
Dazu muss die abstrakte Syntax und die Ausführungssemantik der Sprache erstellt werden. Die Vorgehensweise wird schrittweise erklärt und die benötigten Tools werden nacheinander vorgestellt. Das Kapitel beginnt mit einer Beschreibung der Architektur des
verwendeten Frameworks AMOF2/MAS (Abschnitt 3.2). Danach wird das Metamodell
für die Sprache erstellt (Abschnitt 3.3). Im Anschluss wird die Programmierung mit
Modellen mit dem Metamodellerierungs-Framework AMOF2 erklärt (Abschnitt 3.4). Es
wird gezeigt, wie das Metamodell aus einer XMI-Datei geladen werden kann und wie
Metamodellinstanzen erzeugt werden können. Als nächstes wird das Metamodell um
Laufzeitstrukturen erweitert (Abschnitt 3.5). Die Trennung zwischen abstrakter Syntax
und Laufzeitstrukturen vereinfacht bei komplexeren Sprachen die Sprachbeschreibung
erheblich und ermöglicht die separate Modellierung von statischen und dynamischen
Strukturen. Die Laufzeitstrukturen werden während der Modellausführung instanziiert
und speichern Laufzeitinformationen für statische Strukturklassen. Danach wird das Metamodell um Operationen erweitert (Abschnitt 3.6). Das Ausführungsverhalten der Operationen wird zu Demonstrationszwecken nicht nur mit MAS-Aktivitäten beschrieben,
sondern auch mit Java implementiert und für Query-Operationen mit OCL-Ausdrücken
festgelegt. Die Werkzeuge für die Modellierung mit MAS werden in Abschnitt 3.7 vorgestellt. Das Kapitel endet mit der Vorstellung des Programms für die Ausführung von
CoSAL-Modellen (Abschnitt 3.8).
3.2. Architektur
Das AMOF2/MAS-Framework besteht aus verschiedenen Werkzeugen, die auf Java und
Eclipse basieren (siehe Abbildung 2).
Tools
weitere Sprachtools
MAS-Interpreter
Frameworks
A MOF 2.0 for Java
Plattformen
Java
MAS-Modellierungs-Umgebung
(MAS-Editor, MOF-Modell-Browser, Eclipse-Perspektive)
GEF
Plugins
Eclipse
Abbildung 2: Architektur - Sprachenentwicklung mit AMOF2 und MAS
AMOF2 wird als Modell-Repository und für die Programmierung mit Modellen in Java
verwendet. Metamodelle werden mit externen UML-Tools erstellt und müssen als XMIDateien in AMOF2 geladen werden. Für die Beschreibung der operationalen Semantik
UML-Tools
15
3. Spezifikation und Implementierung
von Metamodellen mit MAS-Aktivitäten stehen einige Eclipse-basierte Werkzeuge zur
Verfügung, die durch eine spezielle Eclipse-Perspektive zusammengefasst werden. Dazu
gehört ein grafischer Editor für die Modellierung von Aktivitäten, der auf dem Graphical
Editing Framework (GEF) basiert, und ein Modellnavigator für das AMOF2-ModellRepository, der die Struktur von Modellen visualisieren kann und die Erstellung von
Aktivitäten für Operationen erlaubt.
3.3. Metamodell
Aus den Konzepten der Sprache wird nun das in Abbildung 3 gezeigte Metamodell erstellt. Es beschreibt die abstrakte Syntax der Sprache und damit alle möglichen Modelle,
die formuliert werden können. Die Basiskonzepte Automat, Zustand und Transition werden im Metamodell auf die Klassen Automaton, State und Transition abgebildet. Ihre
Beziehungen werden durch entsprechende Assoziationen ausgedrückt.
Abbildung 3: Basismetamodell
3.3.1. Zusätzliche Einschränkungen
Das Metamodell definiert eine Menge gültiger Modelle durch die Beschreibung ihrer
Struktur. Es gibt jedoch auch Bedingungen für gültige Modelle, die durch diese Beschreibung nicht ausgedrückt werden können. In der Sprache CoSAL kann zum Beispiel durch
das Metamodell allein nicht festgelegt werden, dass die Ausgangs- und Folgezustände eines bestimmten Zustand in einem Automaten zu demselben Automaten gehören müssen.
Die Beschreibung dieser zusätzlichen Einschränkungen wird als die statische Seman-
16
3. Spezifikation und Implementierung
tik einer Sprache bezeichnet. Sie kann für MOF-Metamodelle mit OCL-Invarianten beschrieben werden. Diese werden im Kontext bestimmter Metamodellklassen definiert und
können zu einem späteren Zeitpunkt auf Konsistenz überprüft werden. Für die Sprache
CoSAL werden folgende Einschränkungen als OCL-Invarianten formuliert:
• Die Transitionen und die Ausgangs- und Folgezustände eines Zustandes in einem
Automaten gehören zu dem selben Automaten.
context State inv selfContainedAutomatons:
automaton.state->includesAll(outgoing.target->union(incoming.source))
and automaton.transition->includesAll(outgoing->union(incoming))
• Ein Automat besitzt einen global eindeutigen Namen.
context Automaton inv uniqueNamesForAutomatons:
Automaton.allInstances()->forAll(a1, a2 |
a1 <> a2 implies a1.name <> a2.name)
• Ein Zustand besitzt einen innerhalb seines Automaten eindeutigen Namen.
context Automaton inv uniqueNamesForStatesInOneAutomaton:
state->forAll(s1, s2 | s1 <> s2 implies s1.name <> s2.name)
• Für ein bestimmtes Eingabezeichen existiert höchstens eine abgehende Transition.
context State inv uniqueInputTrigger:
outgoing->forAll(t1, t2 | t1 <> t2 implies t1.input <> t2.input)
3.3.2. Metamodelle erstellen
Für die Verwendung des Metamodellierungs-Framework AMOF2 müssen Metamodelle
in einem bestimmten Format vorliegen. Das Standardformat ist XMI Version 2.0 mit Modellen, die auf der Basis von CMOF gespeichert sind. Modellierungswerkzeuge, die dieses
Format unterstützen, können direkt für die Erstellung von Metamodellen benutzt werden. Neben XMI werden auch einige herstellerabhängige Austauschformate unterstützt.
Möchte man die Werkzeuge anderer Hersteller verwenden, so muss ein spezieller Transformator verfügbar sein, der ein Modell im toolspezifischen Austauschformat in ein CMOFModell in AMOF2 übersetzen kann. Ein solcher Transformator existiert für das Format
MDXML von MagicDraw UML. MagicDraw UML ist ein UML-Modellierungswerkzeug,
das für die Erstellung von CMOF-Metamodellen für AMOF2 benutzt werden kann.
Die Verwendung eines UML-Tools ist möglich, da MOF und UML einen gemeinsamen
Sprachkern, die UML Infrastructure, besitzen. Dadurch existieren die Elemente in einem UML-Klassendiagramm mit ähnlichen Bezeichnungen auch in CMOF-Modellen.
Der Transformator übersetzt also alle UML-Elemente in CMOF-Elemente, wobei nur
solche UML-Elemente verwendet werden dürfen, für die es entsprechende Elemente in
CMOF gibt. Das bedeutet zum Beispiel, dass die Spezifikation von Sichtbarkeiten für
17
3. Spezifikation und Implementierung
Attribute von Klassen keine Bedeutung hat, da Propertys in CMOF keine Sichtbarkeiten
besitzen.
Das Metamodell für CoSAL kann nun mit Hilfe eines UML-Klassendiagramms in MagicDraw UML erstellt werden. Die Paketstruktur des UML-Modells wird dabei ebenfalls
nach CMOF abgebildet. Zunächst wird also ein Paket model erstellt und in diesem ein
Klassendiagramm mit beliebiger Bezeichnung. Danach werden die Elemente des Metamodells für die Sprache CoSAL entsprechend der Abbildung 12 (befindet sich im Anhang)
hinzugefügt. Zum Schluss muss das Metamodell als MDXML-Datei gespeichert werden.
Es liegt dann in einem Format vor, das mit AMOF2 geladen werden kann.
OCL-Invarianten
OCL-Invarianten können ebenfalls mit MagicDraw UML zum Metamodell hinzugefügt
werden. Dazu öffnet man die Spezifikation einer Klasse unter Specification und erstellt
dann unter dem Punkt Constraints ein neues Constraint. Die Spezifikation dieses Constraints kann man ebenfalls bearbeiten. Als Sprache muss man unter Language OCL
”
2.0“ auswählen. Danach kann unter Body der entsprechende OCL-Ausdruck eingetragen
werden. Dabei wird man durch einen OCL-Syntaxchecker unterstützt. Dieser überprüft
jedoch nicht die semantische Korrektheit des Ausdrucks.
Abbildung 4: OCL-Invarianten mit MagicDraw UML hinzufügen
3.4. Programmieren mit CMOF-basierten Modellen in Java
3.4.1. Einführung
Damit die Sprache CoSAL benutzt werden kann, muss es Tools geben, mit denen Modelle erstellt und ausgeführt werden können. Diese Tools werden in vorhandenen Programmiersprachen erstellt und müssen in ihrer jeweiligen Programmierumgebung auf
18
3. Spezifikation und Implementierung
Modelle zugreifen können. Damit man in Java auf CMOF-basierte Modelle zugreifen
kann, muss das plattformunabhängige Metamodellierungs-Framework MOF für Java
implementiert werden. Eine Implementierung von MOF 2.0 für Java ist A MOF 2.0
for Java (AMOF2). Es beschreibt eine Abbildung für CMOF-Modelle nach Java und
implementiert ein Modell-Repository, das die in MOF beschriebenen Funktionen für
die Verwaltung von Modellen bereitstellt. Modellelemente werden in AMOF2 durch
Java-Objekte repräsentiert. Der Zugriff auf diese Objekte wird durch Java-Interfaces beschrieben, die eine Abbildung von Klassifizierern für Modellelemente nach Java sind. Ein
Interface ist also eine Abbildung für eine Metamodellklasse nach Java und ermöglicht
den lesenden und schreibenden Zugriff auf ein bestimmtes Modellelement. Es existieren
zwei Ausprägungen von Interfaces. Generische Interfaces ermöglichen einen universellen,
aber ungetypten Zugriff auf Modellelemente. Spezifische Interfaces werden auf der Basis
eines Metamodells generiert und erlauben einen getypten Zugriff. Das bedeutet, dass
man unter Verwendung des generischen Interface zum Beispiel mit der Methode Object
get(Property property) auf eine bestimmte Eigenschaft eines Modellelementes zugreifen kann. Obwohl man dabei aber auf ein konkretes Modellelement mit konkreten
Eigenschaften zugreift, sind Rückgabetyp und Argument generische Typen. Damit die
Programmierung sicher ist, müsste man jetzt mit dem Instance-Of-Operator von Java
eine manuelle Typprüfung durchführen. Spezifische Interfaces erlauben dagegen den Zugriff mit speziell generierten Methoden. Angenommen ein Modellelement besitzt eine
Eigenschaft subAutomaton vom Typ Automaton. Dann wird eine Methode Automaton
getSubAutomaton() generiert, mit der ein getypter Zugriff auf die Eigenschaft möglich
ist. Die spezifischen Interfaces müssen aber vor ihrer Verwendung generiert werden. Bevor die Generierung beschrieben wird, möchte ich aber zunächst etwas genauer auf den
Aufbau und die Fähigkeiten von AMOF2 eingehen.
3.4.2. Aufbau und Fähigkeiten von AMOF2
Den Kern von AMOF2 bildet das Instanzmodell, das Modellelemente unabhängig von
ihrem Metamodell speichert und verwaltet. Das Instanzmodell speichert Modellelemente
als Java-Objekte. Die Modelle befinden sich ausschließlich im Hauptspeicher und können
nur als XMI-Dateien gespeichert bzw. geladen werden; eine Anbindung an eine Datenbank existiert also nicht. Dadurch ist ein schneller Zugriff auf Modelle möglich, der für die
Implementierung von Sprachen von großer Bedeutung ist. Der Zugriff auf das Instanzmodell ist im Paket Reflection des MOF-Standard beschrieben. Reflection ermöglicht es
CMOF-Objekten über eine einheitliche Schnittstelle auf ihre Metaklasse und auf ihre
Eigenschaften zuzugreifen und diese zu verändern (ähnlich wie Objekte in Java, die mit
Hilfe von Java-Reflection über das Attribut class auf ihre Klassendefinition, sowie ihre Attribute und Operationen zugreifen können). Die generischen Interfaces, die in der
Einführung für dieses Kapitel genannt wurden, sind im Prinzip eine Implementierung
für Reflection von CMOF. Sie erlauben einen ungetypten Zugriff auf Modellelemente im
Instanzmodell und können direkt von Programmen verwendet werden. Die direkte Programmierung mit Reflection ist jedoch sehr unsicher, da keine statische Typprüfung auf
der Basis eines Metamodells erfolgen kann. Für eine sichere Programmierung mit Model-
19
3. Spezifikation und Implementierung
len erlaubt AMOF2 die Generierung spezifischer Interfaces und Klassen auf der Basis
eines Metamodells. Diese Interfaces und Klassen, die auch als metamodellabhängiges
Repository bezeichnet werden, typisieren den Zugriff auf das Instanzmodell für ein bestimmtes Metamodell und nutzen dabei die vorhandene Reflection-Implementierung von
AMOF2. Für jede Metamodellklasse und ihre Propertys und Operationen wird ein JavaInterface mit Methoden für die Operationen und für den Zugriff auf die Propertys generiert. Des Weiteren werden Java-Klassen als Implementierung für das Interface erzeugt.
Diese Java-Klassen implementieren die Methoden im Interface und leiten Methodenaufrufe an Reflection weiter. Neben dem Zugriff auf Modellelemente über Java-Objekte
sind Factorys und Extents weitere Bestandteile von Reflection. Factorys erlauben die Erstellung von Modellelementen. Für sie wird ebenfalls Java-Code generiert. Extents sind
Lebensräume für Modellelemente. Sie enthalten die Elemente genau eines Modells als Instanz eines Metamodells. Ausführliche Informationen zur Implementierung von AMOF2
und insbesondere auch zur Sprachabbildung der Konzepte von CMOF nach Java werden
in [Sch05] gegeben.
Der nächste Abschnitt zeigt ausschnittweise den generierten Code für das metamodellabhängige Repository von CoSAL. Im Anschluss werden die notwendigen Schritte
für die Initiierung des Codegenerierungsprozesses vorgestellt.
3.4.3. Die CMOF-nach-Java-Abbildung am Beispiel von CoSAL
Die Abbildung der Konzepte von CMOF auf die Konzepte der Programmiersprache
Java ist folgenderweise realisiert: Modellelemente werden auf Java-Objekte abgebildet,
Klassifizierer für Modellelemente auf Java-Klassen und Interfaces, Propertys und Operationen von Modellelementen auf Java-Methoden, Assoziationen zwischen Klassifizierern auf Java-Methoden. Instanzen von Assoziationen werden in Java als Referenzen
repräsentiert.
Diese Abbildung soll nun mit einem Beispiel veranschaulicht werden. Für die Klasse
Automaton aus dem CoSAL-Metamodell wird der folgende Java-Code generiert. Er besteht aus einer Java-Interface-Datei Automaton.java mit Getter- und Setter-Methoden
für die Propertys und Java-Methoden für die Operationen.
public interface Automaton extends cmof.reflection.Object {
public java.lang.String getName();
public void setName(java.lang.String value);
...
public void run(java.lang.String inputSequence);
...
}
Des Weiteren wird Code für Java-Klassen generiert, die das Interface implementieren. Der generierte Code enthält dabei nicht nur das Skelett einer Klasse, sondern auch
Implementierungen für alle Methoden. Diese Implementierungen leiten alle Methodenaufrufe an Reflection weiter. Instanzen der Java-Klassen repräsentieren Modellelemente
als Java-Objekte. Die Instanziierung kann aber nicht direkt, sondern nur über Factorys,
20
3. Spezifikation und Implementierung
erfolgen. Eine Factory erzeugt Instanzen von Metamodellelementen für ein bestimmtes
Paket des Metamodell. Für jedes Paket im Metamodell wird Java-Code für eine Factory generiert. Dazu gehören neben dem Interface der Factory auch Java-Klassen, die
dieses Interface implementieren. Für das Paket model im CoSAL-Metamodell wird zum
Beispiel das folgende Factory-Interface generiert.
public interface modelFactory extends cmof.reflection.Factory {
public hub.sam.stateautomaton.model.State createState();
public hub.sam.stateautomaton.model.Automaton createAutomaton();
...
}
Die Erzeugung von Factorys für die Instanziierung von Metamodellelementen und
die Programmierung mit Modellen wird im übernächsten Abschnitt 3.4.5 gezeigt. Wir
werden uns jetzt zunächst mit der Durchführung der Codegenerierung beschäftigen.
3.4.4. Codegenerierung für das metamodellabhängige CoSAL-Repository
Die Codegenerierung muss bei Änderungen am Metamodell immer wieder neu durchgeführt werden. Es gibt verschiedene Möglichkeiten diesen Prozess zu automatisieren.
Man kann die Codegenerierung in Java programmieren und durch Aufruf eines JavaProgramms starten oder ein Ant-Script erstellen und ausführen. Dabei können spezielle
von AMOF2 bereitgestellte Ant-Tasks verwendet werden.
Für die CoSAL-Codegenerierung werden wir die zuletzt genannte Methode einsetzen.
Die nachfolgende Abbildung zeigt die Verwendung der Ant-Tasks am Beispiel für das
CoSAL-Ant-Script (die komplette Datei befindet sich im Anhang).
Automatisierung der Codegenerierung mit Ant
Zuerst müssen die Ant-Tasks aus AMOF2 im Target init bekannt gemacht werden.
<target name="init">
<typedef name="package" classname="hub.sam.mof.ant.Package"
classpathref="classpath"/>
<taskdef name="generatecode" classname="hub.sam.mof.ant.GenerateCode"
classpathref="classpath"/>
</target>
Danach kann das Task generatecode für die Codegenerierung benutzt werden.
<target name="generate-repository" depends="clean,init">
<generatecode src="resources/StateAutomaton.syntax.mdxml" md="true"
destDir="./generated-src" instances="true" remote="true">
<package name="model" javaPackagePrefix="hub.sam.stateautomaton"/>
</generatecode>
</target>
21
3. Spezifikation und Implementierung
Die benötigten Informationen werden durch Parameter festgelegt. Zwingend erforderliche Parameter sind src und destDir. Der Parameter src gibt die Speicherposition
der Metamodell-Datei relativ zur Position des Ant-Script an. Als Format der Datei wird
standardmäßig XMI verwendet. Der Parameter destDir dient der Angabe des Zielverzeichnis, in dem der generierte Java-Code abgelegt wird. Optionale Parameter sind md
und instances. Beide Parameter sind vom Typ Boolean. Der Parameter md legt fest,
ob das Metamodell in der angegebenen Datei im Format MDXML vorliegt. Falls der
Wert für den Parameter instances auf true gesetzt ist, wird zusätzlicher Code für die
Verwaltung von Laufzeitklassen erzeugt (siehe Abschnitt 3.5).
Das Sub-Task package erlaubt die Anpassung der Paket-Struktur im Metamodell auf
die Java-Umgebung. Über den Parameter javaPackagePrefix kann ein Präfix für das
Paket, das das Metamodell enthält, angegeben werden. Der gesamte Java-Code wird
dann in diesem Java-Paket erzeugt. Im Beispiel erfolgt die Codegenerierung also im
Java-Paket hub.sam.stateautomaton.model.
Als nächstes wird die Verwendung des generierten Repository für die Erstellung eines
Testmodells gezeigt.
3.4.5. Programmieren mit CoSAL-Modellen
Der zentrale Interaktionspunkt mit AMOF2 ist ein Objekt der Klasse Repository. Die
Klasse stellt Methoden für die Verwaltung von Modellen, wie zum Beispiel das Laden und
Speichern von Modellen in XMI-Dateien, zur Verfügung. Wir verschaffen uns zunächst
Zugang zum lokalen Repository.
Repository repository = Repository.getLocalRepository();
Das Repository enthält die Modellelemente in Extents. Der CMOF-Extent ist in das
Repository integriert. Er enthält die CMOF-Modellelemente, wie zum Beispiel Class,
Property und Package. Instanzen dieser Modellelemente werden in MOF-Metamodellen,
wie zum Beispiel dem CoSAL-Metamodell, verwendet. Modellelemente können zusätzlich
in Paketen strukturiert werden. Die Elemente des CMOF-Modells sind im Paket cmof
enthalten. Für die weitere Verwendung müssen sie zunächst aus dem Extent extrahiert
werden. Extents stellen verschiedene Methoden für den Zugriff auf Modellelemente bereit. Eine dieser Methode ist query.
Extent cmofExtent = repository.getExtent(Repository.CMOF_EXTENT_NAME);
Package cmofPackage = (Package) cmofExtent.query("Package:cmof");
Um das CoSAL-Metamodell als Instanz des CMOF-Modells zu laden, muss zunächst
ein neuer Extent angelegt werden. Danach wird das Metamodell, das als XMI-Datei vorliegt, in den Extent metaExtent auf der Basis des CMOF-Modells im Paket cmofPackage
aus der Datei stateautomaton.syntax.mdxml geladen. Anschließend wird das Paket
model extrahiert, über das der Zugriff auf die enthaltenen Metamodellelemente möglich
ist.
22
3. Spezifikation und Implementierung
Extent metaExtent = repository.createExtent("metaExtent");
repository.loadXmiIntoExtent(metaExtent, cmofPackage,
"stateautomaton.syntax.mdxml");
Package metamodel = (Package) metaExtent.query("Package:model");
Ein weiterer Extent speichert ein Modell als Instanz des Metamodells. Für die Instanziierung werden Factorys verwendet. Eine Factory wird am Repository für ein bestimmtes
Metamodell mit der Methode createFactory angelegt.
Extent modelExtent = repository.createExtent("modelExtent");
modelFactory factory = (modelFactory) repository.createFactory(modelExtent,
metamodel);
An diesem Punkt wurde das Metamodell geladen und ein Extent für ein Testmodell angelegt, der Instanzen von Metamodellelementen aufnehmen kann. Die generierte
Factory kann nun für die Instanziierung von Modellelementen verwendet werden, deren
Eigenschaften über die Set- und Get-Methoden hinzugefügt werden können.
Automaton a = factory.createAutomaton();
a.setName("X");
State s = factory.createState();
s.setName("A");
a.getState().add(s);
...
Für die Ausführung von Modellen müsste man lediglich noch Operationen zum Metamodell hinzufügen und ihr Verhalten mit Java oder MAS beschreiben. Zunächst muss
jedoch noch geklärt werden, wie die für die Ausführung wichtigen Laufzeitinformationen, im Modell gespeichert werden können. Dazu wird es nötig sein, das Metamodell um
Laufzeitstrukturen zu erweitern.
3.5. Laufzeitstrukturen
Das Metamodell besteht zur Zeit nur aus Strukturklassen, die die abstrakte Syntax der
Sprache beschreiben und damit die Modelle definieren, die vom Benutzer erstellt werden
können. Das Strukturmodell ist vergleichbar mit einem Programm in herkömmlichen
Programmiersprachen. Seine Struktur definiert einen Ablaufplan für die Ausführung
der operationalen Semantik der Sprache. Damit ein Modell ausgeführt werden kann,
müssen aber auch dynamische Informationen, wie zum Beispiel Werte von Variablen,
im Modell gespeichert werden. Eine Möglichkeit diese Informationen zu speichern ist die
Erweiterung der bestehenden Strukturklassen mit Laufzeitklassen.
Bei der Ausführung der Sprache CoSAL ist es zum Beispiel wichtig den aktuellen
Zustand eines Automaten zu speichern, da die nachfolgende Verarbeitung von Eingabezeichen von diesem Zustand abhängt. Man könnte also ein neues Property currentState
zur Klasse Automaton hinzufügen und würde damit den aktuellen Zustand für einen Automaten während der Modellausführung festhalten. Dieser Ansatz bringt jedoch einige
23
3. Spezifikation und Implementierung
Nachteile mit sich. Struktur- und Laufzeitinformationen verschmelzen in einem Modellelement miteinander. Dadurch wird das initiale Modell während der Ausführung zerstört.
Wenn der initiale Zustand eines Automaten in currentState gespeichert wird und dieser Zustand Teil der Statik des Modells ist, dann könnte man das Modell nicht neu
starten, da man diesen Zustand während der Ausführung verliert. Dieses Problem kann
man aber leicht kompensieren, indem man den initialen Zustand mit separaten Propertys
modelliert.
In komplexeren Sprachen benötigt man jedoch bessere Mechanismen für die Modellierung von Laufzeitinformationen. Diese Sprachen erlauben meistens eine Klassifizierung
von Elementen des Modells. Man definiert ein Element, das eine Klasse von Elementen
beschreibt, und verwendet Instanzen der Klasse an vielen anderen Stellen im Modell.
Auf diese Weise kann man Teile des Modells für die Beschreibung des Modells selbst
wiederverwenden.
Die Sprache CoSAL unterstützt ebenfalls das Konzept der Klassifizierung. Automaten werden einmal definiert und können dann als Unterautomaten in beliebig vielen
zusammengesetzten Zuständen eingesetzt werden. Bei der Ausführung muss aber auch
der aktuelle Zustand der Unterautomaten verändert werden. Würde man den aktuellen Zustand als ein Property der Klasse Automaton modellieren, dann ist klar, dass die
verschiedenen Laufzeitzustände der Unterautomaten nicht voneinander getrennt werden
könnten. Man könnte natürlich das Property für den aktuellen Zustand in Abhängigkeit
der Unterautomaten beschreiben. Dann müsste man sich aber selbst um die Verwaltung
dieser Informationen kümmern.
AMOF2 versucht dieses Problem durch eine separate Modellierung der statischen und
dynamischen Teile des Modellzustands zu lösen und fügt dafür ein neues Modellelement
zur Sprache CMOF hinzu. Dieses Modellelement ist ein neuer Beziehungstyp zwischen
Klassen, der es erlaubt die Statik und die Dynamik eines Sprachkonzeptes in getrennten Klassen zu modellieren und diese dann über die neue Beziehung miteinander in
Relation zu setzen. Diese Beziehung wird als Runtime-Representation-Of -Beziehung bezeichnet und ist nur mit AMOF2 als Modellierungskonzept verfügbar. Während der
Modellausführung können dann Laufzeitinstanzen für Strukturelemente erzeugt werden
und sowohl ihre Laufzeitinformationen in der Laufzeitinstanz speichern als auch über
die Verbindung zum Strukturelement auf die Struktur des Modells zugreifen.
Für die Modellierung der Beziehung wird eine UML-Realization verwendet, die mit
MagicDraw UML zum Metamodell hinzugefügt werden kann. Dieses Modellelement hat
in CMOF keine Bedeutung und kann deshalb benutzt werden. Um die Navigation von
Laufzeitinstanzen zu Strukturelementen zu gewährleisten werden die Runtime-Representation-Of -Beziehungen vor der Modellausführung durch AMOF2 in normale Assoziationen übersetzt. Abbildung 5 verdeutlicht diesen Zusammenhang.
In der Beschreibung der Ausführungssemantik können dann Laufzeitinstanzen erzeugt
werden und über die generierten Propertys auf die Strukturelemente zugreifen. Die Semantik für die Verwaltung von Laufzeitinstanzen wird dabei durch AMOF2 bereitgestellt.
24
3. Spezifikation und Implementierung
Abbildung 5: Abbildung der Runtime-Representation-Of -Beziehung als Assoziation
3.5.1. Laufzeitklassen für das CoSAL-Metamodell
Laufzeitinformationen werden nur für Automateninstanzen benötigt. Wir erweitern das
Metamodell also um die Klasse AutomatonRuntime und fügen die Runtime-Representation-Of -Beziehung als UML-Realization zwischen AutomatonRuntime und Automaton
hinzu (siehe Abbildung 6). Der aktuelle Zustand einer Laufzeitinstanz eines Automaten
wird durch das Property currentState in der Assoziation zur Klasse State gespeichert. Diese Information reicht aber noch nicht für die vollständige Beschreibung des
Laufzeitzustandes. Instanzen von Automaten können sich nämlich unabhängig voneinander in ihren zusammengesetzten Zuständen in verschiedenen Unterzuständen befinden. Zusätzlich können zusammengesetzte Zustände verlassen werden und merken sich
dabei ihren Unterzustand. Der Unterzustand wird dann beim nächsten Erreichen des zusammengesetzten Zustand wieder angenommen. Daher müssen für jede Laufzeitinstanz
eines Automaten in Abhängigkeit der Zustände des Automaten, die Laufzeitinstanzen
von Unterautomaten gespeichert werden. Dazu wird eine reflexive Assoziation für die
Klasse AutomatonRuntime definiert. Über das Property compositeState, dass mit einem Automatenzustand qualifiziert werden muss, kann dann auf diese Laufzeitinstanzen
zugegriffen werden. Die Qualifizierung von Propertys ist ebenfalls eine Erweiterung von
MOF, die nur durch AMOF2 implementiert wird.
Das CoSAL-Metamodell enthält jetzt Elemente, die abstrakte Syntax beschreiben,
und Elemente, die den Laufzeitzustand speichern. Als nächstes kann nun die Ausführungssemantik der Sprache definiert werden.
3.6. Ausführungssemantik
Die Ausführungssemantik wird mit Hilfe operationaler Semantik beschrieben. Konfigurationen entsprechen Modellen, die durch ein Metamodell beschrieben werden. Transitionen werden durch einfache Aktionen auf Modellen realisiert, die den Modellzustand
verändern und dadurch ein Modell in ein anderes Modell überführen. Diese Entwicklung
eines Modells entspricht der Ausführung eines Programms. Die erforderlichen Aktionen
werden durch den MOF-Standard festgelegt. Man kann zum Beispiel Eigenschaften von
Elementen ändern, neue Elemente erzeugen und Elemente vernichten.
25
3. Spezifikation und Implementierung
Abbildung 6: Metamodell erweitert um Laufzeitstrukturen
Für die Beschreibung der Ausführungssemantik unter Verwendung dieser einfachen
Aktionen, benötigt man noch eine Sprache mit der festgelegt werden kann unter welchen Bedingungen und in welcher Reihenfolge die Aktionen ausgeführt werden. AMOF2
erlaubt hier die Verwendung der Programmiersprache Java oder die Modellierung des
Ausführungsverhaltens mit Aktivitäten in MAS, die mit UML-Aktivitäten vergleichbar
sind.
Die Verbindung zwischen Verhaltensbeschreibungen und Metamodell wird über die
Operationen des Metamodells realisiert. Die Ausführung eines Modells beginnt durch den
Aufruf einer Operation an einem Modellelement. Die initiale Operation wird als MainOperation bezeichnet und üblicherweise an einem Modellelement aufgerufen, dass den
äußeren Zusammenhang des Modells bildet. Der Aufruf der Operation bewirkt dann die
Ausführung der verknüpften Verhaltensbeschreibung. Die Ausführung geschieht dabei
immer im Kontext eines Modellelements. Bei der Beschreibung des Verhaltens kann
man auf alle in der Metamodellklasse definierten Eigenschaften zugreifen.
Als nächstes wird die Ausführungssemantik für die Sprache CoSAL definiert. Dazu
wird das Metamodell um Operationen erweitert, die mit Java und MAS implementiert
werden.
3.6.1. Operationen
Die Sprache CoSAL wird für die Ausführung von Automaten benutzt. Im Modell wird
es also einen äußeren Automaten geben an dem die Ausführung beginnt. Deshalb wird
die Klasse Automaton zuerst um die Main-Operation run(inputSequence: String) erweitert. Ihre Aufgabe ist die Erzeugung einer Laufzeitinstanz des Automaten und die
Initialisierung seines Laufzeitzustandes. Danach iteriert sie über die Folge von Eingabezeichen und verändert dementsprechend den aktuellen Zustand des Automaten. Am
Ende, nach dem alle Eingabezeichen konsumiert wurden, vernichtet sie alle Laufzeitinformationen. Für eine bessere Modellierung dieses Verhaltens wird das Metamodell
zusätzlich um die folgenden Operationen erweitert:
26
3. Spezifikation und Implementierung
• AutomatonRuntime::initialise()
Überführt den Automaten vom Startzustand in den ersten echten Zustand.
• AutomatonRuntime::consume(token: String): Boolean
Versucht das Eingabezeichen token über eine aktive Transition zu konsumieren.
Bei Erfolg wird true als Rückgabewerte geliefert und sonst false.
• AutomatonRuntime::incarnateCompositeState(state: State)
Inkarniert den zusammengesetzten Zustand state durch die Erzeugung einer Laufzeitinstanz des zugehörigen Unterautomaten. Die Laufzeitinstanz wird dann über
das Attribut compositeState[state] gespeichert.
• AutomatonRuntime::destroy()
Zerstört die Laufzeitinstanz des Automaten und alle Laufzeitinstanzen von zusammengesetzten Zuständen.
• State::getEnabledTransition(input: String): Transition
Query-Operation, die die aktive Transition für das Eingabezeichen input liefert.
Falls keine aktive Transition existiert, wird null zurückgegeben.
• Transition::fire(context: AutomatonRuntime)
Führt den Zustandsübergang für die durch den Parameter context festgelegte
Laufzeitinstanz des Automaten durch, in dem das Attribut context.currentState
auf den Folgezustand target gesetzt wird.
• Transition::printDebugInfo()
Eine Hilfsoperation, die während der Ausführung Informationen über die Veränderung
des aktuellen Zustand ausgibt.
Eine Abbildung des vollständigen Metamodells inklusive aller Operationen befindet
sich im Anhang.
3.6.2. Verhaltensimplementierung
Ein Modell in der Sprache kann nun ausgeführt werden, indem an einer Instanz eines Automaten die Operation run mit einer bestimmten Folge von Eingabezeichen aufgerufen
wird. Wir werden jetzt einige der Operationen in Java und andere als Aktivitäten in MAS
implementieren. Die Implementierung wird durch das Metamodellierungs-Framework
AMOF2 möglich. Es enthält verschiedene Implementierungsmanager für die Verbindung zwischen Operationen und Verhalten. Der letzte Abschnitt beschäftigt sich mit der
Verwendung der Implementierungsmanager.
Java
Bei der Codegenerierung des metamodellabhängigen Repository im Abschnitt 3.4.4 wurden für Operationen Java-Methoden in den Implementierungsklassen inklusive eines
Code-Skelett erzeugt, das aber natürlich noch kein konkretes Verhalten enthält. Für
27
3. Spezifikation und Implementierung
die Implementierung von Operationen in Java wird dieser generierte Code nun benutzt.
Das Verhalten einer Operation in einer Klasse Class kann implementiert werden, indem
man eine Klasse ClassCustom als Ableitung der generierten Klasse ClassDlg anlegt. Die
Klasse ClassDlg implementiert das Interface Class und benutzt dafür Reflection (siehe
Abschnitt 3.4.2). AMOF2 findet Implementierungen für Operationen durch die Suche
nach Ableitungen von ClassDlg. Die Klasse enthält Methodenrümpfe für Operationen,
die nun in ClassCustom überschrieben werden können. Durch die Spezialisierung kann
bei der Programmierung der Methoden auf alle Eigenschaften von Class zugegriffen
werden.
Die folgende Abbildung zeigt die Klasse AutomatonCustom mit der Operationen aus
der Metamodellklasse Automaton implementiert werden können. In diesem Fall werden
die Operationen run und run(input: String) implementiert.
public class AutomatonCustom extends AutomatonDlg {
@Override
public void run(java.lang.String input) {
AutomatonRuntime runtime = self.metaCreateAutomatonRuntime();
runtime.initialise();
for (int i=0; i < input.length(); i++) {
java.lang.String token = input.substring(i, i+1);
runtime.consume(token);
}
runtime.destroy();
}
...
Zunächst wird eine neue Laufzeitinstanz für den Automaten, an dem die Operation run aufgerufen wurde, erzeugt. Danach wird an der Laufzeitinstanz die Operation
initialise gerufen. Dies führt zur Suche nach einer Implementierung für diese Operation, die dann ausgeführt wird. Im Anschluss werden auf ähnliche Weise nacheinander
die Eingabezeichen über die Operation consume konsumiert.
Die Operation initialise, die von run benutzt wird, ist ebenfalls in Java implementiert.
public void initialise() {
setCurrentState(getMetaClassifierAutomaton().getInitialState());
Transition initialTransition = getCurrentState().getOutgoing().iterator().next();
initialTransition.fire(self);
}
Aktivitäten in MAS
Das Verhalten von Operationen kann auch mit Aktivitäten in der Sprache MAS beschrieben werden. Diese Aktivitäten basieren auf Aktivitäten aus UML, deren Ausführungssemantik in MAS formal spezifiziert wurde. Damit ist es möglich Aktivitäten auszuführen.
28
3. Spezifikation und Implementierung
Aktivitäten bestehen aus Knoten und Kanten. Knoten enthalten Anweisungen (ActionNodes) oder Bedingungen für den weiteren Verlauf der Aktivität (DecisionNodes),
oder speichern Objekte in ObjectNodes, die im weiteren Verlauf referenziert werden
können. Kanten verbinden Knoten. Sie existieren in zwei Ausprägungen. Es gibt Kanten, die den Kontrollfluss von Aktionen, also deren Abfolge, beschreiben (ControlFlows)
und es gibt Kanten, die den Objektfluss zwischen ObjectNodes festlegen (ObjectFlows).
ActionNodes enthalten bestimmte Anweisungen, die auf das Modell zugreifen oder es
verändern. Man unterscheidet verschiedene Arten von Aktionen: eval, call, create, set,
remove und add. ActionNodes können zusätzlich mit InputPins ausgestattet werden, die
Objekte als Argumente für Anweisungen aufnehmen können. Die Rückgabewerte von
Aktionen können auf die gleiche Weise in OutputPins bereitgestellt werden. DecisionNodes beschreiben Bedingungen für den weiteren Verlauf der Aktivität. Dabei wird eine
Bedingung durch einen OCL-Ausdruck beschrieben, der entweder zu wahr oder falsch
ausgewertet wird und den Kontrollfluss entsprechend verzweigt.
Ich werde jetzt die Aktivität, die das Verhalten der Operation consume beschreibt und
in Abbildung 7 zu sehen ist, erklären. Am Beispiel dieser Aktivität werde ich einige Konzepte der Sprache MAS beschreiben und im darauf folgenden Abschnitt 3.7 die Tools
vorstellen mit denen Aktivitäten tatsächlich erstellt werden können. Für weitere Details der Sprache MAS und insbesondere einer genauen Beschreibung der verwendbaren
Aktionen verweise ich auf [SF07].
=currentState =token
call: getEnabledTransition
transition
=transition =self
not transition.oclIsUndefined()
[true]
call: fire
[false]
eval: true
not currentState.subAutomaton.oclIsUndefined()
[false]
eval: false
[true]
=compositeState[currentState] =token
call: consume
return
Abbildung 7: Aktivität für die Operation consume in der Klasse AutomatonRuntime
29
3. Spezifikation und Implementierung
Die Aktivität für die Operation consume wird im Kontext der Klasse AutomatonRuntime
definiert und kann daher alle Attribute und Operationen der Klasse benutzen. Die Abarbeitung der Aktivität beginnt in der ersten Aktion nach dem Startsymbol. Dabei handelt
es sich um eine Call-Aktion, die zum Aufruf der Operation getEnabledTransition führt.
Diese Operation gehört zur Klasse State und soll am aktuellen Zustand aufgerufen werden. Der Aufrufkontext wird daher durch einen Kontext-Pin (erster Pin der Aktion) auf
das Attribut currentState gesetzt. Der zweite Pin ist ein Input-Pin, der das erste Argument für die Operation bereitstellt. Dabei handelt es sich um den Parameter token mit
dem die Operation consume aufgerufen wurde. Die Operation getEnabledTransition
liefert nach ihrer Ausführung als Rückgabewerte entweder eine Instanz von Transition
oder den Wert null. Dieser Wert wird im Output-Pin der Aktion bereitgestellt und
über den dargestellten Objektfluss (blaue Linie) in einem Objektknoten gespeichert.
Dadurch kann der Rückgabewert im weiteren Verlauf der Aktivität durch den angegebenen Namen transition referenziert werden. Die beschriebene Call-Aktion ist per
Kontrollfluss (schwarze Linie) mit einer DecisionNode verbunden. Hier wird der OCLAusdruck not transition.oclIsUndefineded() ausgewertet. Falls der Ausdruck zu
wahr evaluiert, existiert eine entsprechende Transition. Die nachgestellte call-Aktion
ruft dann die Operation fire im Kontext von transition mit der aktuellen Instanz
von AutomatonRuntime (self) als Argument. Sollte der OCL-Ausdruck der DecisionNode zu falsch evaluieren, existiert im aktuellen Zustand keine aktive Transition. Jetzt
wird durch eine weitere DecisionNode geprüft, ob der aktuelle Zustand ein zusammengesetzter Zustand ist. Falls ja, wird an der zugehörigen Instanz von AutomatonRuntime
wieder die Operation consume aufgerufen. Der Zugriff auf die entsprechende Laufzeitinstanz ist über das Attribut compositeState, das über currentState qualifiziert wird,
möglich. Schließlich wird der Rückgabewert der Operation consume in einem speziellen
Objektknoten mit dem Namen return gespeichert. Das Ende der Aktivität wird durch
die entsprechenden Endsymbole dargestellt.
Query-Operationen in OCL
Operationen, die den Modellzustand nicht verändern und lediglich für die Abfrage von
Modelleigenschaften benutzt werden, können als OCL-Ausdrücke implementiert werden. Die Operation State::getEnabledTransition, die in der soeben beschriebenen
Aktivität verwendet wird, ist eine solche Query-Operation. Ihr Aufruf verändert den
Modellzustand nicht und kann somit durch den folgenden OCL-Ausdruck implementiert
werden.
outgoing->select(t | t.input = input)->asOrderedSet()->first()
OCL-Ausdrücke für Query-Operationen werden im Metamodell mit MagicDraw UML
hinzugefügt und während einer Modellausführung von AMOF2 ausgewertet. Für das
Hinzufügen öffnet man in MagicDraw UML die Spezifikation der entsprechenden Operation und aktiviert zunächst im Spezifikationsfenster unter dem Punkt Propertys durch
Auswahl der Option Expert die Sichtbarkeit aller Eigenschaften. Danach wählt man die
30
3. Spezifikation und Implementierung
Eigenschaft Body Condition aus. Hier erzeugt man auf die gleiche Art und Weise, wie
für OCL-Invarianten, ein neues Constraint und hinterlegt dort den OCL-Ausdruck.
Abbildung 8: OCL-Querys mit MagicDraw UML hinzufügen
Eine vollständige Auflistung der Implementierungen für alle anderen Operationen befindet sich im Anhang.
3.6.3. Implementierungsmanager
AMOF2 findet Implementierungen für Operationen über Implementierungsmanager,
die an einem Extent registriert werden. Standardmäßig ist für jeden Extent ein JavaImplementierungsmanager registriert, der die zugehörigen Methoden in den entsprechenden Custom-Klassen findet. Es können aber auch zusätzliche Implementierungsmanager
installiert werden. AMOF2 fragt dann der Reihe nach jeden Manager, ob für die Operation eine Implementierung vorhanden ist und führt diese im Positivfall über den entsprechenden Manager aus. Implementierungen für Operationen können also in vielen
Varianten gleichzeitig vorliegen. Die Auswahl einer konkreten Implementierung erfolgt
durch Angabe und Anordnung der Implementierungsmanager.
Die untere Abbildung zeigt die Registrierung verschiedener Implementierungsmanager
am Modell-Extent für ein Testmodell in der Sprache CoSAL.
Zuerst wird im MASImplementationsManager nach einer Aktivität für die Operation gesucht. Der Implementierungsmanager MultiLevelImplementationsManager stellt
Implementierungen für die zusätzlichen Operationen, die der Verwaltung von Laufzeitklassen dienen, zur Verfügung. Der vorletzte Implementierungsmanager
OclImplementationsManager findet für Query-Operationen den im Metamodell hinterlegten OCL-Ausdruck und führt diesen mit Hilfe des OCL-Prozessors OSLO aus.
ImplementationsManagerImpl ist der Java-Implementierungsmanager.
((ExtentImpl) m1Model.getExtent()).setCustomImplementationsManager(
new ImplementationsManagerContainer(
new ImplementationsManager[] {
new MASImplementationsManager(masContext, env),
new MultiLevelImplementationsManager(m1Model.getFactory()),
new OclImplementationsManager(),
new ImplementationsManagerImpl() }
));
31
3. Spezifikation und Implementierung
3.7. Modellierung der Ausführungssemantik mit MAS
Zur Modellierung von Aktivitäten werden Eclipse-basierte Werkzeuge benutzt. Es existiert ein Modell-Browser, der die Struktur von Metamodellen darstellen kann, und damit
die Auswahl von Operationen erlaubt. Für ausgewählte Operationen können dann Aktivitäten mit Hilfe eines grafischen Editors auf der Basis von GEF erstellt werden. Eine
spezielle Eclipse-Perspektive mit dem Namen Mof Action Semantics Editor öffnet die
erforderlichen Eclipse-Views und ordnet diese sinnvoll an.
3.7.1. MAS-Context
Aktivitäten sind Instanzen des MAS-Metamodells und müssen in einem separaten Extent gespeichert werden. Sie können aber nur in Verbindung mit einem bestimmten
Strukturmetamodell verwendet werden. Diese Verbindung wird als MAS-Context bezeichnet. Der MAS-Context wird in einer masctx-Datei gespeichert. Die folgende Abbildung zeigt die MAS-Context-Datei für CoSAL. Das Strukturmetamodell ist in der
Datei StateAutomaton.syntax.mdxml hinterlegt. Die Aktivitäten für Operationen sind
in StateAutomaton.semantic.xml gespeichert.
syntax = StateAutomaton.syntax.mdxml
semantic = StateAutomaton.semantic.xml
3.7.2. MOF-Modell-Browser
Der MOF-Modell-Browser ist mit AMOF2 implementiert. Er visualisiert die Struktur
der Modelle in den Extents des lokalen Repositorys. Eine grafische Benutzerschnittstelle
erlaubt neben dem Laden von Metamodellen auch das Laden von MAS-Context-Dateien.
Abbildung 9: MAS-Context-Datei laden
Durch das Laden einer MAS-Context-Datei wird das Strukturmetamodell und das
Semantikmodell inklusive MAS-Metamodell geladen. Danach kann man eine Operation
auswählen und durch einen Rechtsklick eine der Aktionen Create Behaviour, Edit Behaviour oder Delete Behaviour wählen (siehe Abbildung 10). Während die letzte Aktion
die Verbindung zwischen Operation und Aktivität löscht, öffnet sich bei Auswahl von
Create oder Edit eine Instanz des grafischen Editors MASE.
32
3. Spezifikation und Implementierung
Abbildung 10: Aktivitäten für Operationen erstellen
Abbildung 11: Aktivitäten mit MASE modellieren
33
3. Spezifikation und Implementierung
3.7.3. MAS-Editor
Mit MASE kann die Aktivität nun modelliert werden (siehe Abbildung 11). Dabei können
die Modellelemente aus der Werkzeugpalette gewählt und auf der Diagrammfläche platziert werden. Der Propertys-View erlaubt für einige Modellelemente das Setzen bestimmter Eigenschaften. Nach Beendigung der Arbeit muss die Aktivität gespeichert werden. Dabei werden auch alle anderen Aktivitäten, die zu dem gleichen Semantikmodell
gehören, gespeichert.
3.8. Modellausführung
3.8.1. Java
Die Ausführung eines Modells wird durch den Aufruf der Main-Operation an einem
Modellelement gestartet. Im betrachteten Beispiel ist dies die Operation run, die an
einem Automaton-Objekt aufgerufen wird. Im einfachsten Fall wurde die Instanz des
Automaten über eine Factory erzeugt und kann direkt benutzt werden.
Automaton automatonX = factory.createAutomaton();
...
automatonX.run();
Modelle können jedoch auch aus XMI-Dateien geladen werden. In diesem Fall muss
der entsprechende Automat zunächst aus dem Modell-Extent extrahiert werden. Dafür
kann die Hilfsmethode query am Extent aufgerufen werden. Sie sucht nach einer Instanz
der Klasse Automaton mit dem Namen X. Das funktioniert natürlich nur für Metamodellelemente, die einen eindeutigen Namen besitzen. Weitere Extent-Methoden erlauben
unter anderem den Zugriff auf alle Modellelemente des Extent.
Automaton automatonX = (Automaton) testModel.getExtent().query("Automaton:X");
automatonX.run();
Auf ähnliche Weise könnte man auch nach Operationen von Metamodellklassen suchen. Da Operationen Instanzen des CMOF-Modells sind, muss nach ihnen im MetamodellExtent gesucht werden. Im unteren Beispiel wird die parameterlose Operation run gesucht.
Operation op = null;
Extent metaExtent = testModel.getMetaModel().getExtent();
Extent cmofExtent = testModel.getMetaModel().getMetaModel().getExtent();
FindOperation: for (Object operationAsObject: metaExtent.objectsOfType(
(UmlClass) cmofExtent.query("Package:cmof/Class:Operation"), false)) {
op = (Operation) operationAsObject;
if (op.getQualifiedName().equals("model.Automaton.run")
&& op.getFormalParameter().size() == 0) {
break FindOperation;
} else {
34
3. Spezifikation und Implementierung
op = null;
}
}
Danach wird die generische Reflection-Methode invokeOperation für den Aufruf der
Operation an einem Automaten benutzt.
ReflectiveSequence<Argument> arguments = new ListImpl<Argument>();
automatonX.invokeOperation(op, arguments);
3.8.2. MAS
Die erforderlichen Schritte für die Ausführung von Modellen mit MAS-Aktivitäten sind
im Gegensatz zu Java-Implementierungen etwas umfangreicher. Zunächst muss die MASContext-Datei geladen werden. Ein Objekt der Klasse MasXmiFiles speichert die Positionen der enthaltenen Modelle relativ zum angegebenen Pfad der MAS-Context-Datei.
MasXmiFiles xmiFiles = new SimpleMasXmiFiles("resources/",
"StateAutomaton.masctx");
Ein MasModelContainer verwaltet die Modelle, die mit Hilfe des MasXmiFiles-Objekt
geladen werden. Das Syntaxmodell wird dabei auf eine spezielle Art geladen. Der Aufruf der Methode loadSyntaxModelForExecution erzeugt für die Runtime-Instance-Of Beziehungen aller Laufzeitklassen die entsprechenden Assoziationen im Metamodell (siehe Abschnitt 3.5). Erst danach können Modelle, deren Semantik diese Assoziationen verwendet, ausgeführt werden. Für das normale Laden des Syntaxmodells gibt es die Methode loadSyntaxModelForEditing. Diese wird zum Beispiel vom MOF-Modell-Browser
für das Anzeigen des Syntaxmodells verwendet.
MasModelContainer masModelContainer = new MasModelContainer(repository);
masModelContainer.loadMasModel(xmiFiles.getMasFile());
masModelContainer.loadSyntaxModelForExecution(xmiFiles.getSyntaxFile(),
"Package:model");
Als nächstes wird das Java-Package-Präfix gesetzt. Da MagicDraw UML in seinem
Format MDXML nicht das Speichern von XMI-Tags erlaubt, müssen diese Informationen
immer wieder neu zum Modell hinzugefügt werden.
masModelContainer.getSyntaxModel().addJavaPackagePrefix(
"hub.sam.stateautomaton");
Jetzt kann ein neuer MasContext für die Modelle im MasModelContainer angelegt
werden. Das Objekt masContext verwaltet die Verbindung zwischen den Modellen. Es
ermöglicht dem MAS-Implementierungsmanager Aktivitäten für Operationen zu finden
und erlaubt dem MOF-Modell-Browser Operationen mit Aktivitäten zu verbinden.
MasContext masContext
= MasRepository.getInstance().createMasContext(masModelContainer);
35
3. Spezifikation und Implementierung
Als nächstes wird ein Testmodell geladen, das im Anschluss ausgeführt werden soll.
MofModelManager testManager = new MofModelManager(repository);
testManager.setM2Model(masModelContainer.getSyntaxModel());
MofModel testModel = testManager.createM1Model("test");
modelFactory testFactory = (modelFactory) testModel.getFactory();
Der MasExecutionHelper installiert die erforderlichen Implementierungsmanager für
die Ausführung von MAS-Aktivitäten im Extent für das Testmodell.
MasExecutionHelper.prepareRun(repository, masContext, testModel);
Das Testmodell wird mit Hilfe der Factory erzeugt und ausgeführt.
Automaton automaton = createLargeTestModel(testFactory);
automaton.run("dbdecacf");
Damit wurde die abstrakte Syntax und die Ausführungssemantik der Sprache CoSAL
mit MOF und MAS spezifiziert und ein Programm für die Ausführung von CoSALModellen in Java implementiert. Man könnte jetzt fortfahren und zum Beispiel einen
grafischen Editor für Modelle erstellen, der auf die gleiche Art und Weise auf das
Modell-Repository in AMOF2 zugreift. Obwohl die Entwicklung von Sprachen mit
dem AMOF2/MAS-Framework durch die Verwendung des CMOF-Metametamodells viele Vorteile bei der Beschreibung komplexer Sprachen bietet, ist die Unterstützung durch
Tools für die Modellierung weiterer Sprachaspekte (zum Beispiel konkrete Syntax) nicht
vorhanden. Andere Ansätze, die diese Unterstützung bieten, werden nun im nächsten
Kapitel mit AMOF2/MAS verglichen.
36
4. Verwandte Arbeiten
4. Verwandte Arbeiten
Dieses Kapitel stellt alternative metamodellbasierte Ansätze für die Spezifikation ausführbarer Sprachen vor.
XMF [CESW04], KerMeta [MFJ05], M3Actions [SE08] und AMOF2/MAS definieren
jeweils eigene Sprachen für eine operationale Semantik, die nur innerhalb dieser Frameworks direkt ausgeführt werden kann. Im Unterschied dazu erlauben Sminco [Sad07]
und EProvide [SW08] [SW] die Beschreibung von operationaler Semantik in vorhandenen Sprachen, die auf Zielplattformen direkt ausführbar sind. Alle anderen Frameworks,
außer XMF, verwenden Eclipse EMF als Metamodellierungs-Framework (eine Implementierung für EMOF in Java). Sie können dadurch von der großen Verfügbarkeit von
Eclipse-Plugins profitieren und auf einfache Weise zum Beispiel grafische Editoren für
Notationen einer Sprache mit GMF bereitstellen. Die einzelnen Ansätze werden nun
etwas genauer vorgestellt.
XMF (eXecutable Metamodelling Facility) basiert auf einer eigenen Metasprache,
XCore, die sich lediglich an den Modellierungskonzepten von MOF orientiert. Für die Beschreibungen der anderen Sprachaspekte stehen weitere Sprachen zur Verfügung. XOCL
(executable OCL) wird für die Beschreibung von operationaler Semantik eingesetzt. Eine textuelle Notation kann mit XBNF, einer Sprache für Grammatiken, beschrieben
werden.
KerMeta erweitert ECore (Bezeichnung des Metametamodells in EMF) um die Beschreibung operationaler Semantik und ermöglicht damit die Ausführung von ECoreModellen. Das Verhalten von Operationen wird mit einer eigenen imperativen Sprache
beschrieben, für die eine textuelle Notation existiert. Bei der Beschreibung wird großer
Wert auf statische Typsicherheit gelegt. Deshalb wurde ECore um zusätzliche Konzepte
erweitert. Zu den Erweiterungen gehören Konzepte für imperative Kontrollstrukturen,
Operationsredefinition, parametrisierbare Klassen und parametrisierbare Operationen.
KerMeta-Modelle sind kompatibel mit ECore-Modellen, da es sich lediglich um eine
Erweiterung handelt. KerMeta erlaubt, im Gegensatz zu AMOF2/MAS, keine Verhaltensbeschreibungen in anderen Sprachen.
Sminco verwendet zwar EMF, aber es beschreibt die Konzepte und die operationale
Semantik einer DSL komplett in der funktionalen Programmiersprache Scheme. EMF
wird in Zusammenarbeit mit GMF lediglich für die Bereitstellung einer grafischen Notation eingesetzt. Dazu wird ein Scheme-Programm in ein ECore-Metamodell transformiert. Das Problem der Co-Evolution zwischen ECore-Modellen und Scheme-Code ist
allerdings noch nicht gelöst. Sminco kombiniert die Vorteile der Metaprogrammierung
mit Scheme und der Metamodellierung mit EMF für die prototypische Entwicklung von
DSLs, die direkt auf Zielplattformen ausgeführt bzw. simuliert werden können. Es ist
aber fraglich, ob eine Semantik-Beschreibung in Scheme für modular aufgebaute komplexe Sprachen praktikabel ist. Sminco ist jetzt ein Teil von EProvide, das als nächstes
vorgestellt wird.
EProvide basiert, genau wie KerMeta, auch auf EMF und erweitert dieses um operationale Semantik. Dabei können verschiedene vorhandene Sprachen, wie zum Beispiel
Prolog, Scheme, Java, Abstract State Machines und QVT, benutzt werden. Die Transitio-
37
4. Verwandte Arbeiten
nen der operationalen Semantik sind als Modell-zu-Modell-Transformationen realisiert.
Die Besonderheit zu allen anderen Ansätzen besteht in der Beschreibung eines kompletten Transformationsschrittes in einer der bereitgestellten Sprachen. Transitionen sind
in EProvide daher eher grobgranular und können viele Eigenschaften des Modells auf
einmal verändern. Bei AMOF2/MAS sind Transitionen als einfache Aktionen realisiert
und damit sehr feingranular.
Die operationale Semantik von M3Actions und die Sprache MAS wurden in einer Kooperation entwickelt. Daher ist dieser Ansatz dem von AMOF2/MAS am ähnlichsten.
Der Hauptunterschied liegt in der Verwendung von EMF als Metamodellierungs-Framework in den M3Actions. Weitere Unterschiede sind, dass die operationale Semantik in M3
durch eine Erweiterung des ECore-Metametamodell definiert wird. AMOF2/MAS definiert die operationale Semantik mit der Sprache MAS, die durch ein Metamodell in M2
beschrieben ist, und verbindet Operationen im Sprach-Metamodell mit Aktivitäten in
MAS. Die Herangehensweisen unterschieden sich also in ihrer physikalischen Umsetzung.
Des Weiteren können Aktivitäten in M3Actions das Verhalten kompletter Klassen oder
Operationen beschreiben, wohingegen AMOF2/MAS nur eine Verhaltensbeschreibung
von Operationen erlaubt. Die Vorteile von M3Actions ergeben sich durch die Verwendung von EMF. Grafische Notationen können mit GMF auf einfache Art und Weise
bereitgestellt werden.
38
5. Zusammenfassung und Ausblick
5. Zusammenfassung und Ausblick
Abschließend möchte ich das Ergebnis der Arbeit kurz zusammenfassen und einen Ausblick geben wie es weitergehen könnte.
Es wurde gezeigt, wie eine einfache Sprache zur Beschreibung und Ausführung von
endlichen deterministischen Automaten, die das Klasse-Instanz-Konzept unterstützen,
spezifiziert und wie ein Programm zur Ausführung von Modellen in Java implementiert
werden kann. Dazu wurde die Implementierung von A MOF 2.0 for Java (AMOF2) vorgestellt und gezeigt, wie ein Modell-Repository benutzt und wie mit Modellen programmiert werden kann. Für die Beschreibung der operationalen Semantik durch Aktivitäten
in MAS wurden einige Eclipse-basierte Tools vorgestellt.
Abgesehen von den MAS-Tools existiert aber keine weitere Unterstützung für AMOF2.
Da AMOF2 mit EMF nicht kompatibel ist, konnte GMF nicht für die Beschreibung
einer grafischen Notationen verwendet werden. Somit konnte auch kein Editor für die
grafische Notation von CoSAL erzeugt werden. Einen Editor hätte man nur umständlich
mit GEF programmieren können. M3Actions bieten dagegen aufgrund ihrer EMF-Basis
eine Anbindung an GMF und sind dem AMOF2/MAS-Ansatz sehr ähnlich. Wenn die
Vorteile von CMOF für die Entwicklung von Sprachen von geringerer Bedeutung sind, als
eine breite Unterstützung durch Sprachtools, dann sind M3Actions für die Spezifikation
einer Sprache besser geeignet. Bei komplexeren Sprachen wird man an den Fähigkeiten
von CMOF allerdings nicht vorbeikommen. Es ist aber fraglich, ob man dann AMOF2
benutzen wird, oder ob EMF einfach um die entsprechenden Konzepte erweitert wird.
39
A. Anhang
A. Anhang
A.1. Metamodell
Abbildung 12: Vollständiges Metamodell
41
A. Anhang
A.2. Ant-Script für die Codegenerierung
<project name="StateAutomaton" basedir="." default="generate-repository">
<property name="src-dir" value="${basedir}/src"/>
<property name="gen-src-dir" value="${basedir}/generated-src"/>
<property name="bin-dir" value="${basedir}/bin"/>
<path id="classpath">
<pathelement path="${bin-dir}"/>
<fileset dir="${basedir}/resources/lib">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="init">
<mkdir dir="${bin-dir}"/>
<mkdir dir="${gen-src-dir}"/>
<typedef name="package" classname="hub.sam.mof.ant.Package"
classpathref="classpath"/>
<taskdef name="generatecode" classname="hub.sam.mof.ant.GenerateCode"
classpathref="classpath"/>
</target>
<target name="clean">
<delete dir="${bin-dir}"/>
<delete dir="${gen-src-dir}"/>
</target>
<target name="generate-repository" depends="clean,init">
<generatecode src="resources/StateAutomaton.syntax.mdxml" md="true"
destDir="./generated-src" instances="true" remote="true">
<package name="model" javaPackagePrefix="hub.sam.stateautomaton"/>
</generatecode>
</target>
</project>
Abbildung 13: build.xml
42
A. Anhang
A.3. Ausführungsverhalten
public void initialise() {
setCurrentState(getMetaClassifierAutomaton().getInitialState());
Transition initialTransition = getCurrentState().getOutgoing().iterator().next();
initialTransition.fire(self);
}
Abbildung 14: AutomatonRuntime::initialise()
outgoing->select(t | t.input = input)->asOrderedSet()->first()
Abbildung 15: State::getEnabledTransition(input: String): Transition
43
A. Anhang
AutomatonRuntime::consume(token: String): Boolean
=currentState =token
call: getEnabledTransition
transition
not transition.oclIsUndefined()
[true]
=transition =self
call: fire
[false]
eval: true
not currentState.subAutomaton.oclIsUndefined()
[false]
[true]
eval: false
=compositeState[currentState] =token
call: consume
return
44
A. Anhang
AutomatonRuntime::incarnateCompositeState(state: State)
Transition::fire(context: AutomatonRuntime)
=currentState.subAutomaton
=ctx =target
create: AutomatonRuntime
set: currentState
eval: self
state
call: initialise
call: printDebugInfo
set: compositeState
not target.subAutomaton.oclIsUndefined() and
ctx.compositeState[target].oclIsUndefined()
AutomatonRuntime::destroy()
[true]
[false]
=ctx =target
eval: metaClassifierAutomaton.state
call: incarnateCompositeState
Transition::printDebugInfo()
<<iterative>>
print: transition executed ...
=s:=
eval: compositeState[s]
print: source state:
print eval: source.name
runtime
[false]
runtime.oclIsUndefined()
[true]
=runtime
print: target state:
print eval: target.name
print: input:
call: destroy
[true]
print: null
input.oclIsUndefined()
[false]
call: metaDelete
print eval: input
45
Literatur
Literatur
[CE00]
K. Czarnecki and U. Eisenecker. Generative Programming. Addison-Wesley,
2000.
[CESW04] Tony Clark, Andy Evans, Paul Sammut, and James Willans. An eXecutable Metamodelling Facility for Domain Specific Language Design. Technical
Report TR-33, University of Jyväskylä, Finland, 2004.
[MET]
Graduiertenkolleg METRIK.
wiki.
http://metrik.informatik.hu-berlin.de/grk-
[MFJ05]
Pierre-Alain Muller, Franck Fleurey, and Jean-Marc Jézéquel. Weaving Executability into Object-Oriented Meta-Languages. In Lionel C. Briand and
Clay Williams, editors, MoDELS, volume 3713 of Lecture Notes in Computer
Science, pages 264–278. Springer, 2005.
[OMG07]
OMG. Unified Modeling Language: Superstructure, V2.1.2. Technical Report
formal/2007-11-02, OMG, 2007.
[Plo81]
Gordon D. Plotkin. A Structural Approach to Operational Semantics. Technical Report DAIMI FN-19, University of Aarhus, 1981.
[Sad07]
Daniel Sadilek. Prototyping Domain-Specific Languages for Wireless Sensor
Networks. In 4th International Workshop on Software Language Engineering
(ateM 2007), 2007.
[Sch03]
Uwe Schöning. Theoretische Informatik - kurzgefasst. 4. Auflage. Spektrum
Akademischer Verlag, 2003.
[Sch05]
Markus Scheidgen. On Implementing MOF 2.0: New Features for Modelling
Abstractions. Technical report, Humboldt-Universität zu Berlin, 2005.
[SE08]
Michael Soden and Hajo Eichler. Ansatz zur Metamodellierung mit Verhalten
und dessen Anwendungen. In Modellierung 2008, pages 243–247, 2008.
[SF07]
Markus Scheidgen and Joachim Fischer. Human Comprehesible and Machine
Processable Specifications of Operational Semantics. In European Conference
on Model Driven Architecture: Foundations and Applications, 2007.
[SW]
Daniel Sadilek and Guido Wachsmuth. EProvide 2.0: an Extensible Framework for Describing Operational Semantics. Publikation ausstehend.
[SW08]
Daniel Sadilek and Guido Wachsmuth. EProvide: Prototyping Visual Interpreters and Debuggers for Domain-Specific Modelling Languages. In Proceedings of the Seventh International Workshop on Graph Transformation and
Visual Modeling Techniques (GT-VMT 2008), 2008.
47
Herunterladen