Über EMF und das CDO-Model-Repository-Framework

Modeling goes Enterprise

In einem Interview mit dem Eclipse Magazin gab Ed Merks interessante Einblicke in Eclipse Modeling als Ökosystem, mit EMF als Kern einer Zwiebelschalenstruktur. Daran anknüpfend taucht dieser erste Artikel einer zweiteiligen Serie tiefer in die verwendeten Technologien ein und beleuchtet dabei vor allem die orthogonalen Aspekte moderner Anwendungen. Das Enterprise-Umfeld stellt hohe Anforderungen an Skalierbarkeit, Verteilung, Persistenz und Transaktionalität, und CDO ergänzt EMF in genau diesen Bereichen.

Die Funktionsweise beinahe jeder vorstellbaren Anwendung lässt sich im Kern auf Eingabe, Verarbeitung und Ausgabe reduzieren. Meistens spielen während der Verarbeitungsphase auch langlebige Daten eine Rolle, die entweder modifiziert werden oder das Verarbeitungsergebnis beeinflussen – oder be

ides. Heute würde man vermutlich eher Business Objects, Business Logic und Presentation Logic sagen. In all diesen Bereichen kann man vom Eclipse Modeling Framework profitieren. Sein pragmatisches Metamodell Ecore ist gut geeignet, als direktes Vehikel für die Modelle aller möglichen realen Konzepte zu dienen. Wenn man dann die ebenfalls zu EMF gehörenden JET-Templates zur Generierung einsetzt, erhält man exzellenten Java-Code für die modellierten Klassen, der sich leicht von Hand an spezielle Anforderungen anpassen und dank semantischem Merge jederzeit regenerieren lässt (JMerge, eine Art intelligente Protected Regions mit „Sinn“ für Grammatik und Symboltabelle von Java). Der generierte Code wird automatisch in handliche Eclipse Plug-ins verpackt, bleibt aber wegen der optionalen Abhängigkeiten auf das Eclipse Runtime auch standalone ausführbar, wie die ebenfalls generierten Modelltests und Modellbeispiele illustrieren.

Den drei Ebenen des Generats – Model Core, Editing Support und Eclipse Presentation – stehen jeweils mächtige Frameworks zur Seite, die nahezu alle technischen Aspekte behandeln, die beim Umgang mit Business Objects eine Rolle spielen. Das reicht von der Vollautomatisierung bidirektionaler Referenzen und Containment über Modellinteraktion via Commands/Adapters bis hin zu modularer Persistenz in Form von Resources, Cross References und XML/XMI als Standardserialisierung. All diese Frameworks sind wohldurchdacht und flexibel erweiterbar. Insbesondere haben sie einen positiven Einfluss auf das Anwendungsdesign, indem sie die Trennung orthogonaler Belange fördern. So muss man bei der Modellierung der Business Objects keinen Gedanken an Fragen der Persistenz verschwenden. Wichtiger noch: diese Belange haben keinen Einfluss auf die Struktur der Modelle, was Letztere wiederum langlebiger macht (Abb. 1).

Abb. 1: Persistenz als orthogonaler Aspekt

Ist das wirklich so? In der Theorie vielleicht, aber in realen Anwendungen kann man das oft nicht so konsequent umsetzen, wie ein Beispiel zeigt: Eigentlich ist Containment ein Attribut logischer Beziehungen zwischen reinen Businesskonzepten und wirkt sich auch direkt auf die in der Benutzerschnittstelle angezeigte Objekthierarchie aus. Bei der Verwendung einer XML-Ressource zur Modellspeicherung landen jedoch alle Objekte, die via Containment vom Wurzelobjekt erreichbar sind, in einer einzigen Datei. Da Textdateien kaum wahlfreien Zugriff ermöglichen, müssen sie also immer komplett gelesen oder geschrieben werden. Das hat fatale Auswirkungen auf die nicht funktionalen Charakteristika der Anwendung. Wenn man jetzt die logischen Containments durch Cross References ersetzt, nur um dem Tree Iterator des XML-Serialisierers ein Schnippchen zu schlagen, dann hat man die Büchse der Pandora bereits geöffnet. Man hat zwar wirkungsvoll die Megadatei in kleine Häppchen zerschnitten, aber wie lange bleiben diese Stückchen klein? Hinzu kommt, dass man für jeden solchen Eingriff mit Zusatzaufwand in der Benutzerschnittstelle bestraft wird, denn man muss die logische Objektstruktur ja irgendwo wieder zusammenkitten. Schließlich könnte man meinen, man hätte mit der Aufteilung des riesigen Objektgrafen in mehrere Ressourcen wenigstens alle technischen Hürden auf dem Weg zu skalierbaren Modellen genommen. Aber weit gefehlt, denn das würde voraussetzen, dass der Java Garbage Collector irgendwann nicht mehr benötigte Objekte wieder aus dem Hauptspeicher räumt, so wie es die Unload Methode im Resource Interface suggerieren mag. Tut er aber nicht!

// Populate a logical model with a cross reference Writer writer = EXTLibraryFactory.eINSTANCE.createWriter(); Book book = EXTLibraryFactory.eINSTANCE.createBook(); book.setAuthor(writer); // Prepare a resource set for XML resources ResourceSet resourceSet = new ResourceSetImpl(); resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap(). put("*", new XMLResourceFactoryImpl()); // Put the writer into a resource Resource writerResource = resourceSet.createResource(URI.createURI("writer.xml")); writerResource.getContents().add(writer); // Put the book into another resource Resource bookResource = resourceSet.createResource(URI.createURI("book.xml")); bookResource.getContents().add(book); // Eject one of the resources writerResource.unload(); System.out.println("Set a breakpoint here and inspect Book::author"); 

Wie man sich leicht überzeugen kann, bleiben nach einem entsprechenden Aufruf alle Java-Referenzen zwischen den Objekten verschiedener Ressourcen unverändert bestehen und verhindern so die Freigabe wertvollen Speicherplatzes (Listing 1). Fairerweise muss man natürlich anerkennen, dass es sich hierbei nicht um Probleme handelt, die erst mit der Einführung von Modeling auftreten. Jede Anwendung wird früher oder später Mühe haben, die klare Eleganz des Businessmodells kompromisslos bis in die Implementierung fortzusetzen. Es gibt eben auch die nicht funktionalen Anforderungen wie Antwortzeiten, Skalierbarkeit, Mehrbenutzerfähigkeit und transaktionale Sicherheit, um nur einige zu nennen. Diese Konzepte werden von EMF zwar nicht direkt adressiert, aber EMF bietet anderen Frameworks offene Schnittstellen zur Lösung der einen oder anderen technischen Herausforderung.

Abb. 2: Typisches CDO Szenario aus der Vogelperspektive

[ header = Seite 2: Eine Laufzeitumgebung für Modelle ]

Eine Laufzeitumgebung für Modelle

Das CDO Model Repository Framework, eine optionale Komponente von EMF, wurde entwickelt, um Modeling auch im Enterprise-Umfeld und im großen Maßstab einsetzbar zu machen (Abb. 2). Der in Bezug auf das Business-Modell voll-transparent erbrachte Mehrwert von CDO lässt sich in die folgenden Kategorien einteilen:

  • Skalierbarkeit: Jedes einzelne Business Object wird grundsätzlich erst bei Bedarf geladen und zuverlässig dem Garbage Collector zugeführt, sobald die Anwendung es nicht mehr referenziert. Gigabyte-große Modelle sind so leicht möglich, ohne dass bereits das Businessdesign darauf Rücksicht nehmen muss.
  • Verteilung: Die lokalen Objekte sind über eine permanente Verbindung zum Model Repository ständig auf dem aktuellen Stand. Es entsteht die Illusion, dass alle Clients auf einem identischen, großen Objektgrafen, einem Distributed Shared Model, operieren. Von diesem (optionalen) Mechanismus stammt übrigens auch der volle Name von CDO: Connected Data Objects.
  • Transaktionalität: Modifikationen des Objektgrafen sind auf unterster technischer Ebene (also nicht nur durch Kooperation der Anwendung wie bei EMF Transactions) transaktionssicher und zwar mit Objektgranularität und standardmäßig optimistischem Locking. Explizite Read oder Write Locks, sowie Save Points und Repository-übergreifende, verteilte Transaktionen gehören auch zum Repertoire von CDO.
  • Persistenz: Die eigentliche Persistenzebene tief im CDO Repository Server ermöglicht die Integration so unterschiedlicher Datenquellen wie relationaler Datenbanken (zum Beispiel via Hibernate), objektorientierter Datenbanken oder Dateisystemen; sogar Web Services wären denkbar.
  • Versionierung: Falls die Implementierung des gewählten Backend Adapters dies unterstützt und das Repository entsprechend konfiguriert ist, sind alle historischen Zustände des Objektgrafen konsistent wiederherstell- und navigierbar. So genannte Audit Views ermöglichen Time Traveling ohne Eingriff in die Businessmodelle oder nennenswerten Mehraufwand in der Anwendung.

All diese Eigenschaften und Funktionen sind entweder transparent oder verbergen sich hinter Programmierschnittstellen, die im Laufe mehrerer Jahre gereift sind. Im Folgenden soll gezeigt werden, wie sich CDO in EMF einfügt, um diese Anforderungen zu erfüllen. Dabei beginnen wir auf der Client-Seite und steigen dann über die Client/Serverkommunikation bis in das Model Repository und die Anbindung externer Datenquellen herab.

Die Anatomie eines CDO-Objekts

Wie inzwischen bekannt, sind die ganz gewöhnlichen Java-Referenzen, die vom EMF-Generator als Instanzfelder in die Subklassen von EObjectImpl generiert werden, letztlich schuld an der Speicherverschwendung. Das einzige Gegenmittel ist ebenso drastisch wie konsequent: Die generierten Klassen dürfen gar keine Fields mehr enthalten. Mittels eines Schalters in der Generatorkonfiguration lässt sich die gesamte Generierung auf so genannte „reflektive Delegation“ umstellen. Danach greifen die generierten Getter und Setter der modellierten Attribute und Referenzen nicht mehr auf Instanzfelder der Klasse zu, sondern delegieren den Zugriff an die Methoden eDynamicGet und eDynamicSet der Superklasse EBasicObjectImpl.

Abb. 3: Skalierbarkeit durch Aufbrechen des Objektgrafen

Genau hier setzt die CDO-Integration mit EMF an. Durch eine weitere Generatoroption wird die Basisklasse für alle Modellobjekte von EObjectImpl in dessen Subklasse CDOObjectImpl geändert. Dadurch übernimmt ein CDO-Objekt die volle Kontrolle über den eigenen Objektzustand und besitzt keine modellbezogenen Fields mehr. Stattdessen speichert jedes CDO-Objekt einen technischen Identifier, die CDOID, sowie einen Verweis auf eine interne Datenstruktur, die CDO Revision, die letztendlich die eigentlichen Modelldaten aufnehmen kann. Eine solche CDO Revision enthält keine Strong References auf andere Objekte oder Revisions, sondern nur noch deren CDOIDs. Sie kann leicht über das Netzwerk transportiert, in Datenbanken gespeichert und fast immer aus dem Hauptspeicher entfernt werden.

[ header = Seite 3: Die nötige Infrastruktur ]

Die nötige Infrastruktur

Damit nicht bei jedem Zugriff auf eine Objektreferenz die dazugehörende Revision vom Server aus der Datenquelle geladen werden muss, sieht das CDO-Framework Caches auf verschiedenen Ebenen vor. Auf der Clientseite hat jede CDO-Session ihren eigenen Revision Cache, der gemäß Voreinstellung aus einem fixed-size LRU Cache und einem nachgeschalteten memory-sensitive Cache besteht. Die eigentlichen CDO-Objekte, die ja nur leere Wrapper mit einer ID sind, werden von so genannten Views beliebig erzeugt, mit der jeweils richtigen Revision verknüpft und in einem eigenen memory-sensitive Cache abgelegt.

Abb. 4: CDO-Objekte und Revisions in verschiedenen Sichten

Welches die richtige Revision ist, hängt dabei nicht nur von der CDOID ab, sondern maßgeblich auch davon, welche Version die View auswählt. CDO unterstützt grundsätzlich drei verschiedene Auswahlstrategien für Revisions, und demzufolge gibt es die drei Sichttypen (Abb. 4):

  • View: eine read-only-Sicht auf den jeweils aktuellsten Zustand des Repositories
  • Audit: eine read-only-Sicht auf historische Zustände, mit änderbarer Zielzeit
  • Transaction: eine read-write-Sicht auf den aktuellen Zustand

Die Daten in einer Revision werden nur während und in einer Transaktion verändert. Wenn die Transaktion am Ende der Bearbeitungssequenz committet wird, wird der Zustand der neuen und geänderten Revisions praktisch eingefroren und mit einer für das Objekt eindeutigen Versionsnummer sowie einer Art Transaktionskennung versehen. Beim erneuten Änderungsversuch innerhalb einer Transaktion wird zunächst eine transaktionale Kopie der Ausgangsrevision erzeugt und intern im CDO-Objekt verlinkt.

Das elegante und reibungslose Zusammenspiel der beteiligten Komponenten wird intern von der CDO State Machine gesteuert. Abbildung 5 zeigt sie in leicht vereinfachter Darstellung. Interessant dabei ist das Remote Invalidate Event, das asynchron vom Repository Server übertragen wird, wenn die CDO-Session auf dem Client dies nicht explizit untersagt. Wenn also eine andere Transaktion durch Änderung eine neue Revision eines Objekts erzeugt, dann werden alle Clients darüber benachrichtigt, und die State Machines ihrer Views ändern den Zustand entweder auf PROXY oder CONFLICT, je nachdem, ob das Objekt lokale Änderungen enthält oder nicht.

Abb. 5: Die möglichen Zustände eines CDO-Objekts

Die Serverkommunikation

Als unteres Ende des CDO Clients in Richtung Repository kann man sich die CDOSession vorstellen. Sie überträgt neue und geänderte Revisions zum Server, wenn Transaktionen committed werden, und sie fordert auch Revisions vom Server an, wenn diese bei Lesezugriffen auf das Modell nicht im Cache gefunden werden. Die gesamte Kommunikationslogik ist in einem Interface abstrahiert, dem CDOSessionProtocol, das sich mit fast beliebigen Transportmitteln implementieren lässt. Die Umsetzung auf Basis der Net4j Signalling Platform, die vor fünf Jahren zunächst eigens für die Anforderungen von CDO entwickelt wurde, ist derzeit die Standard-Implementierung. Net4j bietet u. a.:

  • Einen kompakten und schnellen Multiplexing Kernel, der ausschließlich auf buffered, non-blocking I/O beruht
  • Ein erweiterbares Framework für physische Transportmethoden, mit Connector- und Acceptor-Implementierungen für TCP, HTTP und JVM (direkte Methodenaufrufe innerhalb einer Virtual Machine)
  • Mehrere virtuelle Channels pro physischer Verbindung zur Übermittlung von Buffer-Sequenzen
  • Ein erweiterbares Framework für anwendungsbezogene Kommunikationsprotokolle (CDO implementiert ein solches) auf Stream-Basis
  • Pluggable Authentication, Remote Progress Monitoring und mehr

Für das Verständnis von CDO ist es zunächst ausreichend zu wissen, dass die Client-seitige CDO-Session ihre Revisions und Revision Deltas an das entsprechende Net4j-Protokoll übergibt, das sie auf den Server überträgt und an das richtige Repository weiterleitet. In gleicher Weise, nur mit umgekehrter Richtung, überträgt der Server neue, alte oder geänderte Daten zu den CDO-Sessions in den Clients. Normalerweise geschieht das aber nicht automatisch, sondern das Repository sendet lediglich kurze Nachrichten, die im Client zum bereits beschriebenen Remote Invalidation Event und erst bei Bedarf zum Nachladen der neuen Revisions führt.

[ header = Seite 4: Das Model Repository ]

Das Model Repository

Genau wie ein CDO Client kann ein CDO-Server (Abb. 6) entweder innerhalb von OSGi/Eclipse oder völlig autonom betrieben werden. Auch einer Einbettung in einen JEE Container steht prinzipiell nichts entgegen (eine besonders interessante Option ist übrigens die Einbettung eines CDO Clients in einen Web Container!). In einem Server können, ausreichende Ressourcen vorausgesetzt, beliebig viele Repositories laufen. Jedes einzelne Repository besteht dabei aus zwei Ebenen. Die obere Ebene beheimatet eine Reihe von Managerkomponenten für spezielle Aufgaben. Neben dem Lock Manager, dem Commit Manager, dem Notification Manager und dem Query Manager sind vor allem der Session Manager, der Package Manager und der Revision Manager von Bedeutung. Dazu kommt noch die Möglichkeit, Read und Write Access Handlers am Repository zu registrieren, um zum Beispiel den Zugriff auf Daten zu beschränken oder Fremdsysteme bei Modifikationen zu benachrichtigen. Alle Komponenten dieser Ebene sind noch vollständig unabhängig von dem unterliegenden Persistenzmechanismus.

Abb. 6: Typischer Aufbau eines CDO-Servers

Die untere Ebene eines Repositories dient ausschließlich der Backend-Integration, also der Anbindung an existierende Datenquellen unterschiedlichster Art zur Speicherung und zum Laden von Revisions. Ein solcher Integrationsadapter wird in CDO als „Store“ bezeichnet, und um neue Datenquellen zu integrieren, müssen lediglich wenige Interfaces implementiert werden. Die gängigen Backends kann CDO aber bereits mit Bordmitteln bedienen. Folgende Stores gehören derzeit zum Lieferumfang:

  • MEMStore ist eine transiente Umsetzung des Store-Vertrags. Er wurde hauptsächlich zu Testzwecken entwickelt, kann aber durchaus auch in speziellen Anwendungsszenarien von Interesse sein.
  • DBStore ist der traditionelle, proprietäre O/R Mapper von CDO. Er bietet einige Eingriffsmöglichkeiten in das konkrete Mapping von Klassen auf Tabellen. Im Wesentlichen basiert er auf einer horizontalen Mapping-Strategie, also auf einer Abbildung jeder konkreten Klasse auf eine eigene, in sich abgeschlossene Tabelle. Eigene Mapping-Strategien können wiederum problemlos ergänzt werden. Der DBStore bildet eine Art Referenzimplementierung des Store-Vertrags. Er ist am einfachsten zu konfigurieren, unterstützt zuerst alle Features des Frameworks und sollte folglich zumindest für die Erstevaluierung von CDO verwendet werden.
  • HibernateStore ist eine Anbindung an relationale Datenbanken auf dem Umweg über Hibernate. Er implementiert den Store-Vertrag mithilfe von Teneo, ebenfalls eine Komponente von EMF, und wurde ursprünglich von Martin Taal, dem Vater von Teneo, entwickelt. (Anmerkung des Autors in eigener Sache: Wir sind derzeit aus Zeitgründen auf der Suche nach neuen Adoptiveltern für die Hibernate-Integration und würden uns in diesem Zusammenhang sehr über Interesse bei Hibernate-Experten freuen.)
  • ObjectivityStore bindet CDO Repositories an die objektorientierten Datenbanken der gleichnamigen Firma an. Diese Integration wurde von Simon McDuff, einem Mitglied des CDO-Teams, entwickelt und ist beim kanadischenVerteidigungsministerium erfolgreich im produktiven Einsatz. Leider haben lizenzrechtliche Bedenken eine Veröffentlichung des ObjectivityStores bis jetzt verhindert. Wir stehen diesbezüglich in freundschaftlichem Kontakt mit Objectivity Inc., wo man an einem neuen, kostenfreien Lizenztyp speziell für die Entwicklung mit CDO und EMF arbeitet.

Jede Teilkomponente eines CDO-Servers ist eine ganz normale Java Bean, die klare Interfaces implementiert und bei Bedarf einfach ausgetauscht werden kann. Die Verdrahtung und Konfiguration zur Laufzeit kann auf vielfältige Weise erfolgen. Die CDO-Distribution enthält Beispiele für manuelle Verdrahtung, Spring Application Contexts, einen Extension-Registry-basierten, proprietären Wiring-Container und eine Headless OSGi Application, die die Konfiguration aus einer XML-Datei liest und dann über die OSGi Console steuerbar ist. Alle Komponenten von CDO und Net4j sind in ein Operations-and-Maintenance-Framework eingebunden, das wichtige Aufgaben wie Tracing, Logging, Progress und Performance Monitoring, Preferences und Lifecycle Management abstrahiert und nahtlos in die jeweiligen Services einer konkreten Laufzeitumgebung einbindet.

EMF ist toll!

Ich hoffe, dass ich eine verständliche Einordnung von Modeling in den Enterprise-Kontext geben und aufzeigen konnte, welche Aufgabenbereiche EMF und CDO dabei abdecken. Die Eingangsfrage beantworte ich mit einem klaren: „Ja, EMF ist toll“, und zwar nicht, weil es alle Mittel zum Einsatz von Modeling im großen Maßstab bereits mitbringt, sondern weil es dem Anwender und Frameworks wie CDO elegante Schnittstellen zum Einklinken aller fehlenden Funktionalitäten bietet.

Nachdem Nutzen und Aufbau von CDO beleuchtet wurden, fehlen noch einige praktische Übungen und Codebeispiele. Im zweiten Teil dieser Artikelserie werde ich konkret zeigen, wie man ein EMF Model für CDO aufbereitet, wie man ein CDO Repository startet und wie man Code gegen die CDO APIs schreibt. Dabei werden neben den Grundlagen wie dem Öffnen von Sessions oder dem Arbeiten mit Transactions auch viele der neuen Features wie Resource Folders, Query Framework, Explicit Locking, Save Points, Passive Updates und Change Subscriptions gestreift. Abrunden werde ich die Serie mit einem Ausblick auf die kommenden Aktivitäten in den Bereichen Offline Mode, Legacy Model Support und OCL als Common Query Language auf dem Server, sowie Integrationen mit EFS, Team Support, Common Navigator und GMF. Bis dahin freue ich mich über Besucher auf der brandneuen CDO-Homepage, in meinem Blog über Eclipse und Modeling, sowie auf spannende Diskussionen in der Newsgroup.

Eike Stepper ist der ursprüngliche Autor von CDO und Net4j und leitet das inzwischen siebenköpfige, internationale Committer-Team. Mit seiner 1991 gegründeten Firma ES-Computersysteme konnte er in vielen Projekten Erfahrungen als Entwickler und Berater sammeln. Themen wie Modeling, OSGi-Softwaredesign und „gutes Entwickeln“ faszinieren ihn immer wieder.
Geschrieben von
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.