Samstag, 29. Mai 2010

Workflow eines Xtext-Projektes

Beim letzten Mal haben wir uns mit der Erstellung einer Sprache in Xtext beschäftigt, genauer gesagt mit der Verwendung der Xtext Grammatik zum Erstellen eines eigenen Modells, im Grunde genommen also mit dem Kern der DSL.

In diesem Beitrag würde ich gern den Workflow beschreiben, welchen das Xtext-Projekt durchläuft. Ich hoffe, damit erst einmal ein grundsätzliches Verständnis für meine Pläne zur Umsetzung eines Akzeptanztest-Systems, welche ich in den kommenden Posts vorstellen möchte, aufbauen zu können. In dem System werden sich einige Elemente wiederfinden, denen man auch in Xtext begegnet.

Erzeugung der Artefakte mit der Modeling Workflow Engine (MWE)

Sobald man ein Xtext-Projekt angelegt hat, so findet sich neben der Grammar-Datei mit der Dateiendung xtext eine Workflowdatei mit dem Namen Generate[Sprachname].mwe:

XtextWorkflowDSLProject

Im Bild sieht man den Package-Explorer mit der Ansicht des auch schon im letzten Post verwendeten Projekts. Neben der Workflow-Datei gibt es noch eine Properties-Datei mit einigen projektspezifischen Eigenschaften, welche der Workflow verwendet.

Der Inhalt solch eines Workflows soll uns an dieser Stelle nicht interessieren, dies würde den Umfang des Posts sprengen. Was man wissen muss, ist, dass ein MWE-Workflow einen sequenziellen Aufruf von verschiedenen Komponenten zur modellgetriebenen Entwicklung ermöglicht, daher "Workflow". Das Ganze geschieht in einer XML-basierten deklarativen Notation, außerdem ist es möglich, eigene Komponenten zu schreiben. Mehr dazu findet man auf der MWE-Seite im Eclipsepedia. Ich werde auf diese Technologie an einem anderen Zeitpunkt genauer eingehen.

Führt man den Workflow aus (Rechtsklick –> Run as –> MWE Workflow), so erzeugt Xtext alle benötigten Artefakte, vom Ecore-Modell über das EMF-GenModel (welches wiederum den Java-Code erzeugt) bis hin zu den UI-spezifischen Komponenten wie Editor und Contentassistance.

Diesen Vorgang muss man jedes mal wiederholen, sobald man etwas an der Grammatik geändert hat.

Generiertes UI-Plugin/Projekt

Das vom Xtext-Projekt Assistenten erzeugte Projekt mit der Endung ui wird erst nach dem ersten Durchlauf des Workflows mit Inhalten gefüllt:

XtextWorkflowUIProject

Hier hat man die Möglichkeit, den Proposalprovider anzupassen, einen Labelprovider zu erstellen etc., also sein Plugin für die Sprache anzupassen. In meinem Projekt für die Erstellung eines Akzeptanztest-Systems wird hier ein Wizard hinzukommen, der die Erstellung des Projektes oder auch von Akzeptanztests unterstützt.

Über die plugin.xml-Datei kann man das Plugin starten und den automatisch erstellten Editor benutzen. Man erstellt dann lediglich ein neues, leeres Projekt und eine Datei mit der vorher festgelegten Endung:

XtextWorkflowNewProject

Hier zeigt sich schon eine hervorragende Möglichkeit zur Erweiterung des Plugins: das Akzeptanztest-System könnte durch einen Assistenten die Möglichkeit zum automatischen Erstellen einer DSL-Datei mit der richtigen Endung zur Verfügung stellen.

Generiertes Generator-Projekt (optional)

Zusätzlich zu dem DSL-Projekt und dem UI-Projekt kann man sich ein Generator-Projekt (endet mit .generator) angelegen lassen. Im Xtext-Projekt-Wizard muss man dazu an der entsprechenden Stelle ein Häkchen setzen.

Das erzeugte, eigenständige Projekt dient dazu, aus dem durch Xtext erzeugten Modell Code zu generieren. Damit ist nicht nur Java-Code gemeint, sondern prinzipiell irgendein Code, dies könnten zum Beispiel ein XML-Dokument sein oder SQL-Statements, je nachdem, welchem Zweck das zugrundeliegende Modell dient.

Um dieses Ziel umzusetzen, bedient man sich hier der Codegenerierungssprache Xpand. Der Projekt-Wizard erzeugt im Generator-Projekt bereits einige Standard-Dateien, wie eine DSL-Datei der Xtext-Beispiel-DSL "Entities" und ein Xpand-Template, welches auf diesem Modell arbeitet:

XtextWorkflowGenProject

Des weiteren gibt es hier ein Xtend-Datei, welche von dem Xpand-Template eingebunden wird. Mit Xtend kann man Erweiterungen zu seinem Model schreiben, ohne das Modell zu ändern, ebenso wie eigens definierte Operationen, welche das Modell benutzen. Genauer möchte ich darauf nicht eingehen, da es für meine Zwecke nicht so wichtig ist. Dennoch sollte es nicht unerwähnt bleiben.

Die Beispieldateien würde man löschen und gegen das eigene Modell ersetzen. Was man nun mit Xpand machen kann, werde ich in einem neuen Post zeigen.

Aufgerufen wird Xpand wiederum durch eine Workflow-Datei, welche sich im Projekt befindet. Insgesamt hat dies jedoch wenig Sinn, wenn es nicht aus Plugin aus aufgerufen wird. Wie würde man also dazu vorgehen?

Man startet das Plugin über das UI-Projekt und legt in der neuen Workbench ein Projekt an. Dieses Projekt muss als Dependency das Generator-Plugin besitzen, um auf den Generator und die Xpand-Templates zugreifen zu können. Der im Generator vorhandene Workflow kann nun aus einem eigens erstellten Workflow aufgerufen werden. Dieses Beispiel stammt von der Seite von Peter Friese:

<workflow>
<cartridge file="workflow/EntityGenerator.mwe" model="classpath:/model/MyModel.entity"/>
</workflow>

Der Generator wird über den Workflow aufgerufen und verwendet dann die in der aktuellen Workbench erzeugte DSL-Datei, anstatt die, welchem im Generator-Projekt erzeugt wurde.

In dem oben genannten Artikel von Peter Friese findet man noch mehr Informationen zur Code-Generierung mit Xpand.

Src gegen Src-gen

Jedes der angelegten Projekte verfügt über zwei verschiedenen Source-Ordner. Einmal src, wo sich die Dateien befinden, die der Entwickler selbst ändert. Dann gibt es den Ordner src-gen, in welchem sich fast ausschließlich von Xtext erstellter Code befindet, wie zum Beispiel die Java-Klassen zum erzeugten Modell. Diese Dateien sollte man möglichst nicht verändern, da sie vom Xtext-Generator eh wieder überschrieben werden. Ebenso sollte man eigene Klassen und sonstige Dateien wie eigene Workflows stets in den src-Ordner legen.

Validation

Auch wenn man seine Xtext-Grammatik optimal angelegt hat, so kann man nicht immer vorhindern, dass die DSL eventuell an bestimmten Stellen Inhalte akzeptiert, welche man da eigentlich nicht haben wollte. Auch durch Referenzen kann dieses Problem oft nicht gelöst werden.

Die Lösung bietet ein von Xtext angebotenes Validator-Fragment, welches dem Workflow zur Generierung der Artefakte hinzugefügt wird. Letztendlich beschreibt man in einer Java-Klasse Methoden, welche Überprüfungen von einzelnen Regeln der Grammatik anstellen. Was man genau in diesen Methoden überprüft, ist jedem selbst überlassen.

Genaueres dazu wird es in einem kommenden Post von mir geben, da ich schon mehrfach Gebrauch von dieser Funktion gemacht habe. Inzwischen finden sich Informationen darüber i, Xtext User Guide in Chapter 5 – Unterpunkt "Validation".

Fortsetzung folgt…

In meinem nächsten Post habe ich vor, bereits einmal die prinzipiellen Ablauf, welchen ich mir für das Akzeptanztest-System vorgestellt habe, zu erläutern. Weitere Posts zu Validation und anderen Details möchte ich dann bereits an diesem System demonstrieren.

Mittwoch, 26. Mai 2010

Verwendung von XText

Mit diesem Post (und den nächsten) möchte ich ein Gefühl für die Verwendung von XText vermitteln. Dabei werde ich erst einmal auf die Erstellung der Grammatik eingehen, später auf den Workflow zur Erzeugung der Artefakte und des Plugins. Dies wird eine Basis bieten, um später die Konzepte für den Aufbau des Akzeptanztestsystems zu erläutern.

Dennoch wird es hier keine vollständige Anleitung für XText geben. Die Details kann man in der User Doku zu XText nachlesen. Hier findet man alle Möglichkeiten der Grammatik von XText und Informationen zu weiteren Konzepten, welche auch hier kurz angeschnitten werden sollen.

Außerdem empfehle ich für Beispiele, aber auch spezielle Thematiken rund um XText die Blogs von Sven Efftinge und Peter Friese. Da erfährt man, wie man zum Beispiel vorhandene Ecore-Modelle in XText importiert oder wie man mithilfe von XPand aus einer DSL Code generiert, was auch mir mittlerweile sehr geholfen hat.

Erstellung der Grammatik

Zunächst einmal habe ich eine einfache Grammatik für eine DSL zur Beschreibung von Hotels erstellt. Es ist zwar sehr einfach gehalten, sollte aber deshalb auch leicht nachzuvollziehen sein. Nachdem man ein neues XText Projekt angelegt hat (siehe XText User Guide), kann man die automatisch erstellte Grammatik folgendermaßen abändern:

grammar org.xtext.example.Testdsl with org.eclipse.xtext.common.Terminals

generate testdsl "http://www.xtext.org/example/Testdsl"

Model :
(hotels+=Hotel)*
(zimmer+=Zimmer)*
(gaeste+=Gast)*;

Hotel:
'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'
'Gaeste' '{' (gaeste+=[Gast])+ '}';

Zimmer:
Einzelzimmer | Suite;

Einzelzimmer:
'Einzel' name=ID;

Suite:
'Suite' name=ID;

Gast:
'Gast' name=ID
'Vorname' vorname=STRING
'Nachname' nachname=STRING;

Die Grammatik beginnt mit ihrem Namen und dem Einbinden der von XText bereitgestellten Terminals. Diese Terminals, wie im Beispiel zu sehen ID, STRING und weiterhin INT sowie andere sind in einer eigenen Grammatik definiert, welche man meistens in seiner Grammatik mit verwenden möchte. Nach dem selben Prinzip lassen sich auch beliebige andere Grammatiken einbinden.

Das Schlüsselwort generate stellt den Ausgangspunkt eines wichtigen Prinzips von XText dar. Hier wird der Name des aus der Grammatik zu erstellenden Ecore-Modells angegeben. In Ecore wird das durch die DSL beschriebene Modell festgehalten, woraufhin man dieses mit den Möglichkeiten des Eclipse Modeling Frameworks weiter bearbeiten kann. Es ist auch möglich, auf die Generierung zu verzichten und stattdessen ein Modell zu importieren, bzw. eine Kombination davon. Darauf wollen wir an dieser Stelle erst einmal verzichten.

Mit dem Hintergedanken an das Modell, welches schließlich erzeugt wird, kann man nun mit Hilfe der Grammatik verschiedenen Regeln beschreiben, welche das Modell beschreiben, aber auch das Aussehen der DSL. Die EBNF-ähnliche Form der Grammatik (EBNF – Erweiterte Backus-Naur-Form) kann man relativ einfach nachvollziehen.

Die Regel Hotel zeigt grundlegende Funktionen der Grammatik:

Hotel:
'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'
'Gaeste' '{' (gaeste+=[Gast])+ '}';

Mit Anführungszeichen eingeschlossenen Wörter gelten als Schlüsselwörter in der DSL, welche fest verankert sind und immer an ihrem vorbestimmten Platz vorkommen müssen. Sind sind lediglich dazu da, der DSL eine Struktur zu geben und sie leichter lesbar zu machen. In das Modell werden sie nicht übernommen.

Eine Regel in der DSL beschreibt meist eine Klasse im Modell. Jede Klasse bekommt bestimmte Eigenschaften oder auch Attribute, welche jeweils einen bestimmten Wert haben. In XText spricht man hier auch von Referenzen, welche die einzelnen Regeln im Syntaxbaum miteinander verbinden. Hotel besitzt ein Attribut (um in der Objektwelt zu bleiben) mit dem Namen name, welchem ein bestimmter Wert zugewiesen wird. Dieser wird hier durch die Terminal-Regel ID gebildet. Beim Parsen wird später der Inhalt dieser Regel hinter dem Schlüsselwort Hotel eingefügt, wobei es sich bei ID um eine Kombination aus Ziffern und Buchstaben handeln kann. Ein anderes Beispiel sind vorname und nachname in der Regel Gast.

Möchte man eine Liste erstellen, das heißt, kann das Attribut mehr als einen Wert annehmen, so wird dabei das = durch += ersetzt. Diesen “add”-Operator kennt man aus einigen Programmiersprachen. Ein Beispiel sieht man in der obersten Regel Model:

Model :
(hotels+=Hotel)*

Hier wird zum Beispiel beschrieben, dass die Regel Hotel mehrmals hintereinander existieren kann.

Kardinalität von Elementen

XText bietet mehrere Optionen an, um die Kardinalität, sprich die Anzahl der Elemente anzugeben:

  • keine Angabe – genau ein mal
  • ? – Maximal einmal, oder nicht vorhanden (kennzeichnet Optionalität)
  • + – bei mehreren Elementen, 1 mal oder mehr
  • * – bei mehreren Elementen, 0 oder mehr

Möchte man beispielsweise name aus dem oberen Beispiel optional werden lassen, so würde man es so beschreiben:

'Hotel' (name=ID)?

Beispiele für Listen mit mindestens einem oder mindestens 0 Elementen findet man in der Beispiel-DSL mehrfach. So muss ein Hotel mindestens ein Zimmer besitzen, jedoch kann das Modell auch gar keine Hotels oder Gäste besitzen.

Cross References

Bis jetzt haben wir uns nur mit dem Zuweisen von Regeln beschäftigt. An der entsprechenden Stelle wird die Regel vom Parser eingesetzt. Man kann jedoch auch Objekte durch Regeln an einem Ort beschreiben und diese dann wieder an einem anderen Ort wiederverwenden. Ein Beispiel:

'Hotel' name=ID
'Zimmer' '{' (zimmer+=Zimmer)+ '}'

Würde man die Regel in dieser Weise ausführen, so müsste man die Zimmer direkt in die geschweiften Klammern des Hotels schreiben, also:

Hotel hotel1
Zimmer { Einzel ... }

Geht man wieder zu der oben verwendeten Schreibweise zurück, so kann man an dieser Stelle Referenzen verwenden. Diese werden dadurch gekennzeichnet, dass man die entsprechende Regel in eckige Klammern setzt. Dies bedeutet dann so viel wie: hier wird ein Objekt/ eine Instanz vom Typ X eingesetzt.

'Hotel' name=ID
'Zimmer' '{' (zimmer+=[Zimmer])+ '}'

Nun würde man vorher ein Zimmer definieren, ihm einen Namen geben und diese Referenz dann verwenden.

Suite zimmmer1

Hotel hotel1
Zimmer { zimmer1 }

Wichtig hierbei ist: um eine Referenz wieder zu finden, muss die entsprechende Regel ein Attribut mit dem Namen name besitzen, so wie es schon mehrfach in der Beispielgrammatik vorkommt. Ich denke, dies hängt direkt mit dem Ecore-Modell zusammen. Die meisten Elemente des Ecore Meta-Modells erben von einer Klasse namens ENamedElement, welche für alle Elemente ein Attribut name definiert und sie so identifizierbar macht. Alle anderen Attribute, die nicht zur Referenzierung verwendet werden, kann man natürlich beliebig benennen.

Verzweigungen / Alternativen

Oben in der Grammatik gibt es eine Verzweigung bei Zimmer, wobei die Regel Zimmer zwei Alternativen bietet: Einzelzimmer oder Suite.

Zimmer:
Einzelzimmer | Suite;

Ob diese Konstellation so sinnvoll ist oder nicht sei dahingestellt, jedoch lässt sich hier gut der Sinn solch einer Verzweigung darstellen. Sieht man das Ganze wieder im Hinblick auf das später erzeugte Modell, so erzeugt man hier eine Art der Vererbung. Zimmer ist die Superklasse und Einzelzimmer bzw. Suite sind die spezialisierten Klassen. In unserem Beispiel macht dies nicht so ganz Sinn, denn beide fügen keinerlei Spezialität hinzu sondern besitzen nur das gleiche Attribut name.

Hierbei sieht man aber eine Besonderheit im Aufbau solch einer Hierarchie. Alle gleichen Attribute, das heißt, eigentliche solche, die in die Superklasse kommen würden, müssen hier jeweils in den Regeln aller Kindklassen vorkommen. Die Regel Zimmer ist hier nur dazu da, die Verzweigung zu bestimmen. Im später erzeugten Modell werden dann alle gleichen Attribute weiter oben angesiedelt, wie man im folgenden Abschnitt sehen wird.

Eine weitere Einsatzmöglichkeit solcher Verzweigungen ist, den Ablauf der Sprache etwas beliebiger zu gestalten, sodass man beispielsweise Hotels, Zimmer und Gäste nicht in der genauen Reihenfolge beschreiben muss, sondern diese durch eine Verzweigung einer gemeinsamen Superklasse zuordnet, wodurch es möglich wird, die Reihenfolge völlig beliebig zu gestalten.

Das Modell hinter der Grammatik

Ohne momentan den Workflow zur Erstellung des Modells zu betrachten (das kommt beim nächsten Mal), möchte ich gern einmal so ein Modell vorstellen, und zwar genau das, welches durch die oben beschriebene Grammatik erstellt werden würde.

Ecore-Modell für das Beispiel

In dem Ecore-Modell wird für jede Liste eine EReference (gekennzeichnet durch den Pfeil am Symbol) angelegt, für jedes einzelne Attribut ein EAttribute. Wie man hier sieht, wird die Verzweigung bei Zimmer dadurch aufgelöst, dass hier Unterklassen gebildet werden. Alle gemeinsamen Attribute kommen dabei in die Superklasse, was in unserem Fall nur name ist.

Dies ist noch ein sehr einfaches Beispiel. Wir werden sehen, dass im Laufe der Entwicklung des AKzeptanztest-Systems ein sehr viel komplizierteres Modell herauskommen wird, vor allem da ein Modell nötig sein wird, welches nicht nur eine Domäne wie hier “Hotel”, sondern beliebig viele unterstützen muss.

Ein konkretes Beispiel zum Abschluss

Zum Schluss folgt ein konkreter Text in der oben vorgestellen DSL:

Hotel hotel1
Zimmer { zimmer1 zimmer2 }
Gaeste { gastMuster }

Einzel zimmer1
Suite zimmer2

Gast gastMuster
Vorname "Max" Nachname "Mustermann"

Hier erhält man nun einen Eindruck, was aus der oben beschriebenen DSL werden kann. Man beachte die Reihenfolge insbesondere der Schlüsselwörter und die Verwendung der Referenzen. Im Laufe der Zeit werde ich in diesem Blog immer mal wieder einzelne Details von XText vorstellen, welche zur Umsetzung des Akzeptanztest-Systems nötig sind.

Im nächsten Post habe ich vor, die Erstellung eine DSL aus der Sicht des Workflows zu betrachten, welchen man durchführen muss, um aus der Grammatik ein Modell zu machen und ein Plugin zu erzeugen, mit welchem man dann einen konkreten Text in der DSL schreiben kann.

Donnerstag, 20. Mai 2010

Installation von XText

Zur Installation von XText muss man eigentlich nicht viele Worte verlieren. Auf der Website von itemis gibt es eine vorgefertigte Eclipse Distribution inklusive XText und allen Abhängigkeiten.

Wer sich die Plugins manuell in sein bereits vorhandenes Eclipse einbauen möchte, dem möchte ich momentan davon abraten. Die Links zu den Plugins befinden sich auf der Downloadseite vom Textual Modeling Framework, ebenso die Adresse der Update-Site (diesen Weg sollte man sowieso stets vorziehen). Auch nachdem ich XText über den Update-Manager eines neu aufgesetzten Standard-Eclipse installiert hatte, erschien folgende Fehlermeldung beim Anlegen eines neuen XText Projekts:

Fehler beim Anlegen eines neuen XText Projects

Hierzu gab es keine Erklärung. Alle nötigen Plugins waren vorhanden, wie ein Vergleich mit der vorgefertigten Distribution zeigte. Deshalb möchte ich auch dazu raten, diese zu verwenden.

Ich werde der Sache weiter nachgehen, vielleicht kann ich dann hier eine Lösung dazu anbieten. Im nächsten Post werde ich kurz demonstrieren, wie die Erstellung einer DSL in XText abläuft, bevor sich demnächst alles um die Implementierung des Akzeptanztestsystems drehen wird.

Anmerkung: Alle hier gemachten Angaben beziehen sich auf Eclipse 3.5 Galileo und Version 0.7.2 von XText.

Mittwoch, 19. Mai 2010

Warum XText?

Für die Verwendung des XText-Frameworks zur Umsetzung des Fachsprachsystems gibt es mehrere gute Gründe. Einerseits lassen sich damit externe DSLs (Domain Specific Languages) erstellen. Diese können eine völlig neu definierte Syntax besitzen, was von Vorteil ist, wenn so eine Sprache von einem Domänen-Experten benutzt werden soll und nicht von einem Softwareentwickler. Im Gegensatz dazu ist man bei einer internen DSL von deren Wirtsprache abhängig. Beim Testen von verschiedenen Ansätzen hat sich XText als sehr geeignet für das System herausgestellt, wodurch es dabei geblieben ist. Deshalb wird hier nun nicht weiter auf interne DSLs eingegangen.

Wer mehr über interne und externe DSLs erfahren möchte, dem empfehle ich die Homepage von Martin Fowler. Hier findet man einen kurzen Artikel über DSLs; mehr Informationen findet man in seinem Work-In-Progress Buch "Domain Specific Languages", welches auch über die Seite erreichbar ist.

Das Einsatzgebiet das Fachsprachsystems für Akzeptanztests sind datenbankbasierte Applikationen. Da XText sehr eng mit dem Eclipse Modeling Framework (EMF) integriert ist, kann man die gesamte damit verbundene Technologie zur modellgetriebenen Entwicklung mit verwenden, von der Erstellung eines Domänen-Modells bis hin zur Persistenz. Diese Schritte werden nach und nach in diesem Blog dokumentiert werden.

Davon abgesehen ist die Verwendung einer Plattform wie Eclipse im Allgemeinen von Vorteil. Hier besteht die Möglichkeit, ein Plugin oder eine RCP-Anwendung zu erstellen, mit welchem der Domänen-Experte Tests formulieren kann und der Softwareentwickler diese in seiner Arbeitsumgebung verwenden kann bzw. die Funktionalität dazu implementiert.


Zusammenfassung

Weshalb verwende ich XText:
  • Externe DSL mit frei definierbarer Syntax
  • Anbindung an Eclipse Modeling Framework
  • Nutzung der Eclipse Plattform
Alles zu XText findet man auf dessen Projektseite auf Eclipse.org. Im nächsten Post möchte ich die Installation von XText etwas genauer beschreiben.

Ziel dieses Blogs

Dieser Blog hat das Ziel, meine Arbeiten an einem System zur Formulierung von Akzeptanztests zu dokumentieren.

Akzeptanztests stellen ein geeignetes Mittel dar, um die Anforderungen an eine zu entwickelnde Software zu ermitteln sowie deren Erfüllung zu überprüfen. Dabei findet ein intensiver Austausch zwischen Softwareentwickler und Dömanen-Experten statt. Das Problem hierbei ist, dass Akzeptanztests in einer Programmiersprache geschrieben werden und daher für den Auftraggeber oder den Domänen-Experten nicht oder nur schwer verständlich sind.

Um die Formulierung von Akzeptanztests in der Sprache des Domänen-Experten zu unterstützen, soll ein System entwickelt werden, welches eine Fachsprache zur Verfügung stellt, um die Beschreibung des Anfangs- und Endzustands eines Tests und dessen Testdaten zu ermöglichen.

In diesem Blog möchte ich die Fortschritte und Problemstellungen bei der praktischen Umsetzung solch eines Systems dokumentieren. Für die Umsetzung habe ich mich für XText entschieden, welches in Verbindung mit dem Eclipse Modeling Framework (EMF) bereits ein mächtiges Werkzeug bildet. In meinem nächsten Post werde ich die Vorteile von XText beschreiben, außerdem wird es eine kleine Anleitung zur Installation und Verwendung von XText geben.