Wissenserwerb für ressourcenorientiertes Konfigurieren
Transcrição
Wissenserwerb für ressourcenorientiertes Konfigurieren
Humboldt-Universität zu Berlin Institut für Informatik Unter den Linden 6 10099 Berlin Diplomarbeit Wissenserwerb für ressourcenorientiertes Konfigurieren vorgelegt von: Andreas Kuntzagk Betreuer: Prof. Dr. Joachim Fischer Martin von Löwis of Menar Berlin, 13. März 2001 Inhaltsverzeichnis Einleitung Inhalt der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 1 Modellieren und Konfigurieren modularer technischer Systeme 1.1 Einführung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Strukturorientiertes Konfigurieren . . . . . . . . . . . . . . . . . . . . . 1.3 Ressourcenorientiertes Modellieren / Konfigurieren . . . . . . . . . . . 2 2 4 7 2 Das Konfigurationssystem des SCO-Projektes 2.1 Konfigurationsaufgabe . . . . . . . . . . . . . . . . . . . 2.2 Das zugrunde liegende Metamodell . . . . . . . . . . . . 2.3 Aufbau des Konfigurationssystems . . . . . . . . . . . . 2.4 Die Komponentenbeschreibungssprache . . . . . . . . . 2.5 Bisheriges Modellierungsverfahren . . . . . . . . . . . . 2.6 Darstellung des Komponentenkatalogs als HTML-Seite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 14 16 19 19 22 22 3 Das Werkzeug zum Wissenserwerb 3.1 Alternative Komponentenkataloge . . . . . . . . . . . . 3.2 Techniken für Benutzerschnittstellen . . . . . . . . . . 3.3 Aufbau des Werkzeuges . . . . . . . . . . . . . . . . . . 3.4 Implementierung des Katalogzugriffs . . . . . . . . . . 3.5 Implementierung der grafischen Benutzerschnittstelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 24 34 37 38 45 4 Ausblick 57 A Benutzerhandbuch des Wissenserwerb-Werkzeuges A.1 Das Hauptfenster . . . . . . . . . . . . . . . . . . A.2 Das Fenster zum Ändern von Komponenten . . . A.3 Die Liste der Ressourcen . . . . . . . . . . . . . . A.4 Ändern von Ressourcen . . . . . . . . . . . . . . . 60 60 62 64 65 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B Ausgewählte Dateien 66 B.1 Dateien aus dem Projekt Service Composition . . . . . . . . . . . . . . 66 B.2 Mögliche XML-Darstellung des Komponentenkatalogs (Ausschnitt) . . 76 Abkürzungsverzeichnis 78 Literaturverzeichnis 79 i Abbildungsverzeichnis 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 Eine Zerlegungshierarchie (Ausschnitt nach [CGS91]) . . . . . . . . . . 5 Ausschnitt aus einer Spezialisierungshierarchie . . . . . . . . . . . . . 6 Einschränkung von Eigenschaften durch Spezialisierung . . . . . . . . 6 Komponentenkatalog nach dem Ressourcenmodell . . . . . . . . . . . . 8 Konfigurationsaufgabe beim ressourcenorientierten Ansatz . . . . . . 8 Lösung der Aufgabe von Abbildung 1.5 . . . . . . . . . . . . . . . . . . . 9 Beispiel für auseinanderlaufende Ressourcenforderungen nach [BR96] 11 Änderung der Konfiguration aus Abbildung 1.7 unter Verwendung zusammengesetzter Ressourcen . . . . . . . . . . . . . . . . . . . . . . . 12 2.1 Komponenten eines TINA-Systems (Ausschnitt) nach [AFG+ 98] . . . . 15 2.2 Das Ressourcenmodell in SCO . . . . . . . . . . . . . . . . . . . . . . . . 16 2.3 Konfigurationssystem und WWW-Darstellung . . . . . . . . . . . . . . 23 3.1 3.2 3.3 3.4 3.5 3.6 3.7 . . . . . . . 37 38 48 48 48 48 52 Das Hauptfenster des Werkzeuges mit einer selektierten Komponente Das Katalog-Menü . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Einblenden der Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . Anlegen einer neuen Komponente . . . . . . . . . . . . . . . . . . . . . Kopieren einer Komponente . . . . . . . . . . . . . . . . . . . . . . . . . In diesem Fenster werden die Eigenschaften von Komponenten verändert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.7 Parameter einer Ressource . . . . . . . . . . . . . . . . . . . . . . . . . . A.8 Liste der Ressourcen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.9 Fenster zum Ändern der Eigenschaften einer Ressource . . . . . . . . . 60 61 61 61 62 A.1 A.2 A.3 A.4 A.5 A.6 ii Die Katalog-Schnittstelle . . . . . . . . . . . . Integration eines CORBA-basierten Katalogs Konstruiertes Fenster . . . . . . . . . . . . . Widget-Palette von Glade . . . . . . . . . . . Widget-Baum . . . . . . . . . . . . . . . . . . Widget-Eigenschaften . . . . . . . . . . . . . . Beispiel von CheckListWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 63 64 65 Einleitung Im Rahmen eines Forschungsprojektes (S ERVICE C OMPOSITION – SCO) wurde von der GMD F ORSCHUNGSZENTRUM I NFORMATIONSTECHNIK G MB H und der H UM BOLDT -U NIVERSITÄT ZU B ERLIN ein Expertensystem für die Konfiguration von modularen technischen Systemen entwickelt [vLSN99]. Dieses Konfigurationssystem arbeitet nach einem Verfahren, das ressourcenorientiertes Konfigurieren genannt wird [Hei93]. Geplantes Anwendungsgebiet ist der Entwurf von Telekommunikations- und Multimediadiensten, die auf einer Architektur für offene verteilte Systeme basieren. Die Erfassung des Wissens, mit dem das Expertensystem arbeitet, erfolgte durch das Erstellen von Quelltexten der Programmiersprache Python. Während diese Vorgehensweise in einem Forschungsprojekt ausreichend ist, erfordert die praktische Arbeit mit dem Konfigurationssystem Werkzeugunterstützung bei der Wissenseingabe. Ziel der vorliegenden Arbeit ist die Entwicklung eines Werkzeuges zur Wissenserfassung für das Konfigurationssystem. Dieses soll die Erfassung des Wissens gegenüber der bisherigen Vorgehensweise vereinfachen und – in Grenzen – auch unerfahrenen Nutzern möglich machen. Die Funktion des Konfigurationssystems soll durch das zu erstellende Werkzeug nicht beschränkt werden. Inhalt der Arbeit Zu Beginn der Arbeit wird eine kurze Einführung in die Modellierung und Konfigurierung technischer Systeme gegeben. Dabei werden verschiedene Begriffe aus diesem Bereich erläutert. Danach werden die beiden wichtigsten Ansätze bei der Entwicklung von Konfigurationssystemen näher betrachtet. Im zweiten Teil wird das Konfigurationssystem des Forschungsprojektes S ERVICE C OMPOSITION erläutert. Dabei wird auf das Anwendungsgebiet des Konfigurationssystems eingegangen sowie auf das Metamodell, nach dem das Wissen modelliert wird und auf den Aufbau des Systems. Im dritten und letzten Teil steht das Werkzeug für den Wissenserwerb im Vordergrund. Dabei werden die Entscheidungen bezüglich des Speicherformats für das Wissen und der Benutzerschnittstelle erklärt. Danach wird die Implementierung des Werkzeuges beschrieben. Im Anhang befindet sich ein kurzes Nutzerhandbuch für das Werkzeug sowie einige Dateien, die für das Verständnis dieser Arbeit nützlich sein können. 1 1 Modellieren und Konfigurieren modularer technischer Systeme 1.1 Einführung Beim Entwurf von komplexen technischen Systemen, wie z. B. von Telekommunikations- und Softwaresystemen, Fahrzeugen oder automatische Fabrikationsanlagen werden die Entwürfe oft durch Zusammensetzen vorhandenener Teilentwürfe erzeugt. In der Regel entsprechen diese Teilentwürfe separat produzierbaren Teilen, die sich dann zum fertigen System zusammensetzen lassen. Dadurch soll erreicht werden, daß der Zeit- und Geldaufwand für den Entwurf sinkt. Außerdem ist ein flexibles Eingehen auf Kundenwünsche möglich, ohne den Vorteil der Serienproduktion zu verlieren [Büt97]. Dieser Vorgang des Zusammensetzens1 wird im allgemeinen als Konfigurieren bezeichnet. So schreibt Michael Heinrich [Hei93]: „Konfigurieren ist das Entwerfen eines technischen Systems durch Auswählen, Parametrieren und Zusammenfügen von Exemplaren geeigneter Komponententypen eines vorgegebenen Komponentenkatalogs.“ Mit Komponenten bezeichnet Heinrich die Teile, die zusammengesetzt werden.2 Komponenten gleicher Art werden durch einen Komponententyp3 beschrieben. Für die Gesamtheit aller erfaßten Komponententypen wird der Begriff Komponentenkatalog verwendet [Hei89]. Das Ergebnis des Konfigurierens, also solch eine Zusammenstellung von Komponenten, wird als Konfiguration bezeichnet. Das „Lexikon Informatik und Datenverarbeitung“ [Sch97] beschreibt Konfiguration als: „[. . . ] eine bestimmte Zusammenstellung datenverarbeitender Peripheriegeräte zur Erfüllung meist unterschiedlicher Funktionen der Datenverarbeitung [. . . ]“ In dieser Arbeit wird der Begriff der Konfiguration ebenso wie der des Konfigurierens nicht auf das Anwendungsgebiet der Datenverarbeitung beschränkt, sondern 1 Mit Begriffen wie Zusammensetzen und Teile usw. ist immer das Zusammensetzen eines Entwurfs bzw. Teile eines Entwurfs gemeint, da nur der Entwurfsprozeß und nicht der Vorgang des Zusammensetzens des tatsächlichen Systems im Blickpunkt dieser Arbeit steht. 2 Büttner [Büt97] bezeichnet die Komponenten mit dem Begriff „Bausteine“ und ihre Gesamtheit als „Baukasten“. Hier wird der Bezeichnung Komponente der Vorzug gegeben. 3 In dieser Arbeit wird der Kürze wegen oft „Komponente“ an Stelle von „Komponententyp“ verwendet. Dabei gilt folgende Konvention: bei der Modellierung handelt es sich immer um Komponententypen, die beschrieben werden, bei der Konfigurierung um Exemplare dieser Typen – also Komponenten. 2 1.1 Einführung im allgemeineren Sinne für alle zusammengesetzten technische Systeme verwendet.4 Klaus Büttner beschreibt das Konfigurieren als einen Spezialfall des Konstruierens. Beides beschäftigt sich mit dem Entwurf eines neuen Produktes ausgehend von Anforderungen. Der Unterschied liegt darin, daß beim Konfigurieren nur aus einer Menge von „weitestgehend vorgegebenen“ Bausteinen ausgewählt wird. Dadurch ist der Ablauf beim Konfigurieren gegenüber dem beim Konstruieren wesentlich vereinfacht [Büt97]. Die theoretischen Grundlagen des Konfigurierens sind ein Schwerpunkt der Forschung auf dem Gebiet der Künstlichen Intelligenz. Hier sei auf die Berichte zu dem jährlich stattfindenden Workshop „Planen und Konfigurieren“ verwiesen [HTH89], [Hor93]. Schwerpunkte dabei sind die Beschreibung der Komponenten und ihre Beziehungen in Modellen sowie der Entwurf von Konfigurationssystemen zur Unterstützung bei der Konfigurierung. Ein Konfigurationssystem ist laut „Lexikon Informatik und Datenverarbeitung“ [Sch97]: „[. . . ] ein Expertensystem, dessen Anwendungsbereich in der Konfiguration eines komplexen Objektes, z. B. eines Motors oder einer anderen Maschine besteht. Die Schwierigkeit besteht darin, daß nicht aus einer vorgegebenen Menge ein Element ausgewählt wird, sondern ein neues Objekt synthetisiert werden muß.“ Grund für den Entwurf solcher Konfigurationssysteme ist dabei die hohe Zahl von Freiheitsgraden beim Entwurf der Systeme, die ein Konfigurieren von Hand nur schwer erlauben und fehlerträchtig machen [Büt97]. Büttner [Büt97] unterscheidet zwischen interaktiven und automatischen Systemen. Bei interaktiven Systemen wählt der menschliche Konstrukteur aus den Bausteinen aus, die ihm vom System vorgeschlagen werden und fügt sie der Teilkonfiguration hinzu. Die Aufgabe des Konfigurationssystems liegt dann darin, nur Komponenten anzubieten, deren Hinzufügen keine Konflikte mit bestehenden Komponenten erzeugt und zum Lösen der Konfigurationsaufgabe nützlich sein kann. Nach Heinrich [Hei89] sollen interaktive Systeme auch den Konfigurationsverlauf (vor allem den Grund des Verwerfens bestimmter Komponenten) erklären. Automatische Systeme hingegen erarbeiten aus den Anforderungen an das System selbstständig Konfigurationen, die diese erfüllen. Der Anwender hat nur die Möglichkeit, die gefundenen Lösungen nachzubearbeiten. Dazu ist die Dokumentation der Konfigurationsentscheidungen wesentliche Voraussetzung. Die einem Konfigurationssystem bekannten Komponenten und ihre Eigenschaften sind durch ein Modell des Anwendungsgebietes beschrieben. Das „Lexikon Informatik und Datenverarbeitung“ [Sch97] definiert den Begriff Modell als: Idealisierte, vereinfachte, in gewisser Hinsicht ähnliche Darstellung eines Gegenstandes, Systems oder sonstigen Weltausschnitts, mit dem 4 Das Zusammensetzen chemischer Strukturen, das auch als Konfigurieren bezeichnet wird, liegt außerhalb der Betrachtung, da es sich, wie Dörner [Dör47] ausführt, deutlich vom Konfigurieren technischer Systeme unterscheidet. 3 1 Modellieren und Konfigurieren modularer technischer Systeme Ziel, daran bestimmte Eigenschaften des Vorbilds besser studieren zu können. Die Wahl des richtigen Modelltyps (Metamodells) ist dabei wichtig, um zu erreichen, daß das Modell dem Vorbild in genau den Eigenschaften ähnelt, die studiert werden sollen. Beim Konfigurieren ist der zu betrachtende Weltausschnitt der Anwendungsbereich des Konfigurationssystems. Die zu studierenden Eigenschaften sind dann die Komponenten, ihre Eigenschaften und ihre Beziehungen.5 Der Komponentenkatalog ist Bestandteil des Modells. Dieses kann aber darüber hinaus noch andere Informationen, wie z. B. Randbedingungen, enthalten. Zwei Arten von Metamodellen kommen beim Konfigurieren hauptsächlich zur Anwendung: der ressourcenorientierte Ansatz (siehe Abschnitt 1.3) und der strukturorientierte Ansatz (Abschnitt 1.2) [Hei93], [AW95]. Beide Ansätze haben, wie im folgenden gezeigt wird, ihre Vor- und Nachteile, wobei einige Autoren auch eine Verbindung der Ansätze vorschlagen [AW95]. 1.2 Strukturorientiertes Konfigurieren Der strukturorientierte Ansatz ist mit einer Vielzahl von Veröffentlichungen (z. B. in [HTH89], [Hor93]) und existierenden Konfigurationssystemen [CGS91], [Büt97], [GKK99], [KG91] am weitesten verbreitet. Beim strukturorientierten Konfigurieren steht – der Name sagt es bereits – die Struktur der Komponenten im Vordergrund.6 Diese Struktur der Komponenten wird durch die Has-Parts- („besteht aus“) oder Part-of- („Teil von“) Eigenschaft der Komponententypen beschrieben. Dadurch wird eine Zerlegungshierarchie wie in Abbildung 1.1 erzeugt. Neben der Zerlegungshierarchie werden die Komponententypen in der Regel auch in eine Spezialisierungshierarchie (taxonomische Hierarchie) eingeordnet. In den allgemeineren Komponententypen sind die gemeinsamen Eigenschaften ihrer Spezialisierungen zusammengefaßt, wie Bild 1.2 verdeutlicht. Diese Hierarchie hat üblicherweise eine Baumform, d. h. ein Komponententyp kann nicht gleichzeitig Spezialisierung mehrerer anderer sein (keine Mehrfachvererbung). Um diese Eigenschaft ausdrücken zu können, werden Sichten auf die Objekte eingeführt [CGS91]. Innerhalb jeder Sicht kann dann eine eigene Spezialisierungshierarchie bestehen. Jeder Komponententyp hat über die Einordnung in die Hierarchien hinaus noch weitere Eigenschaften. Diese können mit beliebigen Wertebereichen versehen sein. Für Spezialisierungen kann dann der Wertebereich weiter eingeengt werden (Abbildung 1.3). 5 In der KI-Forschung werden die Modelle meist als Wissensbasis oder Wissen bezeichnet. Die Art des Modells wird dann Wissensrepräsentation genannt. In dieser Arbeit wird der Begriff des Modells verwendet, da er besser verdeutlicht, daß es sich um eine vereinfachte Wiedergabe der Realität handelt. 6 Eine andere gebräuchliche Bezeichnung ist die des Constraints-basierten Konfigurierens. Der Be- 4 1.2 Strukturorientiertes Konfigurieren Auto Fahrgestell Achse Rad Felge Motor Turbolader Karosserie Zylinder Tür Reifen Abbildung 1.1: Eine Zerlegungshierarchie (Ausschnitt nach [CGS91]) Die Abhängigkeiten zwischen den Komponenten werden durch Beschränkungen, sogenannte Constraints, dargestellt. Sie beschreiben Randbedingungen der Beziehungen zwischen den Komponenten. Constraints dienen sowohl zur Ermittlung oder Einengung von noch unbekannten Eigenschaften von Komponenten als auch zur Validierung einer (Teil-) Konfiguration. Werden Constraints miteinander verknüpft, die gemeinsame Variablen haben, so ergibt sich ein Netz. Wird eine Eigenschaft, die im Constraints-Netz vorkommt, mit einem Wert belegt, so ergibt sich durch Anwendung des Netzes u. U. eine Vielzahl von Einschränkungen des Wertebereichs anderer Eigenschaften. Ausgangspunkt des Konfigurationsvorganges (die Konfigurationsaufgabe) ist die Vorgabe von Komponenten, für die einige Eigenschaften festgelegt und andere noch variabel sind. Dadurch wird eine abstrakte Konfiguration vorgegeben, die die zu lösende Aufgabe erfüllt. Im Verlauf des Konfigurationsverfahrens wird diese allgemeine Konfiguration konkretisiert. Ziel ist eine Konfiguration, in der Eigenschaften und Zusammensetzung aller Komponenten bekannt sind und alle Constraints erfüllt werden.7 Das wird durch wiederholtes Anwenden der Aktionen • Zerlegen einer Komponente in ihre Teile, • Spezialisieren einer Komponente und damit einhergehend Einschränken von Eigenschaften, 7 griff der Constraints wird später erläutert. Neben den hier betrachteten sogenannten harten Constraints, die für eine korrekte Konfiguration erfüllt sein müssen, gibt es noch weiche Constraints, durch die optimale Konfigurationen bestimmt werden. Durch Lockern dieser Constraints können eventuelle Konflikte gelöst werden [CGS91]. 5 1 Modellieren und Konfigurieren modularer technischer Systeme Konstruktionsobjekt Autoteil Auto PKW Motor Fahrwerkteil Achse Reifen Rad Abbildung 1.2: Ausschnitt aus einer Spezialisierungshierarchie Auto +Höchstgeschwindigkeit: [0 km/h - 300 km/h] +Farbe: {rot gruen blau schwarz} +Hersteller: Autofirma Porsche +Höchstgeschwindigkeit: [200km/h -300km/h] +Farbe: {rot schwarz} +Hersteller: = Porsche AG LKW +Höchstgeschwindigkeit: [0km/h - 120km/h] +Hersteller: Lkw-Firma Abbildung 1.3: Einschränkung von Eigenschaften durch Spezialisierung • Integrieren von Teilkomponenten zu Zusammensetzungen sowie • Festlegen von noch unbestimmten Eigenschaften. erreicht [CGS91]. Sind bei einer erreichten Konfiguration nicht alle Constraints erfüllt, so wird der letzte Schritt rückgängig gemacht und als ungültig gekennzeichnet (Backtracking).8 Die Auswahl des jeweils nächsten Schrittes kann durch verschiedene Verfahren getroffen werden. Möglich sind die Anfrage beim Benutzer, der Vergleich mit „ähnlichen“ Fällen (fallbasiertes Schließen), das Auswerten von externen Simulationsergebnissen oder das Anwenden der Constraints zur Einengung von Werten [CGS91], [Pfi93]. Die Strategien dafür sind bestimmend für die Qualität eines Konfigurationssystems. Sie werden unter dem Begriff Kontrollwissen zusammengefaßt. 8 6 In den einzelnen Konfigurationssystemen können darüber hinaus noch andere Verfahren zur Auflösung solcher Konflikte angewendet werden. 1.3 Ressourcenorientiertes Modellieren / Konfigurieren Für Systeme, in denen die Strukturbeziehungen von Komponenten zum Modellierungszeitpunkt nicht festlegbar sind, ist dieses Verfahren ungeeignet. Ein weiterer Nachteil ist, daß die Eigenschaften der Komponententypen zum Teil von diesen getrennt in Constraints beschrieben werden. Außerdem ist beim Hinzufügen eines neuen Komponententyps dessen Verträglichkeit mit allen bisher erfaßten zu überprüfen und durch Constraints zu beschreiben. Das Erfassen und Warten des Kontrollwissens ist komplex und nur durch Experten möglich. 1.3 1.3.1 Ressourcenorientiertes Modellieren / Konfigurieren Das einfache Ressourcenmodell In den Beiträgen zum 3. Workshop „Planen und Konfigurieren“ beschreibt Michael Heinrich [Hei89] das ressourcenorientierte Konfigurieren. Diesem Konfigurationsverfahren liegt als Metamodell das Ressourcenmodell zugrunde. Wichtigstes Konzept des Ressourcenmodells ist das der Ressource. Ressourcen sind quantitative und qualitative Eigenschaften von Komponenten, aus denen sich die Beziehungen zu anderen Komponenten ergeben [vLSN99], [Hei89]. Jede Komponente stellt Ressourcen bereit und verbraucht/erfordert Ressourcen. Die Beziehungen zwischen den Komponenten werden allein dadurch ausgedrückt, daß eine Komponente eine Ressource (Eigenschaft) benötigt, die eine andere bereitstellt [Hei89]. Beim „Benötigen“ einer Ressource durch eine Komponente unterscheidet Heinrich [Hei89] zwischen dem verbrauchen – d. h. diese Ressource steht danach nicht mehr zur Verfügung und dem erforderlich sein – d. h. mehrere Komponenten können sich die Ressource teilen. Ressourcen, die verbraucht werden, werden auch exklusive Ressourcen genannt. Auch für das ressourcenorientierte Konfigurieren schlägt Heinrich vor, die Komponenten in eine Spezialisierungshierarchie einzuordnen. Im Gegensatz zur Spezialisierung beim strukturorientierten Ansatz, wo Komponenten abstrakter Typen in Teilkonfigurationen auftreten können und dann später zu konkreten Komponenten spezialisiert werden, werden beim ressourcenorientierten Ansatz nur die nicht abstrakten Komponenten verwendet. Beim ressourcenorientierten Konfigurieren werden nach [Hei89] die Anforderungen an das System durch benötigte Ressourcen der Umgebung dargestellt. Vorhandene Voraussetzungen werden dementsprechend durch bereitgestellte Ressourcen abgebildet. Im Verlauf des Konfigurierens werden Komponenten aus dem Katalog hinzugefügt, bis eine korrekte Konfiguration erreicht wird – d. h. alle von einer Komponente benötigten Ressourcen werden von einer anderen Komponente bereitgestellt und exklusive Ressourcen werden in ausreichender Zahl bereitgestellt. Tritt im Laufe des Konfigurierens die Situation ein, daß eine Ressource gefordert wird, die von keiner Komponente bereitgestellt wird, so sind ein oder mehrere Auswahl-Entscheidungen rückgängig zu machen (Backtracking). Das Hinzufügen einer weiteren Komponente gliedert Heinrich in drei Schritte: 1. Auswahl der nächsten nicht befriedigten Ressource der Teilkonfiguration, 7 1 Modellieren und Konfigurieren modularer technischer Systeme 2. Auswahl des bestbewerteten Komponententyps, der diese Ressource bereitstellt und Hinzufügen eines Exemplars dieses Typs zur Konfiguration sowie 3. Abgleich der benötigten und bereitgestellten Ressourcen der neuen Komponente mit dem Rest der Konfiguration [Hei93]. Die Algorithmen zur Auswahl der nächsten Ressource und zur Bewertung der Komponenten sind wieder Bestandteil des Kontrollwissens. Dieses wird oft durch das Anwendungsgebiet des Konfigurationssystems bestimmt [Hül98] [DKBS96]. Stromversorgung PC Internetbenutzung Modem Netzwerkverbindung Ethernetverbindung Ethernetkarte Netzwerkverbindung Stromversorgung Telefonanschluß Telefonverbindung Netzwerkverbindung Stromversorgung Telefonverbindung Modem Telefonverbindung Abbildung 1.4: Komponentenkatalog nach dem Ressourcenmodell Abbildung 1.4 zeigt einen einfachen Komponentenkatalog nach dem Ressourcenmodell, die Abbildungen 1.5 und 1.6 eine Konfigurationsaufgabe und deren schrittweise Lösung mit diesem Katalog. Die Konfigurationsaufgabe besteht in diesem Beispiel darin, daß ein System entworfen werden soll, daß eine Internetbenutzung ermöglicht. Als Voraussetzung ist nur eine Stromversorgung gegeben. Internetbenutzung Systemumgebung Stromversorgung Abbildung 1.5: Konfigurationsaufgabe beim ressourcenorientierten Ansatz 8 1.3 Ressourcenorientiertes Modellieren / Konfigurieren 1. Konfigurierungschritt Systemumgebung Internetbenutzung Stromversorgung PC Netzwerkverbindung 2. Konfigurierungschritt Systemumgebung Internetbenutzung Stromversorgung Telefonverbindung PC Netzwerkverbindung Modem 3. Konfigurierungschritt Systemumgebung Internetbenutzung Stromversorgung Telefonanschluß PC Telefonverbindung Netzwerkverbindung Modem Abbildung 1.6: Lösung der Aufgabe von Abbildung 1.5 9 1 Modellieren und Konfigurieren modularer technischer Systeme • Im ersten Schritt wird eine Komponente gesucht, die die geforderte Ressource „Internetbenutzung“ bereitstellt. Dies ist die Komponente „PC“. Die von „PC“ geforderte Ressource „Stromversorgung“ wird durch die Systemumgebung bereitgestellt. Die Forderung nach „Netzwerkverbindung“ kann momentan nicht befriedigt werden. • Um „Netzwerkverbindung“ bereitzustellen, wird die Komponente „Modem“ hinzugefügt.9 Die Forderung nach „Stromversorgung“ wird wieder durch die Umgebung befriedigt, wohingegen „Telefonverbindung“ als offene Forderung übrigbleibt. • Im letzten Schritt wird nach dem Hinzufügen von „Telefonanschluß“ eine korrekte Konfiguration erreicht. Es bestehen keine offenen Ressourcenforderungen mehr. Während des Konfigurierens werden die von einer Komponente benötigten Ressourcen mit den bereitgestellten Ressourcen der Komponententypen im Katalog verglichen. In dieser einfachen Ausprägung des Modells stimmen Forderung und Bereitstellung überein – die bereitgestellte Ressource befriedigt die Forderung – wenn geforderte und bereitgestellte Ressource von gleicher Art sind. Für die im folgenden eingeführten Erweiterungen werden unter Umständen neue Vergleichsoperationen nötig. Im Verlauf des Konfigurierens werden die benötigten Ressourcen jeder Komponente mit Ressourcen, die diesen Bedarf befriedigen, verbunden. Werden diese Ressourcen verbraucht und nicht nur benötigt, so ist die Bereitstellung dieser Ressource bei der bereitstellenden Komponente entsprechend zu reduzieren. Den Abgleich (das Verbinden) von Forderungen und Bereitstellungen für eine Teilkonfiguration wird Bilanzierung genannt. Auch die Bilanzierung ist bei Erweiterungen des Ressourcenbegriffs eventuell anzupassen. 1.3.2 Erweiterung des Modells Strukturbeziehungen im Ressourcenmodell Auch Strukturbeziehungen zwischen Komponenten („ist Teil von“) lassen sich durch Ressourcen ausdrücken. Dabei werden von den zusammengesetzten Komponenten besondere Ressourcen bereitgestellt, die nur von ihren Teilen verbraucht werden [Hei93]. Es läßt sich allerdings so nicht ausdrücken, daß die Komponente, die eine Ressourcenforderung befriedigt, Teil der gleichen Komponente sein muß wie die fordernde Komponente. Dadurch entsteht, was Börding und Rahmer [BR96] „auseinanderlaufenden Ressourcenforderungen“ nennen. Ein Beispiel für das Problem findet sich in Abbildung 1.7. In dieser Abbildung fehlt die Systemkomponente. Sie setzt sich aus den Komponenten 9 „Netzwerkverbindung“ wird auch von der Komponente „Ethernetkarte“ bereitgestellt. Wäre diese anstelle von „Modem“ hinzugefügt worden, so hätte sich eine Sackgasse in der Konfigurierung ergeben, da sie die Ressource „Ethernetverbindung“ fordert, die keine der Komponenten des Katalogs bereitstellen. 10 1.3 Ressourcenorientiertes Modellieren / Konfigurieren Bereich Arbeitsplatz 1 Arbeitsplatz Fernsprechen Telefonbenutzung Telefonkomponente Zugang Telefonnetz Anschluß 1 Takt Gebührenanzeige Arbeitsplatz Fernsprechen mit Gebührenkontrolle Telefonbenutzung Gebührenanzeigekomponente Telefonkomponente Bereich Arbeitsplatz 2 Zugang Telefonnetz Anschluß 2 Takt Abbildung 1.7: Beispiel für auseinanderlaufende Ressourcenforderungen nach [BR96] „Arbeitsplatz Fernsprechen“, „Arbeitsplatz Fernsprechen mit Gebührenkontrolle“, „Anschluß 1“ und „Anschluß 2“ zusammen.10 Börding und Rahmer schlagen vor, dieses Problem durch zusammengesetzte Ressourcen zu lösen. Eine zusammengesetzte Ressource ist entweder eine Menge aus anderen zusammengesetzten Ressourcen oder eine atomare Ressource11 . Der Bedarf nach einer zusammengesetzten (nicht atomaren) Ressource wird durch eine bereitgestellte befriedigt, wenn die Teilressourcen der geforderten Ressource wiederum durch Teilressourcen der bereitgestellten Ressource befriedigt werden. Bei atomaren Ressourcen wird davon ausgegangen, daß eine Vergleichsfunktion für diese existiert [BR96]. Abbildung 1.8 verdeutlicht die Probleme, die dieser Ansatz aufwirft. Es ist erforderlich, neue Komponenten einzuführen, die die zusammengesetzten Ressourcen bereitstellen. Diese Komponenten sind konzeptionell aus den Komponenten zusammengesetzt, die die einzelnen Teilressourcen bereitstellen. Diese Zusammensetzung von Komponenten sollte sich auch im Modell (z. B. durch Strukturressourcen) widerspiegeln.12 Der Bedarf einer zusammengesetzten Komponente sind zusammengesetzte Ressourcen, die aus dem Bedarf der Teilkomponenten bestehen.13 In SCO wird das Problem der „auseinanderlaufenden Ressourcenforderungen“ durch den Abgleich von Ressourcen innerhalb einer zusammengesetzten Komponente gelöst (Abschnitt 2.2). 10 In [BR96] fehlt allerdings ein Hinweis auf diese Systemkomponente. eine Ressource, die denen des einfachen Modells entspricht 12 Börding und Rahmer [BR96] machen keine Aussage über zusammengesetzte Komponenten. 13 Welche Ressourcen aus dem Bedarf der Teilkomponenten jeweils zusammengefaßt werden – also durch die gleiche Komponente befriedigt werden müssen – ist für jede zusammengesetzte Komponente neu zu bestimmen. 11 11 1 Modellieren und Konfigurieren modularer technischer Systeme Bereich Arbeitsplatz 1 Arbeitsplatz Fernsprechen Telefonbenutzung Telefonkomponente Zugang Telefonnetz, Takt Anschluß 1 Bereich Arbeitsplatz 2 Arbeitsplatz Fernsprechen mit Gebührenkontrolle Gebührenanzeige Telefonbenutzung Telefon- und Gebührenanzeige-komponente Zugang Telefonnetz, Takt Anschluß 2 Abbildung 1.8: Änderung der Konfiguration aus Abbildung 1.7 unter Verwendung zusammengesetzter Ressourcen Umwelteigenschaften und Unverträglichkeit Laut Heinrich [Hei89] gibt es einige Komponenteneigenschaften, wie z. B. Umweltbedingungen und Unverträglichkeiten zwischen Komponenten, die sich nicht durch Ressourcen beschreiben lassen. Diese Eigenschaften seien nicht anwendungsspezifisch und könnten deswegen in das Problemlösungsverfahren fest eingebaut statt durch Ressourcen beschrieben zu werden. Dessenungeachtet lassen sich auch Umweltbedingungen durch Erweiterungen des Ressourcenbegriffs modellieren. Umweltbedingungen sind auch nur weitere Eigenschaften, die für das Funktionieren der Komponente notwendig sind. Sie unterscheiden sich von den bisher durch Ressourcen beschriebenen allein darin, daß sie nur von der jeweiligen Systemumgebung bereitgestellt werden können.14 Umwelteigenschaften wie Temperatur oder Luftfeuchte sind eine Form von Ressourcen, deren Wert ein Intervall ist. Bei solchen intervallwertigen Ressourcen wird ein Bedarf dann durch eine Bereitstellung befriedigt, wenn das Intervall der Bereitstellung das des Bedarfs umschließt. Die Unverträglichkeit zweier Komponenten ist mit dem Ressourcenmodell nur schwer zu beschreiben. Eine Möglichkeit wäre, daß beide Komponenten eine exklusive Systemressource verbrauchen. Dadurch wird aber die Eigenschaft, daß sich die beiden Komponenten nicht vertragen, erst in der Beschreibung des zu konfigurierenden Systems festgelegt. Wenn diese Unverträglichkeit grundsätzlich in jeder Konfiguration gilt, so sollte sie sich auch schon während der Modellierung ausdrücken lassen. Eine weitere Möglichkeit, die Unverträglichkeit zweier Komponenten zu beschreiben, besteht nach Ansicht des Autors darin, eine neue Kategorie von Ressourcen einzuführen. Diese seien Ausschlußressourcen genannt. Der Bedarf an einer Ausschlußressource sei befriedigt, wenn keine Komponente sie bereitstellt. Eine der unverträglichen Komponenten stellt die Ausschlußressource bereit, die andere 14 Es ist durchaus vorstellbar, daß später dem Katalog z. B. eine Komponente „Klimaanlage“ hinzugefügt wird, die diese Eigenschaften bereitstellt. Wurden diese Eigenschaften nicht in das Lösungsverfahren eingebaut sondern als Ressourcen modelliert, ist jetzt keine weitere Änderung nötig. 12 1.3 Ressourcenorientiertes Modellieren / Konfigurieren fordert sie an.15 Die Einführung von Ausschlußressourcen erfordert eine Anpassung des Konfigurationsvorgangs. Die Ausschlußressourcen dürfen nicht als primäres Kriterium bei der Auswahl der hinzuzufügenden Komponente dienen. Vielmehr sind sie eine Randbedingung, um die Auswahl der Komponenten, die eine andere Ressourcen-Forderung erfüllen, einzuengen. Weiterhin ist beim Hinzufügen einer Komponente, die eine Ausschlußressource fordert, zu überprüfen, ob in der bestehenden Teilkonfiguration Komponenten vorhanden sind, die diese Ressource bereitstellen – also sich nicht mit der neuen Komponente vertragen.16 Ðurd̄anović, Kleine Büning und Suermann schlagen vor, zu jeder Komponente eine Liste mit unverträglichen Komponenten zu speichern [DKBS96]. Dadurch geht allerdings der Vorteil des ressourcenorientierten Konfigurierens verloren, daß beim Hinzufügen einer neuen Komponente nicht der gesamte Katalog auf eventuelle Änderungen hin zu überprüfen ist. Wird die Unverträglichkeit dagegen in einer globalen Liste gespeichert, so verliert sich der Vorteil des Ressourcenmodells, daß alle Informationen, die eine Komponenten betreffen, mit dieser zusammen abgespeichert werden. Zusammenfassend läßt sich feststellen, daß der ressourcenorientierte Ansatz für die Konfiguration in schwach strukturierten Anwendungsbereichen gut geeignet ist. Er läßt sich durch die Einführung neuer Ressourcentypen auch an neue Konfigurationsaufgaben anpassen. In der Praxis existieren einige Konfigurationssysteme, die auf diesem Ansatz basieren, so zum Beispiel zum Konfigurieren von Telefonanlagen oder von lokalen Netzwerken [DKBS96], [Hei93]. Der Vorteil des ressourcenorientierten Ansatzes gegenüber dem strukturorientierten liegt in der einfachen Erweiterbarkeit des Komponentenkatalogs. Beim Hinzufügen eines neuen Komponententyps ist im Idealfall nur dieser mit seinen Ressourcen zu beschreiben. Die bereits erfaßten Komponenten müssen nicht geändert werden. Beim Hinzufügen einer neuen Ressource, die bisher nicht betrachtet wurde, ist dagegen eventuell eine Änderung bestehender Komponententypen notwendig. In einigen Fällen, wie der unauflösbaren Unverträglichkeit zweier Komponenten, scheint die Verbindung mit Konzepten aus dem strukturorientierten Ansatz angebracht. 15 16 Dabei geht aber verloren, daß die Unverträglichkeit eigentlich eine symmetrische Eigenschaft ist. Die Auflösung eines Konflikts kann sich u. U. als nicht trivial herausstellen, wenn diese Komponente die einzige ist, die eine bestimmte Ressource bereitstellt. 13 2 Das Konfigurationssystem des SCO-Projektes 2.1 Konfigurationsaufgabe Das Forschungsprojekt SCO beschäftigte sich mit der Konfiguration von TINASystemen. Die „Telecommunications Information Networking Architecture“ (TINA) hat das Ziel, die Entwicklung neuer Telekommunikationsdienste1 zu erleichtern und die Interoperabilität dieser Dienste zwischen verschiedenen Anbietern zu gewährleisten [TIN01]. Dies wird durch die Standardisierung von Schnittstellen und Abläufen erreicht. TINA ist eine Spezialisierung des allgemeinen ODP-Referenzmodells [ITU95] für offene verteilte Systeme. Eine Reihe von Standardisierungsdokumenten [TIN01] beschreibt die Kommunikationssysteme aus verschiedenen Gesichtspunkten wie Unternehmenssicht (enterprise viewpoint), Informationssicht (information viewpoint), Funktionale Sicht (computational viewpoint) und Verfahrenssicht (technology viewpoint) . In „Service Architecture“ [AFF+ 98] und „Service Component Specification“ [AFG+ 98] werden eine Reihe von Software-Komponenten beschrieben, die für das Funktionieren eines Dienstes in einem TINA-System von Bedeutung sind. In Abbildung 2.1 sind einige dieser Komponenten und ihre Abhängigkeiten dargestellt.2 Einige Komponenten sind beim Kunden angesiedelt während andere sich beim Dienstanbieter befinden. Dabei sind einige Komponenten dienstspezifisch während andere von allen Diensten gemeinsam genutzt werden. Ein Beispiel für letztere sind die Komponenten, die die Berechtigung zur Benutzung des Systems und einzelner Dienste überprüfen. Beim Entwurf der Architektur stand die Benutzung wiederverwendbarer Softwarekomponenten im Vordergrund, um die Entwicklung neuer Dienste zu beschleunigen. Außer den in den TINA-Dokumenten beschriebenen Komponenten sind noch weitere für einen funktionierenden Dienst notwendig: die Software-Komponenten benötigen Hardware, auf der sie ausgeführt werden, sowie ein Betriebssystem. Desweiteren benötigen viele von ihnen weitere Software – wie Datenbanksysteme oder Programmbibliotheken. Beim Zusammensetzen eines neuen TINA-Dienstes aus den wiederverwendbaren Softwarekomponenten handelt es sich um eine typische Konfigurierungsaufgabe.3 1 z. B. herkömmliche Telefonie, Videokonferenzen, „Video-on-Demand“, aber auch ManagementDienste wie An- und Abstellen von Leistungsmerkmalen o. ä. 2 Diese Abbildung soll nur einen Eindruck von der Komplexität der Systeme geben. Deswegen wird hier nicht weiter auf die verwendeten Abkürzungen und ihre Bedeutung eingegangen. 3 Falls neue, dienstspezifische Komponenten benötigt werden, geht das über eine reine Konfigurie- 14 2.1 Nutzerdomäne Konfigurationsaufgabe Anbieterdomäne Sub asUAP IA UAF UA PA SF ssUAP Schnittstelle USM SSM Erzeugung einer Komponente Abbildung 2.1: Komponenten eines TINA-Systems (Ausschnitt) nach [AFG+ 98] Das Institut „GMD Fokus“ hat u. a. im Auftrag der D EUTSCHEN T ELEKOM viele wesentliche Komponenten für ein TINA-System4 sowie mehrere Beispieldienste entwickelt [FOK01]. Bei der testweisen Installation der Dienste stellten sich immer wieder eine Reihe von Abhängigkeiten zwischen den Komponenten und verschiedenen Softwarepaketen und -bibliotheken heraus. Das Wissen darüber war auf verschiedene Entwickler verteilt. Bei einem kommerziellen Einsatz der TINA-Architektur ist es aber notwendig, schnell aus den vorgefertigten Teilen neue Dienste erzeugen zu können. Dabei sollen auch die Abhängigkeiten zwischen den Komponenten berücksichtigt werden und das Konfigurieren Personen möglich sein, die die Interna des Systems nicht genau kennen (wie z. B. Kundenberater). Aus diesem Grund wurde das Projekt SCO ins Leben gerufen, das die Entwicklung eines Konfigurationssystems für diesen Anwendungsbereich als Ziel hatte. rung natürlich hinaus. Dieser Fall wird im weiteren nicht berücksichtigt, sondern nur die Erstellung von Diensten, die eine Kombination von bestehenden darstellen. Nach der Erstellung neuer, dienstspezifischer Komponenten ist das Hinzufügen dieser wieder eine Konfigurationsaufgabe. 4 Die Gesamtheit dieser Komponenten wird als TINA-Plattform bezeichnet. 15 2 Das Konfigurationssystem des SCO-Projektes 2.2 Das zugrunde liegende Metamodell Bei den zu konfigurierenden TINA-Systemen handelt es sich um stark modulare technische Systeme. An vielen Stellen läßt sich eine Komponente oder eine Gruppe von Komponenten durch andere ersetzen. Feste Strukturbeziehungen sind dagegen nur wenige vorhanden. Daher wurde in SCO als Grundlage des Konfigurationssystems ein ressourcenorientiertes Modell gewählt [vLSN99]. Neben den allgemeinen Abhängigkeiten zwischen Komponenten spielen auch Strukturbeziehungen eine Rolle. Durch die TINA-Architektur wird bereits vorgegeben, daß sich bestimmte Komponenten beim Kunden und andere beim Anbieter befinden (in TINA-Termini: innerhalb der Kundendomäne bzw. innerhalb der Anbieterdomäne). Weiterhin müssen sich bestimmte Software-Komponenten innerhalb des gleichen Computers befinden. Eine weitere strukturelle Beziehung besteht darin, daß für bestimmte Dienste besondere Hardware innerhalb eines Computers erforderlich ist. Diese Strukturbeziehungen lassen sich mit dem in 1.3.1 beschriebenen einfachen Modell nicht ausdrücken. Deshalb wurden neben der Spezialisierungshierarchie5 nach Heinrich [Hei89] auch Strukturbeziehungen, durch Strukturressourcen beschrieben, in das Modell aufgenommen (siehe Abschnitt 1.3.2). Eine Komponente, die andere enthält, wird in SCO Container genannt. Darüberhinaus bietet das Ressourcenmodell von SCO die Möglichkeit auszudrücken, daß Bedarf und Angebot einer Ressource nicht nur global, sondern auch innerhalb eines bestimmten Containers ausgeglichen sein müssen. Neben den Ressourcen werden die Komponenten noch durch weitere Eigenschaften beschrieben. Diese dienen der Darstellung im Konfigurationswerkzeug oder beinhalten Installationsanweisungen für die Softwarekomponenten. Abbildung 2.2 illustriert das Metamodell aus SCO. Komponententyp +GUI-Attribute: text +name: text +Installationsanweisungen: text Container 0..1 benötigt stellt bereit * * Ressourcentyp auszugleichen innerhalb Abbildung 2.2: Das Ressourcenmodell in SCO Die Ressourcen werden in SCO folgendermaßen klassifiziert: 5 Bestimmte übergeordnete Komponententypen in der Spezialisierungshierarchie werden in SCO als Komponentenkategorien bezeichnet. Exemplare dieser abstrakten Komponententypen können nicht in Konfigurationen erscheinen. Andere Komponententypen, von denen auch Spezialisierungen bestehen, können, im Gegensatz zum Ansatz von Heinrich [Hei89], durchaus in Konfigurationen verwendet werden. 16 2.2 Das zugrunde liegende Metamodell • Strukturressourcen bilden das Enthaltensein einer Komponente in einer anderen ab. Sie werden von der Komponente bereitgestellt, die andere Komponenten enthält und von den enthaltenen Teilkomponenten verbraucht.Ein Beispiel dafür sind die Steckplätze für Erweiterungen innerhalb eines PCs. • Flußressourcen repräsentieren Schnittstellen für den Informations-, Energie- und Materialfluß. Beispiele sind die Schnittstellen der TINA-Komponenten. • Umgebungsressourcen und Spezifikationsressourcen werden durch die zu lösende Konfigurierungsaufgabe bestimmt. Die Einordnung von Ressourcen in diese Klasse ist zum Modellierungszeitpunkt nicht möglich. Ressourcen, die in einer Konfigurierungsaufgabe vom System gefordert werden, können in einer andere Aufgabe auch als benötigte Ressourcen einer Komponente auftreten. Ebenso können Ressourcen in einer Konfiguration von der Systemumgebung und in einer anderen von Komponenten bereitgestellt werden. Die Ressourcen können desweiteren nach ihrem Datentyp unterschieden werden. Danach entscheidet sich auch, wie Bereitstellung und Bedarf dieser Ressourcen verglichen wird und wie der Verbrauch einer Ressource durch eine Komponente erfolgt. • Atomare Ressourcen entsprechen den Ressourcen, die schon von Heinrich [Hei89] eingeführt wurden. Eine Bereitstellung ist mit einer Forderung verträglich, wenn sie vom gleichen Typ oder eine Spezialisierung der geforderten ist. Atomare Ressourcen werden in den SCO-Dokumenten nicht explizit erwähnt, werden aber benutzt. So sind die verschiedenen Strukturressourcen hier einzuordnen. • Numerische Ressourcen sind eine Verallgemeinerung der exklusiven Ressourcen bei Heinrich. Eine bereitgestellte Ressource kann eine Forderung befriedigen, wenn ihr Wert größer oder gleich dem geforderten Wert ist. Beim Verbrauch wird dann der Wert um den der Forderung reduziert. Numerische Ressourcen sind erschöpft, wenn ihr Wert auf Null sinkt. Sie können dann keinen Bedarf mehr befriedigen. • Intervallressourcen haben als Wert ein Intervall. Sie sind ausgeglichen, wenn das benötigte Intervall innerhalb des bereitgestellten liegt. Die Versionen von Softwareprodukten stellen solche Intervallressourcen bereit. • Eigenschaftsbeschreibungen werden Ressourcen genannt, die eine Menge von Eigenschaften der Komponenten zusammenfassen. Bei ihnen muß der Verbrauch eine Teilmenge der Bereitstellung sein. Da auch andere Ressourcen Eigenschaften von Komponenten beschreiben, ist der Begriff mengenwertige Ressourcen als Bezeichnung prägnanter. Im Unterschied zu den in [BR96] eingeführten zusammengesetzten Ressourcen sind die Elemente der Menge keine Ressourcen. 17 2 Das Konfigurationssystem des SCO-Projektes Ein Spezialfall der mengenwertigen Ressourcen sind Ressourcen, bei denen die Menge nur ein Element enthalten kann. Diese könnten für jeden der möglichen Werte auch als separate atomare Ressource modelliert werden, was die Zahl der Ressourcen im Modell aber wesentlich erhöhen würde. Für die Ressourcen wurde ebenfalls eine Vererbungshierarchie eingeführt, was es erlaubt, die Zuordnung zu einer der vorgenannten Kategorien durch Vererbung zu modellieren. Alle Strukturressourcen erben z. B. von der abstrakten Ressource „StructureResource“, Intervallressourcen von „Counted“. Neben den Ressourcen und Komponenten gibt es noch Dienstmuster. Sie stellen Systemkomponenten dar, die zur Spezifikation der Konfigurationsaufgabe beim ressourcenorientierten Konfigurieren benutzt werden. Beschrieben werden sie durch Listen von initialen Ressourcenforderungen für die Kunden- bzw. Anbieterdomäne, die Bereitstellung von Ressourcen durch das System unterstützen sie jedoch nicht. Es lassen sich also aus den Dienstmustern nur Konfigurationen erstellen, die keine Ressourcen von der Systemumgebung benötigen. Die Einordnung der Dienstmuster in Kategorien erfolgt nur zur besseren Präsentation gegenüber den Nutzern des Konfigurationssystems [vLSN99]. Bei dem in SCO entwickelten Konfigurationssystem handelt es sich um ein interaktives System. Das System ermittelt zu einer Teilkonfiguration alle möglichen Vervollständigungen. Aus der Menge aller darin enthaltenen Komponenten kann der Nutzer die nächste hinzuzufügende Komponente auswählen. Das Verfahren zur Bestimmung der möglichen Vervollständigungen ist eine an die Erweiterungen des Modells angepaßte Form des in Abschnitt 1.3.1 vorgestellten Konfigurationsalgorithmus. Dieser wird in [vLSN99] so zusammengefaßt: 1. Ermittlung der aktuellen Bilanz 2. Auswahl einer unausgeglichenen Ressource 3. Auswahl einer Komponente, die diese Ressource bereitstellt 4. Auswahl eines Containers für die neue Komponente (anhand von Strukturressourcen) 5. Einfügen der Komponente in den Container 6. Falls kein Container gefunden werden kann: Fortsetzen mit 3 7. Aktualisierung der Bilanz 8. Falls es noch unausgeglichene Ressourcen gibt, fortsetzen mit 1 9. Ansonsten: Vollständige Konfiguration merken, Alternativen probieren 10. Dazu: Letzte eingefügte Komponente wieder entfernen, fortsetzen mit 4 11. Falls es keine alternativen Container mehr gibt: Letzte eingefügte Komponente wieder entfernen, fortsetzen mit 3 18 2.3 Aufbau des Konfigurationssystems Die Ermittlung der Bilanz erfolgt entlang der Zerlegungshierarchie. Bei der Bilanzierung eines Containers werden erst die enthaltenen Komponenten bilanziert. Danach werden die bereitgestellten und benötigten Ressourcen dieser Komponenten soweit möglich miteinander abgeglichen. Verbleiben nach diesem Containerinternen Abgleich noch offene Forderungen von Ressourcen, die nicht innerhalb dieses Containers ausgeglichen sein müssen (siehe oben), so werden diese den vom Container benötigten hinzugefügt und mit der Bilanzierung des übergeordneten Containers fortgefahren. Offene Forderungen von Ressourcen, die innerhalb des Containers auszugleichen sind, führen zu einem Konflikt, der durch das Hinzufügen von Komponenten in diesen Container eventuell noch zu lösen ist. 2.3 Aufbau des Konfigurationssystems Das Konfigurationssystem besteht aus zwei wesentlichen Teilen6 : • der grafischen Benutzeroberfläche, die die Darstellung der aktuellen (Teil-) Konfiguration übernimmt und die Benutzereingaben verarbeitet und • einem Konfigurationsserver, der den Katalog der Komponenten beinhaltet, die Bilanzierung der Ressourcen für die aktuelle Konfiguration durchführt und mögliche Vervollständigungen ermittelt sowie Installationsskripte erstellt. Der Konfigurationsserver implementiert dazu eine Reihe von CORBA-Schnittstellen. Wesentlich dabei sind die Schnittstelle „ComponentRepository“, die den Zugang zum Konfigurationssystem ermöglicht, und „Configuration“, die die Bearbeitung einer Konfiguration gestattet. Für die Details der Schnittstelle sei auf die IDL-Datei „composer.idl“ im Anhang B.1.1 verwiesen. 2.4 Die Komponentenbeschreibungssprache 2.4.1 Anforderungen In Konfigurationssystemen werden die Komponenten und ihre Beziehungen in der Regel durch spezielle Sprachen beschrieben. Für die Auswahl einer Komponentenbeschreibungssprache wurden im Projekt SCO eine Reihe von Kriterien festgelegt. Sie wurden in funktionale und nicht funktionale Aspekte gegliedert. Zu den funktionalen Aspekten wurden folgende Anforderungen gezählt: • Die Komponenten und Ressourcen sollen separate Konstrukte sein. • Die Spezialisierung von Komponenten und Ressourcen muß sich ausdrücken lassen. 6 In SCO wurde neben der hier beschriebenen Version auch eine TINA-Variante des Systems erzeugt. Diese wurde als ein TINA-Dienst mit den dazugehörigen zusätzliche Komponenten implementiert [vLSN99]. 19 2 Das Konfigurationssystem des SCO-Projektes • Die Definition von zusätzlichen Attributen zu den Komponenten muß möglich sein. Dabei handelt es sich um die Attribute für die Benutzerschnittstelle und die Installationsanweisungen. • Die Beschreibung des Bilanzierungs- und Verbrauchsverhalten von verschiedenen Ressourcentypen muß möglich sein. Vor allem sollen sich nachträglich noch Ressourcen hinzufügen lassen, die neue Beziehungen zwischen den Komponenten beschreiben. Als nichtfunktionale Anforderungen wurden identifiziert: • die leichte Erlernbarkeit der Notation, • die direkte Erkennbarkeit der Relationen zwischen Komponenten und Ressourcen in der textuellen Darstellung, • die Beschreibung der verbrauchten und bereitgestellten Ressourcen einer Komponente mit geringem Aufwand und • die Möglichkeit der dynamischen Verarbeitung der Beschreibungen durch das Konfigurationssystem. 2.4.2 Python als Beschreibungssprache Aus der Forderung, daß sich nicht weiter eingegrenztes Verhalten von Ressourcen in der Beschreibungssprache ausdrücken lassen soll, wird in [vLSN99] gefolgert, daß nur Programmiersprachen zur Beschreibung in Frage kommen. Die Wahl einer Programmiersprache wurde außerdem damit begründet, daß sich die Installationsanweisungen der Komponenten als Algorithmen in der Sprache ausdrücken lassen sollen [vLSN99]. Davon wurde aber kein Gebrauch gemacht, sondern diese Anweisungen als reine Zeichenketten abgelegt. In diesen Zeichenketten liegen die Installationsanleitungen in einer XML-ähnlichen Notation vor. Eine Folgerung, die in [vLSN99] aus der Forderung, daß Spezialisierung beschreibbar ist, getroffen wurde, ist, daß es sich um eine objektorientierte Sprache handeln muß. Die Spezialisierung ließe sich zwar auch mit anderen Sprachen darstellen, jedoch die Benutzung einer objektorientierten Sprache und der sprachenspezifischen Darstellung der Spezialisierung gewährleistet, daß die Vererbung von Attributen und Verhalten durch die Sprachimplementation erfolgt und nicht für das Konfigurationssystem realisiert werden muß. Aus der Forderung nach dynamischem Laden der Komponenten- und Ressourcenbeschreibungen wird gefolgert, daß nur interpretierte Sprachen geeignet sind. Gegen die Definition einer neuen und für die Verwendung einer bestehenden Sprache wird ausgeführt, daß eine neue Sprachdefinition sich am Anfang als nicht stabil erweisen würde und Anwendern garantiert unbekannt wäre [vLSN99]. Aus diesen Erwägungen heraus wurde in SCO als Beschreibungssprache Python gewählt. Dabei wurde die Sprache mit anderen weitverbreiteten Programmiersprachen verglichen. 20 2.4 Die Komponentenbeschreibungssprache Das Ressourcenmodell des Konfigurationsservers ist in zwei Pythondateien abgespeichert: eine für die Komponenten und eine für die Ressourcen. Die Datei „components.py“ beinhaltet die Komponenten. Jede Komponente wird durch eine eigene Python-Klasse repräsentiert. Die Vererbung von Komponenten spiegelt sich in der Vererbungsrelation zwischen den einzelnen Klassen wieder. Basis aller Komponentenklassen ist die Klasse Component, die in der Datei „SCOSupport.py“ definiert ist. Verbrauchte und bereitgestellte Ressourcen werden in Listen von RessourcenInstanzen abgespeichert. Auch die Eigenschaft, ob es sich um eine abstrakte Komponente handelt, ist als Attribut innerhalb der Klasse definiert. Das gleiche gilt für mögliche weitere Eigenschaften der Komponente, die für das eigentliche Modell nicht von Bedeutung sind.7 Als Beispiel sei hier die Beschreibung der Komponente TTest1 SF angeführt: class TTest1 SF(Service): info = "resources/empty.txt" icon48 = "images/java-floppy.gif" name = "TTest1 Service Factory" provides = [CorbaService("TTest1SF")] consumes = [ProviderSide(),Java((1,1,5)),DPEBootstrap(),DPE()] Diese Komponente ist eine Spezialisierung der Service-Komponente und verbraucht die Ressourcen ProviderSide, Java, DPEBootstrap und DPE. Die Ressource Java wird mit dem Wert (1,1,5) verbraucht. Bereitgestellt wird CorbaService mit dem Wert "TTest1SF". Wäre die Komponente TTest1 SF abstrakt8 , so wäre noch ein Eintrag abstract=1 vorhanden. Die Reihenfolge der einzelnen Attribute ist nicht festgelegt. Während in den Komponentenklassen keinerlei Verhalten beschrieben wird, ist das bei den Ressourcen nicht der Fall. Diese sind in der Datei „resources.py“ definiert. Auch für jede Ressource existiert eine eigene Klasse, Vererbung wird wiederum durch Vererbung der Pythonklassen beschrieben. Alle Ressourcen sind Spezialisierung der Klasse Resource aus „SCOSupport.py“. Dort ist ebenfalls die Ressource StructureResource definiert, die die Basisklasse für alle Strukturressourcen bildet. Feste Attribute wie bei den Komponenten gibt es bei den Ressourcen nicht.9 Statt dessen werden für einige der Ressourcen Funktionen definiert: init (self) initialisiert gegebenenfalls die Instanzen des Ressourcentyps. must balance at(self,component) gibt an, innerhalb welcher Komponenten dieser Ressourcentyp ausgeglichen sein muß. 7 Dabei handelt es sich um Informationen, die die Darstellung in der grafischen Benutzeroberfläche beeinflussen bzw. der Erstellung von Installationspaketen der fertigen Konfiguration dienen. 8 also eine Komponentenkategorie, siehe Abschnitt 2.2 9 Klassenattribute können jedoch auftauchen, falls sie für die im folgenden beschriebenen Funktionen benötigt werden. 21 2 Das Konfigurationssystem des SCO-Projektes provider for(self,consumed) berechnet, ob die übergebene Ressource durch diese Ressource bereitgestellt werden kann. Hier findet bei versionsbehafteten oder gezählten Ressourcen (Siehe 2.2) der Vergleich zwischen Verbrauch und Bedarf statt. consume(self,resource) zeigt der Ressourceninstanz an, daß sie durch den übergebenen Bedarf verbraucht wird. unconsume(self,resource) macht consume(self, resource) wieder rückgängig. exhausted(self) überprüft, ob die Instanz noch weiterhin einen Bedarf befriedigen kann oder erschöpft ist. Diese Funktionen werden in Ressourcen wie z. B. BalanceAtBasicHardware, BalanceAtDomain, ABI und VersionedSoftware benutzt. Sie bilden die Basis für die verschiedenen Arten von Bilanzierung und werden selbst nicht als verbrauchte oder bereitgestellte Ressource in einer der Komponenten auftauchen. Hier die Definition von VersionedSoftware als ein Beispiel. class VersionedSoftware(Resource,BalanceAtBasicHardware): def init (self,version): self.version = version def provider for(self,consumed): return isinstance(consumed,self. class ) and \ self.version >= consumed.version 2.5 Bisheriges Modellierungsverfahren Bisher wurde der Komponentenkatalog durch Verändern der entsprechenden Pythondateien von Hand erstellt. Das Wissen über die Komponenten und ihre Beziehungen stammte dabei aus Befragungen von Programmierern einzelner Bestandteile der TINA-Plattform und der Beispieldienste sowie von Personen, die Erfahrungen bei der Installation dieser gewonnen haben, durch den Entwickler des Konfigurationssystems. Nachteilig bei dieser Vorgehensweise ist, daß neue Komponenten und Ressourcen nur von Personen hinzugefügt werden können, die über die Struktur besagter Dateien und über die Syntax der Programmiersprache Python Kenntnis haben. 2.6 Darstellung des Komponentenkatalogs als HTML-Seite In einer Studienarbeit wurde vom Autor die dynamische Darstellung der Komponenten des Katalogs in einer HTML-Seite beschrieben [Kun00]. Das Ziel dabei war, eine Übersicht über die Komponenten und ihre Eigenschaften zu Informations- und Werbezwecken zu ermöglichen. 22 2.6 Darstellung des Komponentenkatalogs als HTML-Seite Um Informationen über die im Katalog vorhandenen Komponenten zu erhalten, werden dabei die CORBA-Schnittstellen zwischen dem Konfigurationssystem und dessen Benutzerschnittstelle benutzt (Abschnitt 2.3 und Anhang B.1.1). Diese ermöglicht es, die Eigenschaften einer namentlich bekannten Komponente zu ermitteln. Eine Liste aller Komponentennamen kann durch Abschreiten des Spezialisierungsbaumes ausgehend von einem bekannten Namen ermittelt werden. Aus dieser Liste aller Komponenten werden bestimmte Komponenten herausgefiltert, die nicht angezeigt werden sollen. Auch bietet der Filter die Möglichkeit, die Auswahl der angezeigten Komponenten durch den Benutzer weiter einzuschränken. Auf diesen Filter greift über eine weitere CORBA-Schnittstelle der HTML-Generator zu. Dieser generiert aus den erhaltenen Komponentenlisten dann HTML-Seiten. Abbildung 2.3 verdeutlicht die Beziehungen zwischen den Bestandteilen des Konfigurationssystems und dem HTML-Generator. WWW-Info::Filter Konfigurationsserver ComponentDesc WWW-Info::HTML-Generator ComponentRepository Composition Benutzeroberfläche Abbildung 2.3: Konfigurationssystem und WWW-Darstellung Die zur Informationsgewinnung benutzte CORBA-Schnittstelle zwischen dem Konfigurationssystem und dessen Benutzeroberfläche ist für die Wissenserfassung nicht geeignet. Sie bietet keine direkten Informationen über die Ressourcen und deren Bilanzierungsverhalten. Weiterhin ist kein schreibender Zugriff auf den Katalog möglich. Für die WWW-Darstellung wurden zusätzliche Eigenschaften von Komponenten in den Katalog eingetragen. Dies beeinträchtigt nicht die Funktion des Konfigurationssystems. Solch zusätzliche Eigenschaften sollten bei der Bearbeitung des Katalogs durch ein Werkzeug zum Wissenserwerb erhalten bleiben. 23 3 Das Werkzeug zum Wissenserwerb 3.1 Alternative Komponentenkataloge Wissenserwerb bedeutet, daß der Katalog mit den Komponenten und Ressourcen zu verändern oder neu zu erstellen ist. Daraus ergibt sich, daß die Funktion des Werkzeugs für den Wissenserwerb eng an die Implementierung des Katalogs gebunden ist. In der Regel wird das Modellierungswerkzeug parallel zu dem Konfigurationssystem entwickelt, so daß gewährleistet ist, daß beide mit dem Katalog funktionieren. Im Projekt SCO war die Werkzeugunstützung beim Wissenserwerb nicht vorgesehen [vLSN99]. Deswegen wurden bei der Wahl der Programmiersprache Python als Komponentenkatalog die Erfordernisse des werkzeugunterstützten Wissenserwerbs nicht berücksichtigt. Bei der Erstellung des Werkzeuges ist deshalb die Frage zu behandeln, ob die bestehende Form des Katalogs weiterhin verwendet werden kann oder ob eine andere Form benötigt wird und welche anderen Formate sinnvoll wären. Für die Bewertung der verschiedenen Möglichkeiten werden folgende Kriterien benutzt: • Kriterien, die unbedingt zu erfüllen sind: – Der Komponentenkatalog muß in das Konfigurationssystem integrierbar sein. – Das Format muß das Ressourcenmodell mit den Erweiterungen von SCO widerspiegeln. – Die Bearbeitung des Katalogs durch das Modellierungswerkzeug muß möglich sein. – Die Erweiterung um neue Ressourcentypen mit eigenem Bilanzierungsverhalten muß möglich sein. Die Verhaltensbeschreibung muß dabei anzeigbar und veränderbar sein. • Kriterien zur Beurteilung von Lösungen, die die vorherigen Kriterien erfüllen: – Die Erweiterung der Komponenten um neue Eigenschaften (z. B. die in 2.6 erwähnten) muß möglich sein. – Die Integration in das bestehende Konfigurationssystem soll mit geringem Aufwand verbunden sein. – Die Anforderungen des Komponentenkatalogs an die Hardware-Ausstattung sollten gering sein. 24 3.1 Alternative Komponentenkataloge – Der Aufwand zur Installation und Pflege der zur Laufzeit benötigten Technologien sollte gering sein. – Die Erfassung der Komponenten durch verschiedene Anwender an verschiedenen Arbeitsplätzen sollte möglich sein. Im folgenden werden mehrere Lösungen näher beschrieben: die Benutzung eines relationalen Datenbanksystems (3.1.2), die Verwendung einer Datei-basierten Lösung mit einer anderen Beschreibungssprache (3.1.3) und die Beibehaltung der bisherigen Speicherlösung (3.1.4). Vorher werden einige generelle Ausführungen zur Integration neuer Komponentenkataloge in das Konfigurationssystem gemacht. 3.1.1 Integration neuer Kataloge in das Konfigurationssystem Beim Einsatz einer neuen Lösung für den Komponentenkatalog ergeben sich zwei generelle Probleme: die Integration in das Konfigurationssystem und die Umwandlung bestehender Kataloge. Um einen neuen Katalog im Konfigurationssystem verwenden zu können, muß der Mechanismus, mit dem die Komponenten und Ressourcen dynamisch geladen werden, so verändert werden, daß die Informationen aus dem neuen Katalog gewonnen werden. Durch den Aufruf der folgenden Funktion wird der Katalog vom Konfigurationssystem gelesen: def load(self, module): import types d = {} exec "import "+module in d m = d[module] . . . Die übergebene Variable module enthält den Namen des zu ladenden Moduls. Daraus wird zur Laufzeit eine import-Anweisung mit dem Modulnamen konstruiert und diese Abweisung dann ausgeführt. Danach enthält das assoziative Feld d die im Verlauf der Anweisung belegten Namen. Mit dem Modulnamen als Schlüssel ist dabei eine Variable vom Typ ModuleType1 enthalten. Im weiteren Verlauf der load()-Funktion werden dann aus dieser Modul-Variablen alle Klassen-Variablen, die von den Basisklasssen Resource bzw Component abgeleitet sind, ermittelt und zur späteren Verarbeitung abgespeichert. Um ohne Änderung des Konfigurationssystems einen neuen Komponentenkatalog zu verwenden, wäre es notwendig, bei einem import der Dateien „resources.py“ und „components.py“ aus dem Katalog den Quelltext für die Klassen für Komponenten und Ressourcen zu erzeugen, diesen zu interpretieren und das Ergebnis dem Namensraum des zu importierenden Moduls hinzuzufügen. 1 Ein Python-interner Datentyp, der Module der Sprache repräsentiert. In Python sind Module, Klassen und Funktionen wiederum Objekte, die sich an Variablen zuweisen lassen. 25 3 Das Werkzeug zum Wissenserwerb Die Erzeugung von Python-Quelltext ist von der Art des Katalogs abhängig und wird in dem jeweiligen Abschnitt diskutiert. Durch die Verwendung der exec-Anweisung kann dieser dann interpretiert werden. Falls für die QuelltextGenerierung andere Module geladen werden oder Klassen, Funktionen oder Variablen benutzt werden, die außerhalb des Moduls nicht gesehen werden sollen, so können sie nach Benutzung mit del wieder gelöscht werden. Tiefergehende Informationen zur Arbeitsweise von import sowie zum dynamischen Ausführen von Anweisungen mit exec finden sich im Buch „Python 2“ [vLF00] sowie im „Python Reference Manual“ [vR00]. Neben den Klassendefinitionen steht in den Dateien „resources.py“ und „components.py“ nur jeweils eine import-Anweisung in der ersten Zeile. Der Komponentenkatalog sollte diese Anweisungen vor dem Laden auch ausführen, um die Funktion des bisherigen Katalogs zu simulieren. Besser noch wäre es, wenn jedweder Quelltext außerhalb der Klassendefinitionen erhalten bliebe und beim import an der „richtigen“ Stelle2 ausgeführt würde. Die Einbindung eines neuen Katalogs ist somit ohne Änderung des Konfigurationssystems möglich, wenn sich aus diesem dynamisch der Python-Quelltext erzeugen läßt, der der alten Katalogform entspricht. Unter anderem ist es wichtig, daß die Beschreibung der Eigenschaften in dem neuen Katalog sich in Python überführen läßt.3 Das zweite Problem, das bei jedem Wechsel des Katalogs zu beachten ist, ist die Umwandlung des alten Katalogformats in das neue Format. Der Beispiel-Katalog, der in SCO verwendet wurde, enthält nur wenige Komponenten und Ressourcen (41 bzw. 31). Diese könnten mit dem zu entwickelnden Modellierungswerkzeug in den neuen Katalog eingefügt werden. Bei einem größeren Katalog wäre das aufwendig und fehleranfällig. Dort wäre eine automatische Konvertierung wünschenswert. Dazu müßten die Informationen aus dem bestehenden Katalog extrahiert werden, was bedeutet, daß das Extrahieren der Daten aus dem bestehenden Katalog implementiert werden müßte. Dies entspricht schon der Lösung einer wesentlichen Teilaufgabe bei der Verarbeitung des bestehenden Katalogs. 3.1.2 Relationale Datenbank Management Systeme Für die dauerhafte Speicherung großer, gut strukturierter Datenmengen werden oft Relationale Datenbanksysteme eingesetzt. Relationale Datenbank Management Systeme (RDBMS) basieren auf dem relationalen Datenmodell. Nach diesem Modell werden alle Daten durch Relationen dargestellt. Relationen sind Mengen von Tupeln, die einem Relationenschema genügen. Nach „Relationale Datenbanken und SQL“ [MU97] ist [. . . ] ein Relationenschema s [. . . ] gegeben durch eine (endliche) 2 Die Ermittlung dieser Stelle ist nicht immer trivial, da sich im Laufe des Wissenserwerbs die Reihenfolge der Klassen ändern kann. 3 z. B. nur erlaubte Zeichen für Bezeichner 26 3.1 Alternative Komponentenkataloge Menge A von Bezeichnern (Attribut-Bezeichner) und einer Zuordnung, die jedem Attributbezeichner aus A eine Domäne s(a) zuordnet. Mit Domänen werden die Wertebereiche für die Attribute bezeichnet. Ein Tupel ist eine Menge von Paaren aus Attributbezeichnern und Werten. Es genügt genau dann einem Schema, wenn es nur Attributbezeichner des Schemas enthält und die jeweiligen Werte aus der für diesen Bezeichner im Schema festgelegten Domäne stammen. Die Tupel werden durch die Werte ihrer Attribute identifiziert. Da Relationen Mengen sind, spielt die Reihenfolge der Tupel keine Rolle. Eine Teilmenge der Attribute, durch die jedes Tupel der Relation eindeutig identifiziert wird, wird Schlüssel genannt. Beziehungen zwischen verschiedenen Relationen werden durch das Einfügen von Fremdschlüsseln in einer Relation ausgedrückt. Das sind Attribute, deren Domäne die Menge aller Schlüssel einer anderen Relation sind, zu der diese Relation in Beziehung steht. Die Darstellung der Relationen erfolgt in der Regel in Form von Tabellen mit den Attributbezeichnern als Spaltenüberschrift. Die Zeilen der Tabelle entsprechen den Tupeln mit den Attributwerten in den durch die Bezeichner bestimmten Spalten. Die Bestandteile des Komponentenkatalogs lassen sich folgendermaßen in das relationale Datenmodell abbilden: • Je eine Relation für die Komponenten und Ressourcen mit ihren elementaren Eigenschaften. Als Schlüssel dient der Name. • Eine Relation, die Komponentennamen zusammen mit den Namen der verbrauchten Ressourcen dieser Komponente und dem eventuellen Wert enthält. • Eine weitere Relation für die Bereitstellung von Ressourcen. • Um Mehrfachvererbung darstellen zu können, ist es erforderlich, eine separate Tabelle anzulegen, in der vererbende und erbende Klasse gegenübergestellt werden.4 • Zusätzliche Eigenschaften lassen sich durch die Erweiterung der Relationen um neue Attribute darstellen. In den Tabellen 3.1, 3.2 und 3.3 wird die Modellierung in Relationen für einige Komponenten angedeutet. Die Vererbung wird vom relationalen Datenmodell nicht unterstützt, d. h. die erbende Klasse hat nicht automatisch die Eigenschaften ihrer Basisklasse. Dies sicherzustellen ist Aufgabe des Konfigurationssystems. Die Erweiterung der Komponenten um neue Eigenschaften kann entweder durch Hinzufügen von Spalten in der Komponentenrelation oder durch Anlegen neuer Relationen erfolgen. 4 Die Zergliederung des Katalogs in viele verschiedene Relationen ergibt sich aus der Normalisierung der Relationen. Dies ist eine zentrale Forderung bei Relationalen Datenbanksystemen [MU97] 27 3 Das Werkzeug zum Wissenserwerb Klassenname Langer Name abstrakt Infotext Software TestJar PAJar NULL Java Test Services Java Provider Agent Y N N NULL resources/testServices.txt resources/pa.txt Tabelle 3.1: Komponenteneigenschaften als Relation Komponente Ressource Wert Komponente erbt von Software PAJar PAJar TestJar TestJar TestJar InComputer Java CorbaJava TinaJava Java Swing NULL (1,1,5) „VisiBroker“ NULL (1,1,5) (1,0,1) TTest1_SF TestJar PAJar Software Product OperatingSystem Service Service ServiceSurround Component Software Software Tabelle 3.2: Ressourcenverbrauch Tabelle 3.3: Komponentenvererbung Auf die Daten einer Datenbank wird üblicherweise mit einer Datenmanipulationssprache zugegriffen. Bei den relationalen Datenbanken hat sich die Sprache SQL als Standard etabliert [MU97]. SQL erlaubt sowohl die Definition und Manipulation von Relationenschemata als auch die Abfrage und Veränderung der Daten in den Relationen. Für die verschiedenen Datenbanksysteme existieren Bibliotheken um aus Programmen bestimmter Programmiersprachen heraus SQLAnfragen an das Datenbanksystem zu stellen. Auch für Python bestehen einige solcher Bibliotheken zur Datenbankanbindung [PyD01]. Das Bilanzierungsverhalten läßt sich mit den Mitteln relationaler Datenbanken nicht ohne weiteres darstellen. Eine mögliche Lösung wäre es, das Verhalten in einer Programmiersprache auszudrücken und in Datentypen des RDBMS (z. B. Zeichenketten) abzulegen. Für die Interpretation dieser Daten als Verhaltensbeschreibung ist der Mechanismus zum Laden der Komponenten und Ressourcen aus dem Katalog verantwortlich. Die Wahl von Python als Programmiersprache ist hier naheliegend. Wird das Verhalten in Python beschrieben, so ist die Generierung des PythonQuelltextes, der dem alten Katalog entspricht (siehe Abschnitt 3.1.1), trivial: für jede Komponente bzw. Ressource wird eine Klassendefinition konstruiert, in die Zuweisungen für die Eigenschaften und Funktionsdefinitionen für das Verhalten eingefügt werden. Die Benutzung einer relationalen Datenbank durch das in Python geschriebene Konfigurationssystem wäre also prinzipiell möglich. Auch der Zugriff durch ein Wissenserwerb-Werkzeug ist durch die vorgenannten Programmbibliotheken realisierbar. 28 3.1 Alternative Komponentenkataloge Der parallele Zugriff auf RDBMS wird in der Regel durch sogenannte Sperren abgesichert. Dabei wird beim lesenden Zugriff auf ein Datum dieses für Veränderungen gesperrt. Soll das Datum durch eine Operation verändert werden, so wird es auch für den lesenden Zugriff gesperrt. Nach Ausführung der Aktion wird die Sperre wieder entfernt. Die Details der Behandlung von Sperren, wie Granularität (Tabelle, Spalte), implizites oder explizites Sperren und Auflösung von Verklemmungen (Deadlocks) sind für die einzelnen RDBMS unterschiedlich [MU97]. Auch für den Netzwerkzugriff bieten die verschiedenen RDBMS Lösungen an. RDBMS sind komplexe Produkte, die u. U. hohe Anforderungen an die Hardware stellen5 und deren Installation und Wartung umfangreiche Kenntnisse erfordern. 3.1.3 Eine andere Beschreibungssprache Neben dem Verwenden von Datenbanken ist die Ablage des Katalogs in Dateien möglich, die in einer bestimmten Beschreibungssprache formuliert sind. Aufgrund der Anforderung, Verhalten zu beschreiben, wurde in SCO die Verwendung einer Programmiersprache beschlossen. Dabei wurde die Sprache Python gegenüber anderen Programmiersprachen vorgezogen (Abschnitt 2.4). Eine andere Programmiersprache wäre aus Sicht des Modellierungswerkzeuges zu erwägen, wenn deren Verarbeitung wesentlich einfacher wäre und diese sich im Konfigurationssystem mit wenig Änderungen verwenden ließe. Um aus Dateien einer Programmiersprache Informationen über ihren Inhalt zu erhalten, ist es erforderlich, sie in die Syntaxkonstrukte der Sprache zu zerlegen. Dieser Vorgang wird allgemein Parsen genannt. Parser sind Bestandteile aller Compiler und Interpreter [ASU92]. Neben von Hand erzeugten Parsern gibt es auch Parser-Generatoren, die aus einer Beschreibung der Syntax (der Grammatik) einen Parser für die Sprache erzeugen. Dieser läßt sich dann in andere Programme einbinden. Prominenteste Vertreter dieser Parser-Generatoren sind die Programme YACC bzw. BISON. Parser liefern als Ergebnis Baumstrukturen, die die Zerlegung der Eingabe in die in der Grammatik der Sprache definierten Teile widergeben. Die benötigten Informationen müssen dann aus diesen Bäumen heraus gesammelt werden. Das Programm KIMWITU [vEB00] generiert C-Programme, die Operationen auf solchen Bäumen ausführen. Zum Abspeichern des veränderten Kataloges ist auch die Erzeugung von Quelltext in der Beschreibungssprache notwendig. Eine der Operationen, die mit KIMWI TU erzeugte Programme ermöglichen, ist der entgegengesetzte Vorgang zum Parsen – das Unparsen. Beim Unparsen wird der Syntax-Baum wieder in seine textuelle Repräsentation überführt. Ebenso wie das Parsen läßt sich das Unparsen auch ohne den Einsatz von durch Generatoren erzeugten Programmen realisieren. Dabei 5 Für das RDBMS Oracle8i wird als Mindestvoraussetzung 128MB Hauptspeicher, 400MB Auslagerungsspeicher und 600MB Plattenplatz angegeben. Dabei ist der Bedarf für die eigentlichen Daten noch nicht berücksichtigt. 29 3 Das Werkzeug zum Wissenserwerb muß der Syntax-Baum komplett abgegangen werden und für jeden Knoten (der ein Syntax-Konstrukt beschreibt) die Text-Repräsentation ausgegeben werden. Ob eine Erweiterung um neue Komponenteneigenschaften möglich ist, hängt von der gewählten Sprache ab. Objektorientierte Sprachen, die nach SCO besonders für die Beschreibung des Modells geeignet sind [vLSN99], bieten die Möglichkeit, diese als neue Klassenattribute einzuführen. Wird für die Komponentenbeschreibung eine andere Programmiersprache als Python benutzt, so ist die Frage zu lösen, wie das Verhalten der Ressourcen ausgedrückt werden soll. Wird es in Programmfragmenten der neuen Programmiersprache beschrieben, ist daraus beim Laden automatisch Python-Quelltext zu erzeugen, der dann interpretiert werden kann.6 Eine solche automatische Übersetzung ist nur bei einer Sprache, deren Struktur Python ähnelt, möglich. Die Alternative wäre es, das Verhalten als Python-Quelltext in Zeichenketten oder anderen Datentypen der Programmiersprache zu speichern. Damit würden eventuelle Vorteile des Sprachwechsels wieder verloren gehen, da zur Beschreibung des Verhaltens wieder nur Python zur Verfügung steht. Für die Beschreibung der statischen Eigenschaften bietet Python für jedes Konstrukt des erweiterten Ressourcenmodells ausreichende Ausdrucksmöglichkeiten [vLSN99]. Deswegen ist der Wechsel zu einer anderen Programmiersprache nicht sinnvoll. Falls eine andere Beschreibungssprache als eine Programmiersprache gewählt wird, so ist das Verhalten ebenfalls durch Code-Fragmente innerhalb dieser zu beschreiben. Das hat ebenfalls die oben erwähnten Nachteile. Auch hier ist die Zerlegung der Beschreibung in die syntaktische Struktur notwendig. Das Parsen könnte u. U. einfacher sein, da nicht die komplexen Strukturen einer Programmiersprache verarbeitet werden müßten. Eine in Frage kommende Sprache ist die „Extensible Markup Language“ (XML) [XML01]. Diese Sprache ist vom „World Wide Web Consortium“ standardisiert und dient dem Austausch von strukturierten Daten. Es gibt zwei Arten von XMLDokumenten: zum einen Dokumente, deren mögliche Struktur in einer „Document Type Declaration“ (DTD) festgelegt ist. Diese Dokumente werden als valid (gültig) bezeichnet. XML-Dokumente, deren Struktur nicht näher beschränkt ist, die aber dem XML-Standard genügen, heißen well-formed (wohlgeformt). XML kennt keine Verhaltensbeschreibungen und keine Datentypen. Alles, was nicht die Struktur beschreibt, wird als Text interpretiert. Der Anwendung obliegt es, diesen Text zu interpretieren. Für das Speichern der Verhaltensbeschreibungen ergeben sich dann die gleichen Probleme wie bei der Verwendung von Datenbanken. Für das Verarbeiten von XML-Dokumenten steht eine Reihe von Werkzeugen zur Verfügung. Dabei existieren zwei Ansätze: SAX (Simple API for XML) liest das XML-Dokument und generiert beim Auffinden 6 Die Änderung des Konfigurationssystems dahingehend, daß es intern eine andere Beschreibungssprache verwendet, würde einen unvertretbar hohen Aufwand erfordern. Außerdem sind die Argumente gegen andere Sprachen, die in [vLSN99] dargelegt wurden, zu berücksichtigen. 30 3.1 Alternative Komponentenkataloge von Strukturelementen Ereignisse, die über eine Schnittstelle an ein externes Programm gesendet werden können. Dieses kann dann diese Signale weiterverarbeiten. DOM (Document Object Model) ist eine abstrakte Schnittstelle, um Syntax-Bäume von XML-Dokumenten zu erzeugen und auf diesen Operationen durchzuführen [ABC+ 98]. Es existieren verschiedene Implementierungen dieser Schnittstelle. Die Verarbeitung von XML-Dokumenten durch das Wissenserwerb-Werkzeug ist also möglich. Wie einfach die Generierung des Python-Code ist, hängt davon ab, wie die Struktur des XML-Dokumentes gestaltet ist. Im Abschnitt B.2 wird ein Beispiel für einen Komponentenkatalog als wohlgeformtes XML-Dokument gegeben, dessen Struktur sehr eng an die der originalen Pythondateien angelehnt ist. Dieses Beispiel zeigt auch, daß die Erweiterung von Komponenten um neue Eigenschaften möglich ist. Wird für den Katalog eine Datei-basierte Lösung gewählt, so ist die Konsistenz beim gleichzeitigen Verändern der Dateien durch verschiedene Nutzer erst einmal nicht gewährleistet. Ist dies erwünscht, so wäre dies durch das Werkzeug zur Wissenserfassung zu verwirklichen. Ein Teil der möglichen Probleme ließe sich auch durch die Verwendung einer Versionsverwaltungsoftware wie CVS vermeiden.7 Der Zugriff auf den Katalog von verschiedenen Arbeitsplätzen läßt sich durch die Trennung des Wissenserwerbs in eine zentrale Katalog-Komponente und verteilte Benutzerschnittstellen ermöglichen. 3.1.4 Beibehaltung des bestehenden Katalogs Wird die bestehende Form des Komponentenkatalogs beibehalten, so entfällt die Integration in das Konfigurationssystem. Diese Form wurde in SCO gerade deswegen gewählt, da sie sehr gut das Ressourcenmodell widerspiegelt und sich das Bilanzierungsverhalten ausdrücken läßt. Zwei Möglichkeiten, durch das Wissenserwerb-Werkzeug auf den bestehenden Katalog zuzugreifen, sind denkbar: das Importieren der Dateien in ein PythonProgramm und das Parsen der Dateien. Import der Dateien Die erste Variante, das Importieren der Dateien in ein Python-Programm, wäre analog zur Ladeoperation in SCO (siehe 3.1.1). Danach wären die in den Dateien definierten Klassen in einem assoziativen Feld enthalten. Über die darin gespeicherten Klassen-wertigen Objekten wären Informationen über die jeweiligen Basisklassen (<Klassenobjekt>. bases ) und die in der 7 Die Benutzer würden dann lokale Kopien des Katalogs bearbeiten und diese danach in die Versionsverwaltung einbringen. Dabei würden sie auf gleichzeitig erfolgte Änderungen durch andere Nutzer aufmerksam gemacht werden. Die Auflösung der Konflikte müßte jedoch per Hand erfolgen und würde wieder Wissen über die Dateistruktur und Pythonsyntax erfordern. 31 3 Das Werkzeug zum Wissenserwerb Klasse definierten Variablen und deren Werte (<Klassenobjekt>. dict ) ermittelbar. Im Komponentenkatalog existieren in den verschiedenen Klassendefinitionen unterschiedliche Arten der Festlegung von Klassen-Variablen. Zum einen werden den Variablen Konstanten von elementaren Datentypen (in der Regel Zeichenketten) zugewiesen. Dabei handelt es sich um die Attribute, die zur Darstellung in der Benutzerschnittstelle des Konfigurationssystems bzw. zum Erzeugen von Installationsanweisungen benötigt werden. Auch zusätzliche Eigenschaften von Komponenten würden so eingetragen werden. Der Wert dieser Variablen läßt sich leicht durch <Klassenobjekt>.<Variable> ermitteln. Zum anderen kommen als Klassenvariablen Listen von Instanzen anderer Klassen vor. Das sind die Listen mit verbrauchten und bereitgestellten Ressourcen. Um welche Klassen es sich dabei jeweils handelt, ist ermittelbar (<Instanz>. class ). Allerdings ist es nicht möglich, zu ermitteln, mit welchen Parametern diese Instanzen erzeugt wurden. Parameter werden z. B. bei der Instantiierung von Numerischen Ressourcen (die Anzahl) sowie von Intervallressourcen benutzt. In diesen Fällen werden die Parameter nur in Variablen der Instanz abgelegt, aus denen sie sich ermitteln lassen. Es kann aber generell nicht ausgeschlossen werden, daß später Ressourcen hinzugefügt werden, deren Instanzen auch parametrisiert werden. Um auch bei Instanzen dieser neuen Ressourcen später feststellen zu können, mit welchen Werten sie erzeugt wurden, wäre eine Konvention notwendig, nach der sich die Initialisierungsparameter ermitteln lassen. Es ist in Python üblich, die Klassenfunktion repr () so zu definieren, daß sie einen Ausdruck zurückgibt, dessen Ausführung als Ergebnis eine Kopie der Instanz liefern würde. Wenn für alle Ressourcen des Katalogs diese Funktion so definiert wurde, lassen sich mit repr(<Instanz>) die Initialisierungsparameter ermitteln. Die Klassen für die Ressourcen haben weiterhin Variablen, die die MemberFunktionen der Klasse (das Bilanzierungsverhalten) beinhalten. Das Verhalten dieser Funktionen wird in einer internen Darstellung, dem Bytecode, abgespeichert. Das Programm DECOMPYLE [Goe01] ist in der Lage, aus dem Bytecode wieder Quelltext zu generieren. Vom Autor wird das Programm allerdings als „work in progress“ eingestuft. Versuche mit dem Bytecode, der aus den Dateien „resources.py“ und „components.py“ erzeugt wurde, liefern korrekten und mit dem ursprünglichen Quelltext in der Funktion gleichenden Quelltext. Es wäre also möglich, den Quelltext zu den Klassenfunktionen innerhalb des Modellierungswerkzeuges zu erhalten. Die in 3.1.1 angesprochenen Anweisungen, die außerhalb der Klassendefinitionen stehen können, werden beim Importieren ausgeführt. Ihr Quelltext läßt sich auf diesem Weg nicht ermitteln und somit beim Abspeichern auch nicht wieder herstellen. Außerdem können diese Anweisungen Seiteneffekte haben, die das Wissenserwerb-Werkzeug beeinflussen können. Einer dieser Seiteneffekte ist, daß auch die von „ressources.py“ und „components.py“ importierten Module sowie alle davon abhängenden installiert sein 32 3.1 Alternative Komponentenkataloge müssen. Für die Modellierung sind diese Module aber unerheblich und sollten nicht benötigt werden. Parsen der Katalogdateien Die andere Möglichkeit, aus dem WissenserwerbWerkzeug auf den bestehenden Katalog zuzugreifen, ist die des Parsens der Dateien. Hierfür gilt generell, was schon zum Parsen anderer Programmiersprachen gesagt wird (Abschnitt 3.1.3). 3.1.5 Fazit Alle der betrachteten Möglichkeiten erfüllen die in Abschnitt 3.1 angegeben harten Kriterien. Sie lassen sich in das Konfigurationssystem integrieren, sie spiegeln das Ressourcenmodell wider, die Bearbeitung durch ein Modellierungswerkzeug und das Hinzufügen neuer Ressourcentypen ist möglich.8 RDBMS erlauben durch den intelligenten Einsatz von Sperrmechanismen die parallele Wissenserfassung durch mehrere Anwender und den gleichzeitige Zugriff durch Wissenserwerb und Konfigurationssystem auf den Katalog ohne zusätzlichen Aufwand. Da in der Regel der Netzwerkzugriff auf RDBMS möglich ist, können die Anwender auch von verschiedenen Rechnern aus die Wissenseingabe durchführen. Die Erweiterung der Komponenten um neue Eigenschaften ist möglich. RDBMS haben vergleichsweise hohe Anforderungen an die Hardwareausstattung und die Pflege und Installation. Der Aufwand zur Integration in das Konfigurationssystem läßt sich erst nach einer prototypischen Lösung abschließend beurteilen. Aufgrund der bestehenden Programmbibliotheken scheint er jedoch gering. Die Vorteile eines RDBMS-basierten Katalogs wiegen die Nachteile nur für große Kataloge mit vielen Komponenten auf, die häufig von verschiedenen Anwendern gleichzeitig verändert werden sollen. Die Verwendung einer anderen Beschreibungssprache stellt nur geringen Aufwand an die Hardwareausstattung, es werden zur Laufzeit keine zusätzlichen Technologien benötigt. Die Erweiterbarkeit der Komponenten um neue Eigenschaften ist abhängig von der Sprache, ist aber bei geeigneter Wahl möglich. Die parallele Wissenserfassung wird nicht unterstützt und wäre durch das ModellierungsWerkzeug zu ermöglichen. Der Aufwand zur Integration in das Konfigurationssystem ist beim Einsatz von Parser-/Unparser-Generatoren gering. Die Verwendung einer anderen Beschreibungssprache bietet allerdings keine Vorteile gegenüber der bisherigen Lösung. Allein XML könnte, durch die Festlegung einer geeigneten DTD, zum Datentransfer zwischen verschiedenen (ressourcenorientierten) Konfigurationssystemen benutzt werden. Sollen auch Informationen über das Bilanzierungsverhalten von Ressourcen ausgetauscht werden, so ist eine neutrale Notation dafür zu finden. 8 Für die Verhaltensbeschreibung der Ressourcen muß dabei u. U. auf eine Programmiersprache zurückgegriffen werden. 33 3 Das Werkzeug zum Wissenserwerb Wird der bestehende Katalog weiterverwendet, so entfällt der Aufwand für die Integration in das Konfigurationssystem und die Umwandlung des Katalogs in ein neues Format. Die Erweiterung von Komponenten um neue Eigenschaften ist in diesem Katalog ohne weiteres möglich [Kun00]. Zur Laufzeit sind, außer einer Python-Installation, keine weiteren Technologien notwendig, die Anforderungen an die Hardware sind gering. Die Erfassung der Komponenten durch verschiedene Anwender an verschiedenen Arbeitsplätzen wäre bei dieser Katalogform gesondert zu implementieren. Sowohl die Verwendung eines RDBMS als auch des bestehenden KatalogFormates haben Vor- und Nachteile. Aufgrund des Aufwandes für die Installation eines RDBMS wurde für den Prototypen des Werkzeugs die bestehende Katalogform weiterverwendet. Dabei wurde die Parser-Lösung verwendet, da auf dem anderen Weg nicht alle Informationen ermittelbar sind. Die Implementierung ist in Abschnitt 3.4 beschrieben. 3.2 3.2.1 Techniken für Benutzerschnittstellen Arten der Mensch-Rechner-Interaktion Bei der Wissenserfassung durch das Werkzeug sind eine Reihe von Interaktionen zwischen Nutzer und Programm notwendig. Dabei ist zu beachten, daß die Gestaltung dieser Interaktionen für den Benutzer nachvollziehbar ist. Die Gesamtheit der möglichen Interaktionen wird Benutzungs- oder Benutzerschnittstelle genannt. Theoretische Betrachtungen der Mensch-Rechner-Interaktionen sind ein Teilgebiet der Forschung zu Mensch-Maschine-Systemen. In der Literatur werden verschieden Arten von Mensch-Rechner-Interaktionen betrachtet [BHO+ 88]: Kommandotechnik Die Kommandotechnik erlaubt es dem Benutzer, Bearbeitungsfolgen aus einer Reihe von elementaren Kommandos zusammenzusetzen. Die Kommandotechnik ist sehr flexibel und erlaubt gegebenenfalls Interaktionen, die bei der Erstellung der Benutzungsschnittstelle nicht vorgesehen waren. Sie ist besonders für unstrukturierte Aufgaben geeignet. Falls der Benutzer die Kommandos im Gedächtnis gespeichert hat, ist der Zeitaufwand für das Ausführen einer Aufgabe gering, da das Suchen auf dem Bildschirm nach der richtigen Interaktion entfällt. Dagegen ist der Lern- und Gedächtnisaufwand sehr hoch. Das Erkennen neuer Interaktionsmöglichkeiten (d. h. unbekannter Kommandos) ist nur schwer möglich. Deshalb ist diese Interaktionsform nur für Experten und routinierte Benutzer angemessen. Die Wissenserfassung soll aber gerade auch Nichtexperten und unerfahrenen Benutzern möglich sein. Aus diesem Grund ist diese Art der Benutzerinteraktion nicht oder nur als Ergänzung zu den anderen Interaktionsformen für den Wissenserwerb geeignet. 34 3.2 Techniken für Benutzerschnittstellen Menütechnik Bei der Menütechnik werden dem Benutzer Menüs mit Symbolen oder Beschreibungen möglicher Aktionen angeboten, aus denen er auswählt. Vorteil der Menütechnik ist, daß das Gedächtnis im Vergleich zur Kommandotechnik entlastet wird. Das Erkennen der möglichen Interaktionen ist sehr einfach. Durch die Verwendung von z. B. Tastaturkürzeln ist eine Kombination mit der Kommandotechnik gut möglich. Ein Nachteil der Menütechnik ist, ein hoher Platzbedarf, da alle möglichen Aktionen angezeigt werden müssen. Zur Anwendung kommt diese Interaktionsart oft als Ersatz für die Kommandotechnik, wenn die Aktionen sich vorher bestimmen lassen und ihre Zahl klein ist. Solche Aktionen sind in dem hier betrachteten Anwendungsgebiet das Laden und Speichern des Modells, die Auswahl einer Komponente oder Ressource und der darauf auszuführenden Aktion (Löschen, Kopieren usw.) sowie das Beenden der Arbeit mit dem Werkzeug. Formulartechnik Die Formulartechnik ist Papierformularen nachempfunden. Der Benutzer trägt in ihm dargebotene Formulare bestimmte Werte ein. Sie wird besonders dort eingesetzt, wo strukturierte Daten zu erfassen sind. Bei geeigneter Gestaltung der Formulare bietet sie eine gute Benutzerführung und ist selbstbeschreibend. Bei der Wissenserfassung stellen die Namen der Komponenten und Ressourcen solche strukturierten Daten dar. Für ihre Bearbeitung ist die Formulartechnik geeignet. Direktmanipulation Mit Direktmanipulation werden Interaktionsformen bezeichnet, bei denen Benutzeraktionen direkte Veränderungen von Objekten auf dem Bildschirm bewirken (z. B. Verschieben von Objekten mit einer Maus9 ). Die Direktmanipulation wird u. a. eingesetzt, wenn numerische Werte eingestellt werden sollen (z. B. durch Darstellung als Schieberegler) oder sich die Veränderung von Objekteigenschaften gut darstellen lassen. Auch das Auslösen von Aktionen durch Ziehen von Objekten mit der Maus in vordefinierte Regionen – sogenanntes Drag & Drop – ist eine Form der Direktmanipulation. Da jede dieser Techniken ihre Vor- und Nachteile hat, treten in Programmen in der Regel Kombinationen von ihnen auf [BHO+ 88]. 3.2.2 Technische Möglichkeiten der Umsetzung Bei Benutzerschnittstellen von Programmen, die auf Standard-PC’s arbeiten sollen, werden häufig die folgenden Techniken für Benutzerschnittstellen verwendet: Textbasierte Benutzerschnittstellen Dies ist eine der ältesten Techniken für Benutzerschnittstellen. Hier erfolgt die Darbietung der Informationen durch das Programm ausschließlich in Form von Texten. Die Benutzerinteraktionen werden mittels Buchstabeneingabe auf einer Tastatur gesteuert. Diese Schnittstellen stellen 9 Streng betrachtet ist bei der Interaktion mit der Maus auch mindestens eine Indirektion vorhanden, da gedanklich die Mausbewegungen in die Ebene des Bildschirms projiziert werden müssen. 35 3 Das Werkzeug zum Wissenserwerb geringe Anforderungen an die Hardware. Sie eignen sich gut für die Kommandotechnik. Auch die Menütechnik und Formulartechnik lassen sich einsetzen, wenn mehrzeiliger Text möglich ist. Dagegen läßt sich Direktmanipulation nur schwer umsetzen, da die Darstellung der zu manipulierenden Objekte schwierig ist. Textbasierte Schnittstellen werden von vielen Benutzern als „veraltet“ empfunden. Grafische Benutzerschnittstellen Unter diesem Begriff werden alle Benutzerschnittstellen zusammengefaßt, die außer Text noch weitere (grafische) Elemente zur Darstellung nutzen. Neben der Tastatur kommt als Eingabegerät vor allem die Maus zum Einsatz, durch die sich ein grafisches Zeiger-Objekt (der Mauszeiger) mittels Direktmanipulation auf dem Bildschirm bewegen läßt. Aktionen werden in der Regel durch Betätigen von Schaltern auf der Maus oder durch die Tastatur ausgelöst, wobei die Aktion oft von der Position des Mauszeigers mitbestimmt wird (d. h. sie sind kontextsensitiv). Zur Umsetzung solcher Benutzerschnittstellen existiert eine Vielzahl von Programmbibliotheken, die verschiedene vordefinierte grafische Elemente zeichnen, die Mausbewegungen durch Bewegungen des Mauszeigers anzeigen sowie Möglichkeiten der Verarbeitung von Eingaben durch Tastatur und Maus bieten. Solche Programmbibliotheken sind bereits Teil einiger moderner Betriebssysteme, bei anderen sind sie zusätzlich zu installieren. WWW-basierte Schnittstellen So werden Benutzerschnittstellen bezeichnet, die die Hypertext Markup Language (HTML) und das Hypertext Transfer Protocol (HTTP) benutzen. HTTP ist dabei das Netzwerkprotokoll, mit dem HTMLDokumente von einem Klienten (HTTP-Client) bei einem Anbieter (HTTP-Server) angefordert und dann übermittelt werden. HTML erlaubt die Definition von Struktur und Inhalt von Textdokumenten, die Definition von Verweisen auf andere Dokumente sowie das Einbetten von Multimedia-Inhalten in die Dokumente. Die Darstellung der so definierten Inhalte übernehmen spezielle Programme – sogenannte Browser. Neben der reinen Strukturbeschreibung bietet HTML auch Elemente, mit denen sich die Darstellung bestimmen läßt. Dabei ist die Interpretation dieser Elemente dem Browser überlassen. Die Möglichkeiten der Benutzerinteraktion, die mit HTML beschreibbar sind, sind das Verfolgen von Verweisen auf andere Dokumente und das Ausfüllen und Absenden von Formularen. Wie diese Interaktionen ausgeführt werden, ist Browser-abhängig. Dabei können alle vier Techniken zum Einsatz kommen. Neben der Anzeige statischer Dokumente kann HTML auch dynamisch erzeugt werden. Dabei werden die in Formularen eingetragenen Daten per HTTP übermittelt und bei der Erzeugung von HTML-Dokumenten benutzt. Dadurch kann eine Benutzerschnittstelle in Formulartechnik mittels HTML erzeugt werden. Das in Abschnitt 2.6 beschriebene System benutzt diesen Weg. Der Vorteil von HTMLbasierten Benutzerschnittstellen ist die Möglichkeit, aus einer Vielzahl von Be- 36 3.3 Aufbau des Werkzeuges triebssystemen über das Internet die Anwendung benutzen zu können. Nachteil ist, daß Direktmanipulation und Kommandotechnik nur begrenzt realisierbar sind. Die Darstellung der HTML-Dokumente und damit der Benutzerschnittstelle variiert je nach Browser und Betriebssystem sehr stark. Außerdem existiert neben der Benutzerschnittstelle der Anwendung immer auch die Benutzerschnittstelle des Browsers, die in der Regel in ihrer Gestaltung zu ersterer nicht konsistent ist. Es gibt Methoden, die Beschränkungen bezüglich der möglichen Benutzerinteraktionen zu durchbrechen. Das wird durch in das HTML-Dokument eingebettete Programme erreicht, die auf Bibliotheken für grafische Benutzerschnittstellen zugreifen. Es gilt hier, was bereits zu grafischen Benutzerschnittstellen gesagt wurde. Der Vorteil neben der Freiheit bei der Ausgestaltung der Benutzerschnittstelle ist auch hier die Möglichkeit, das Programm aus der Ferne zu nutzen. Entscheidende Nachteile sind die langen Wartezeiten bei schlechter Internetanbindung sowie der Verlust von Betriebssystem- und Browserunabhängigkeit10 . Beide Möglichkeiten für WWW-basierte Schnittstellen sind mit den genannten Einschränkungen für den Wissenserwerb nur bedingt geeignet. 3.3 Aufbau des Werkzeuges Sowohl bei den Betrachtungen zum Format des Komponentenkatalogs (3.1.5) als auch zu den Benutzerschnittstellen (3.2.1) wurden mehrere mögliche Varianten erkannt. Davon wird jeweils eine in einem Prototyp des Wissenserwerb-Werkzeuges umgesetzt. Um eine spätere Einbindung anderer Varianten zu ermöglichen, ist die Entkoppelung von Katalogzugriff und Benutzerschnittstelle sinnvoll. Für jeden Katalog müssen sowohl die Lade- als auch die Speicheroperation implementiert werden. Deshalb wurde eine von der Katalogform unabhängige Schnittstelle für diese Operationen definiert (Abbildung 3.1). Dabei wird eine von der Katalogform unabhängige Beschreibung der Komponenten und Ressourcen durch die Klassen „Component“ und „Resource“ benutzt. «interface» Component +name: string +abstract: boolean +provided: [Resource,string] +consumed: [Resource,string] +pythonCode: string +bases: [Component] Resource ModelStore +laden(): [Resource],[Component] +speichern(ResList:[Resource],CompList:[Component]) PythonStore Benutzerschnittstelle +name: string +bases: [Resource] +pythonCode: string Abbildung 3.1: Die Katalog-Schnittstelle 10 Viele dieser Programmpakete sind nicht für alle Browser vorhanden oder unterschiedlich umgesetzt. Andere müssen erst vom Benutzer heruntergeladen und installiert werden. 37 3 Das Werkzeug zum Wissenserwerb Zwischen dem Laden und Speichern werden die Veränderungen des Katalogs auf einer Arbeitskopie ausgeführt.11 Diese Operationen werden in dieser Abbildung der Benutzerschnittstelle zugeordnet. Bei näherer Betrachtung der Implementierung (3.5) läßt sich feststellen, daß innerhalb dieser „Benutzerschnittstelle“ nochmal eine Trennung zwischen den Operationen auf den Daten und deren Darstellung besteht. Die Schnittstelle zwischen Katalog-Komponente und Benutzerschnittstelle wurde im Prototypen als Python-Klasse (ModelStore) realisiert, da beide Teile auch in Python geschrieben wurden. Die Klasse PythonStore, in der der eigentliche Zugriff auf den bestehenden Katalog umgesetzt wird, ist eine Spezialisierung von ModelStore. «interface» ModelStore +laden(): [Resource],[Component] +speichern(ResList:[Resource],CompList:[Component]) CORBAStore PythonStore Benutzerschnittstelle Katalog CORBA-Katalog Abbildung 3.2: Integration eines CORBA-basierten Katalogs Soll der Katalogzugriff in einer anderen Programmiersprache umgesetzt werden oder ist die Verteilung von Katalog und Benutzerschnittstelle auf verschiedene Rechner gewünscht, so ist die Verwendung einer CORBA-Schnittstelle zwischen Benutzerschnittstelle und Katalog sinnvoll. Dazu würde eine Python-Klasse, die Spezialisierung von ModelStore ist, die CORBA-Schnittstelle kapseln. In Abbildung 3.2 ist diese Klasse mit CORBAStore bezeichnet. 3.4 Implementierung des Katalogzugriffs Als Programmiersprache für den Katalogzugriff wird Python [vR00] benutzt. Python ist eine objektorientierte Skriptsprache, mit der eine Reihe von Bibliotheken (Module) für verschiedene Aufgaben mitgeliefert werden. Die schnelle Implementierung von Prototypen war eines der Hauptziele beim Entwurf der Sprache. Diese Sprache hat den Vorteil, das bereits ein Parser-Modul existiert, das Syntax-Bäume aus Python-Quelltext generiert. In den folgenden beiden Abschnitten werden die Details der Informationsgewinnung aus den Pythondateien des Katalogs und die Generierung von Python-Quelltext näher beschrieben. 11 Das entspricht auch der Arbeitsweise anderer gängiger Programme, z. B. von Office-Paketen. 38 3.4 Implementierung des Katalogzugriffs 3.4.1 Informationsgewinnung aus den Pythondateien Die Verarbeitung des Komponentenkatalogs erfolgt durch die Python-Klasse PythonStore, die die ModelStore-Schnittstelle implementiert (Abschnitt 3.3). Um aus den Pythondateien die Informationen über den Komponentenkatalog zu gewinnen, wird das Python-Modul parser benutzt. Dieses gehört zu jeder Standardinstallation von Python und bietet eine Schnittstelle zum Python-internen Parser, der in C implementiert ist.12 Die beim Parsen erzeugten Objekte vom Typ ASTType lassen sich in eine Listenoder Tupelrepräsentation umwandeln. Die Listenrepräsentation eines SyntaxBaumes (ebenso die eines Teilbaumes) beginnt mit einer numerischen Konstante, die den Typ des Wurzelknotens beschreibt, sowie den Listenrepräsentationen der Teilbäume. Auf diese Weise werden die Syntax-Bäume als vielfach verschachtelte Listen widergegeben. Die Blätter des Baums bestehen aus Konstanten für den Typ der Terminale sowie einer Zeichenkette13 . Die Lexik und Syntax von Python ist im „Python Reference Manual“ [vR00] beschrieben. Die folgende Liste enthält als Beispiel den Syntaxbaum der Anweisung print ’a’.14 [file input, [stmt, [simple stmt, [small stmt, [print stmt, [NAME, ’print’], [test, [and test, [not test, [comparison, [expr, [xor expr, [and expr, [shift expr, [arith expr, [term, [factor, [power, [atom, [STRING, "’a’"]]]]]]]]]]]]]]]], [NEWLINE, ’’]]], [ENDMARKER, ’’]] Die Klasse PythonStore verarbeitet diese Listenrepräsentation der geparsten Pythondateien. Daraus werden in der Funktion reduce() die Informationen über den Katalog gesammelt. Das soll am Beispiel von findCompDef() erläutert werden. Die Funktion findCompDef() sucht in einem Syntaxbaum nach Teilbäumen mit Klassendefinitionen. Jeder dieser Teilbäume beginnt mit: [stmt, [compound stmt, [classdef,. . . Wird in findCompDef() festgestellt, daß die übergebene Liste diesem Muster genügt, so wird der Teilbaum, der mit [classdef, beginnt, an die Funktion addComponent() übergeben.15 Diese ermittelt daraus die Eigenschaften einer Komponente. Anstelle dieses Teilbaums wird ein Platzhalter eingefügt, der später 12 Einige Informationen zu der Verwendung des Moduls findet sich in der „Python Library Reference“ [vRDJ00]. 13 z. B. dem Namen eines Bezeichners 14 Die numerischen Werte der Konstanten wurden durch ihre symbolischen Namen aus den PythonModulen „symbol“ und „token“ ersetzt. 15 Es wäre ausreichend, im Syntaxbaum nach [classdef. . . zu suchen, was aber eine höhere Zahl von Rekursionen erfordern würde. 39 3 Das Werkzeug zum Wissenserwerb beim Wiederherstellen der Pythondateien benutzt wird. def findCompDef(self, List): if type(List) == ListType: if len(List) > 1 \ and List[0] == symbol.stmt \ and List[1][0]== symbol.compound stmt \ and List[1][1][0] == symbol.classdef: self. addComponent(List[1][1]) return 1,List else: for i in range (1, len(List)): (a, s) = self. findCompDef(List[i]) if a: List[i] = comp def else: List[i] =s return 0,List return 0,List Beginnt die momentan bearbeitete Liste nicht mit [stmt, [compound stmt, [classdef, so wird findCompDef() rekursiv auf die Elemente der Liste angewendet. Auf diese Weise wird der gesamte Syntaxbaum abgeschritten. Dieser Aufwand ließe sich reduzieren, wenn Einschränkungen bezüglich der Struktur der Datei „components.py“ gemacht würden. Die Funktion addComponent ermittelt die Basisklassen der Komponente. Hier wird von der Annahme ausgegangen, daß dort nur ein Tupel von Bezeichnern steht. Python erlaubt aber an dieser Stelle beliebige Ausdrücke, die als Ergebnis Klassen-Objekte liefern [vR00]. Soll dies unterstützt werden, so ist die Funktion findbasesComp() entsprechend anzupassen. def addComponent(self, Tuple): . . . if Tuple[4][0] == symbol.testlist: bases = self. findbasesComp(Tuple[4]) i = 7 else: i = 4 if Tuple[i][0]== symbol.suite : abstract,provided,consumed,Tuple[i]=self. findconsumed2(Tuple[i]) Die weiteren Eigenschaften der Komponente werden in der Funktion findconsumed2() ermittelt. Dies geschieht wieder durch rekursives Abgehen des Syntaxbaumes und Suchen nach der für Zuweisungen typischen Struktur [stmt, [simple stmt, [small stmt, [expr stmt . . . Die gefundenen Zuweisungen werden dann dahingehend untersucht, ob es sich um Zuweisungen an die bekannten Eigenschaften von Komponenten handelt 40 3.4 Implementierung des Katalogzugriffs (consumes, provides, abstract). Je nach Eigenschaft wird dann der Syntaxbaum, der die rechte Seite der Zuweisung beschreibt, ausgewertet. Dabei werden folgende vereinfachende Annahmen über die Klassendefinitionen in der Komponentendatei getroffen16 : 1. Auf der linken Seite der Zuweisungen stehen nur die Bezeichner. 2. Auf der rechten Seite von provides= und consumes= steht eine Liste der Art [<Ressourcenname>,("<Wert>")]. 3. Auf der rechten Seite von abstract= steht der Wert 1, falls die Komponente abstrakt ist oder ein anderer Wert falls nicht. Die Teile des Syntaxbaumes, die diesen Zuweisungen entsprechen, werden entfernt. Was nach der Abarbeitung von findconsumed2() in dem Syntaxbaum noch vorhanden ist, enthält allen zusätzlichen Quelltext innerhalb der Klassendefinition. Er wird durch den Unparser, der in 3.4.2 beschrieben ist, wieder in seine textuelle Repräsentation umgewandelt. Abschließend wird eine Instanz der Klasse Datatypes.Component erzeugt, in der die gewonnenen Daten gespeichert werden. t = Tuple[i][3:−1] Code ="" for t2 in t: u = unparser.Unparser() s = u.astlist2str(t2) Code = Code + s comp = Component(name, bases, provided, consumed, abstract,Code) self.Components[name]=comp Die Ermittlung der Ressourcen und ihrer Eigenschaften aus dem Syntaxbaum der Datei „resources.py“ geschieht auf vergleichbare Weise durch die Funktionen findResDef(), addResource() und findbasesRes(). Hierbei wird der gesamte Quelltextblock innerhalb der Klassendefinition wieder in seine Textrepräsentation umgewandelt.17 3.4.2 Erzeugung der Pythondateien des Katalogs Die Erzeugung der Pythondateien geschieht in zwei Schritten. Zuerst werden zwei Syntaxbäume generiert, die diesen Dateien entsprechen. Danach werden diese Bäume durch einen Unparser in Quelltext umgewandelt und in die Dateien geschrieben. 16 17 Diese Annahmen treffen für den Beispielkatalog von SCO zu. Denkbar ist, den Quelltext in die Definitionen der Funktionen init (), must balance at(), provider for(), consume(), unconsume() und exhausted(), die in Abschnitt 2.4 beschrieben sind, zu zerlegen. 41 3 Das Werkzeug zum Wissenserwerb Erzeugung der Syntaxbäume Die Erzeugung der Syntaxbäume erfolgt in der Funktion expand() der Klasse PythonStore. Dabei werden die durch reduce() um die Klassendefinitionen reduzierten Syntaxbäume wieder erweitert. Als erstes wird nach der Stelle in den Syntaxbäumen gesucht, an welcher der von reduce() eingetragene Platzhalter erstmalig auftritt. Dieser erste Platzhalter wird durch einen Teilbaum ersetzt, der durch die Funktionen getCompList() bzw. getResList() erzeugt wird. Alle weiteren Platzhalter werden entfernt. Die eingehängten Teilbäume entsprechen der Folge von Klassendefinitionen in den Pythondateien „components.py“ bzw. „resources.py“. Die Erzeugung dieser Teilbäume soll hier anhand der Funktion getResList() kurz aufgezeigt werden. Diese Funktion ist für den Teilbaum mit den Klassendefinitionen der Ressourcen zuständig. Zuerst werden die Ressourcen, die in einem assoziativen Feld gespeichert sind, so sortiert, daß die Basisklassen vor den von ihnen erbenden stehen.18 def getResList(self): . . . sorted res=self.order Resources() Für jede Ressource der sortierten Liste wird ein Teilbaum generiert, der einer Python-Klassendefinition entspricht. for res in sorted res: res tree = [symbol.stmt, [symbol.compound stmt, [symbol.classdef, [token.NAME, "class"], [token.NAME, res.name]]]] Falls die Ressource Basisklassen hat, so wird ein Teilbaum für die Notation dieser durch get bases() erzeugt und dem Ergebnisbaum hinzugefügt. bases = get bases(res) if bases : for b in bases: res tree[1][1].append(b) Falls kein Python-Quelltext für die Ressource definiert ist, wird ein Syntaxbaum eingefügt, der der Python-Anweisung pass, die leere Quelltextblöcke kennzeichnet, 18 Zyklen werden dabei nicht erkannt und führen zu Fehlverhalten des Programms. 42 3.4 Implementierung des Katalogzugriffs entspricht. if not (res.pythonCode): res tree[1][1].append([symbol.suite, [token.NEWLINE, ’’], [token.INDENT, ’’], [symbol.stmt, [symbol.simple stmt, [symbol.small stmt, [symbol.pass stmt, [token.NAME, ’pass’]]], [token.NEWLINE, ’’]]], [token.DEDENT, ’’]]) result.append(res tree) Wenn dagegen Quelltext existiert, so wird dieser geparst (mit Hilfe des parserModuls) und das Ergebnis benutzt. Dieses wird mit [suite, [NEWLINE,’’], [INDENT,’’]. . . [DEDENT,’’]] umgeben, da der Quelltext innerhalb einer Klassendefinition stehen soll. else: tree2 = [symbol.suite, [token.NEWLINE, ’’], [token.INDENT, ’’]] tree2.append( get Code(res.pythonCode)) tree2.append([token.DEDENT, ’’]) res tree[1][1].append(tree2) result.append(res tree) Die Erzeugung des Syntaxbaumes für die Komponentendefinitionen verläuft analog. Der Unparser Nach dem die Syntaxbäume generiert wurden, werden diese in Quelltext umgewandelt. Dazu wurde ein allgemeiner Unparser für Python-Syntaxbäume19 entwickelt. Dessen Funktionsweise soll im folgenden umrissen werden. Bei der Quelltextgenerierung wird die Tatsache genutzt, daß zu jedem Terminal im Baum auch eine Zeichenkettenrepräsentation abgelegt ist. Eine Aneinanderreihung dieser Zeichenketten ergibt einen dem Original sehr ähnlichen Text, wenn für das Terminal NEWLINE ein Zeilenumbruch erzeugt wird. Problematisch ist die Einrückung der Zeilenanfänge. Diese sind in Python signifikant, da Anweisungen durch die Tiefe ihrer Einrückung gruppiert werden: alle Anweisungen eines Blockes haben die gleiche Einrückungstiefe. Daher ist es beim Unparsen wichtig, die Hierarchie der Einrückungen wiederherzustellen. 19 Syntaxbäume, die den vom Parser-Modul gelieferten entsprechen 43 3 Das Werkzeug zum Wissenserwerb Der Beginn eines neuen Blocks wird im Syntaxbaum durch das Terminal INDENT angezeigt, das Ende durch DEDENT. Die naheliegende Lösung, beim Auftreten eines INDENT einen Zähler zu erhöhen, bei DEDENT diesen wieder zu verringern und nach jedem Zeilenumbruch dem Zähler entsprechend einzurücken20 , ist nicht ausreichend. Grund dafür ist, daß am Ende eines Blockes erst das NEWLINE auftritt und dann das DEDENT, wodurch die nächste Anweisung, die schon nicht mehr zum Block gehört, wie der Block – also falsch – eingerückt wird. Im Unparser wird dieses Problem gelöst, indem neben den Blättern (den Terminalen) auch die Struktur des Baumes berücksichtigt wird. Dabei wird der gesamte Baum abgeschritten. def unparse(self, List): if isinstance(List, ListType): if sym name.has key(List[0]): if List[0] == stmt: s= "\n" + " " *self.indent + self. unparse(List[1]) return s Vor jeder Python-Anweisung wird ein Zeilenumbruch und die Einrückung ausgegeben. Wenn direkt zuvor ein Block endete, ist die Einrückung hier korrekt, da das DEDENT schon verarbeitet wurde. elif List[0] == suite: sl = map(self. unparse, List[1:]) return string.join(sl,’’)+"\n"+" "*self.indent else: sl = map(self. unparse, List[1:]) return string.join(sl,’’) Bei einem anderem Nichtterminal wird die Funktion unparse() rekursiv aufgerufen. NEWLINE wird ignoriert, da der Zeilenumbruch schon behandelt wird. Ist ein Terminalsymbol ein Bezeichner (NAME), so wird nach dem Namen noch ein Leerzeichen ausgegeben, um zu verhindern, daß aufeinanderfolgende Bezeichner ohne Trennzeichen aufeinanderfolgen. Von allen anderen Terminalen wird die Zeichenkette ausgegeben (z. B. Klammern, Operatoren usw. ). else: if tok name.has key(List[0]): if List[0] == NEWLINE: return ’’ elif List[0] == DEDENT: self.indent = self.indent − 1 return ’’ 20 Diese Idee entstammt einer Nachricht in der Newsgroup „comp.lang.python“. 44 3.5 Implementierung der grafischen Benutzerschnittstelle elif List[0] == INDENT: self.indent = self.indent + 1 return ’’ elif List[0] ==NAME: return List[1]+’ ’ return List[1] 3.5 3.5.1 Implementierung der grafischen Benutzerschnittstelle Die Schnittstellenbibliothek GTK+ Die Auswahl von Bibliotheken zur Erstellung von grafischen Benutzerschnittstellen ist groß. Für das Modellierungswerkzeug fiel die Entscheidung zugunsten der Bibliothek GTK+.21 Dabei waren folgende Eigenschaften ausschlaggebend: • GTK+-Programme lassen sich durch entsprechende Erweiterungen in einer Vielzahl von Programmiersprachen erstellen. So z. B. ADA95, C++, Dylan, Eiffel, Guile, Haskell, JavaScript, Objective C, Pascal, Perl und Python [GTK00].22 Besonders letzteres ist hier von Interesse, da bereits andere Faktoren für die Verwendung von Python sprachen. (siehe 3.4) • GTK+ existiert für eine Vielzahl von UNIX-Systemen sowie MS W INDOWS. Portierungen nach MacOS [Mac00] und BeOS [BeO00] sind im Entstehen. • Die Darstellung aller GTK+-Programme auf einem Rechner ist einheitlich. Durch das Verwenden sogenannter Themen („Themes“) kann es an die Benutzerwünsche angepaßt werden. • Die Lizenz von GTK+ (GNU LGPL) erlaubt die kostenlose Entwicklung und Verbreitung von GTK+-Programmen. Die Quelltexte sind offen zugänglich und erlauben so guten Einblick in die interne Arbeitsweise der Bibliothek. GTK+ beinhaltet eine Reihe von Fenster-Elementen (sogenannten Widgets), die zur Erzeugung von grafischen Benutzerschnittstellen nützlich sind. Das reicht von einfachen Widgets wie Beschriftungen, Schaltern oder Trennstrichen bis zu komplexen wie Fenstern für die Farb- oder Dateiauswahl. Wenn hier von GTK+ die Rede ist, so ist eigentlich die Kombination von drei Software-Bibliotheken gemeint: 21 Das Kürzel GTK steht für „GIMP Toolkit“, da die Bibliothek ursprünglich für das Programm GIMP (Gnu Image Manipulation Program), ein Open-Source Bildbearbeitungsprogramm, entwickelt wurde. GTK+ wird aber in zunehmendem Maße auch für andere Softwareprojekte und Programme benutzt. Als größtes dieser Projekte ist die Entwicklung der Desktop-Umgebung GNOME [GNO00] zu nennen. 22 Ein Ansatz zur Verwendung von GTK+ in Java-Programmen ist in iX 1/2001 [Lud01] beschrieben. 45 3 Das Werkzeug zum Wissenserwerb GLib (G Library) ist eine Bibliothek, die verbesserte und sicherere Alternativen zu einigen Systemrufen, einige Datentypen, wie doppelt verkettete Listen oder Zeitnehmer, sowie Funktionen zum Speichermanagement und zur Fehlerbehandlung bereitstellt. Obwohl diese Bibliothek zusammen mit GTK+ entwickelt wurde, ist sie inzwischen auch ohne GTK+ einsetzbar. GDK Die Bibliothek GDK (GIMP Drawing Kit) kapselt alle Aufrufe von Funktionen der XLib des X Window Systems aus der GTK+-Bibliothek.23 GTK Die eigentliche GTK+-Bibliothek: In ihr sind alle Widgets sowie Funktionen zur Behandlung von Ereignissen wie z. B. Benutzereingaben enthalten. Auf sie wird im Anschluß noch näher eingegangen. Bei der Entwicklung von GTK+ wurde ein objektorientierter Ansatz verfolgt. Da für die Implementierung die Programmiersprache C24 verwendet wurde, die ursprünglich keine Mittel für objektorientiertes Programmieren bereitstellt, werden einige Hilfskonstrukte benutzt. So wird für jedes Widget sowohl eine Datenstruktur für die jeweilige Klasse als auch für die Instanzen definiert. Die Benennung der Klassenstruktur folgt dem Muster: Gtk<WidgetName>Class, die der Instanzen Gtk<WidgetName>. Erster Bestandteil der Datenstruktur ist immer die Struktur der Basisklasse (die Klasse, deren Spezialisierung diese Klasse ist), bzw. der dazugehörigen Instanz. Dadurch wird eine Vererbung der Datenstruktur von der Basisklasse auf die abgeleitete Klasse erreicht. Auch die Eigenschaft der Polymorphie wird damit gewährleistet, d.h. ein Zeiger auf eine Instanz einer abgeleiteten Klasse kann anstelle eines Zeigers auf die Instanz einer Basisklasse verwendet werden.25 Danach folgen die Attribute der Klasse bzw. der Instanzen. Die Zuordnung von Funktionen zu Widgetklassen erfolgt nur über den Namen. Dabei wird folgendes Schema benutzt: gtk <widgetname> <funktionsname(). Handelt es sich um eine Funktion, die auf einer Instanz der Klasse arbeitet, ist der erste Parameter immer ein Zeiger auf diese Instanz.26 Auf Grund der oben dargestellten Polymorphie kann es auch ein Zeiger auf eine Instanz einer abgeleiteten Klasse sein. Dadurch wird die Vererbung von Funktionen erreicht [Pen99]. Zu jeder Klasse existiert mindestens ein Konstruktor (mit dem Namen gtk <widgetname> new()), eine Funktion, die Instanzen dieser Klasse erzeugt. Zu einigen Widgets sind auch parametrisierte Konstruktoren vorhanden. 23 Das gilt natürlich nur für Systeme, die das X Window System benutzen. Auf anderen Plattformen, wie MS-Windows oder BeOS, werden die vergleichbaren Bibliotheken gekapselt. 24 Die Entscheidung gegen C++ zugunsten von C wurde laut Havoc Pennington [Pen99] aus einer Vielzahl von Gründen getroffen, u.a. Vorliebe der Autoren, bessere Standardisierung und Verbreitung von C im UNIX-Umfeld. 25 Wie Rolf Herzog [Her98] anführt, gilt dies nur, wenn der Compiler die Elemente in der Reihenfolge im Speicher ablegt, in der sie deklariert werden. 26 Zur Laufzeit wird überprüft, ob die übergebenen Instanz einer Klasse angehört, für die die Funktion definiert ist. 46 3.5 Implementierung der grafischen Benutzerschnittstelle Ein weiteres wichtiges Konzept von GTK+ ist das der Ereignisse27 . Durch Ereignisse wird ein GTK+-Programm über Benutzerinteraktionen informiert. Nach dem Aufruf von gtk main() durchläuft das Programm eine Schleife, die erst durch gtk mainquit() wieder beendet wird. Innerhalb dieser Schleife wird jedesmal, wenn eine Benutzereingabe (per Maus oder Tastatur) erfolgt, ein Ereignis ausgelöst. Wenn vom Programmierer mittels gtk signal connect() Funktionen (sogenannte Callback-Funktionen) mit dem Ereignis verbunden wurden, so werden diese jetzt aufgerufen. Außer durch Benutzerinteraktionen können Ereignisse auch durch das Programm selber erzeugt werden und so die Kommunikation zwischen Programmteilen ermöglichen. Das geschieht durch Aufruf von gtk signal emit(). 3.5.2 GTK+ -Programme mit Glade Die Erstellung von komplexen Oberflächen ist oft sehr aufwendig, da eine Vielzahl von Widgets erstellt und initialisiert werden und in die sie umgebenden Widgets eingebettet werden muß.28 Besonders das Festlegen von Eigenschaften der Widgets, wie z.B. Größe oder Positionierung, erfordert wiederholtes Testen. Um dieses Problem zu umgehen, wurden spezielle Werkzeuge entwickelt, sogenannte GUI-Builder. Mit ihnen lassen sich grafische Benutzeroberflächen leicht erzeugen. Dabei wird das spätere Aussehen der Oberfläche mit der Maus konstruiert. In der Regel wird dann daraus Quelltext einer Ziel-Programmiersprache und ZielOberflächenbibliothek generiert, dessen Ausführung eine Oberfläche genau diesen Aussehens erzeugt. Außerdem werden oft auch Programmfragmente für das Verhalten bei Benutzerinteraktionen erzeugt. Diese müssen dann vom Programmierer ausgefüllt werden. Es existiert eine Vielzahl solcher Werkzeuge für verschiedene Oberflächenbibliotheken und Programmiersprachen. Auch für GTK+ gibt es mehrere solcher Werkzeuge [GTK00]. Von diesen wurde Glade [GLA] ausgewählt, da es den ausgereiftesten Eindruck machte und keine Abhängigkeiten von anderen Technologien vorhanden sind. Glade bietet neben der Ansicht der Ziel-Oberfläche (Abbildung 3.3) und einer Palette von Widgets (Abbildung 3.4), wie sie typisch für Werkzeuge dieser Art sind, noch eine Baumansicht der Widgets (3.5) und einen Editor für die Widget-Eigenschaften (Abbildung 3.6). Neben der Generierung von Quelltexten für verschiedene Programmiersprachen (C, C++, ADA95, Perl und Eiffel) gibt es noch eine weitere Möglichkeit, mit Glade erzeugte Oberflächen in Programme einzufügen: das Initialisieren von Oberflächen aus den sie beschreibenden Projektdateien des Glade-Werkzeuges29 durch die Bibliothek libglade [Hen00a]. Auf die einzelnen Widgets kann über Namen, 27 In Dokumentationen zu GTK+ ist in diesem Zusammenhang oft von Signalen die Rede. Um diese vom Konzept der Signale von Unix-Betriebssystemen zu unterscheiden, wird in dieser Arbeit statt dessen der Begriff Ereignisse verwendet. 28 Auch bei den Widgets eines GTK+-Programms besteht eine Enthaltensein-Hierarchie. 29 Diese sind wohlgeformte XML-Dokumente (Abschnitt 3.1.3) und deshalb u. U. auch von Hand zu erzeugen und zu bearbeiten. 47 3 Das Werkzeug zum Wissenserwerb Abbildung 3.3: Konstruiertes Fenster Abbildung 3.4: Widget-Palette von Glade Abbildung 3.5: Widget-Baum Abbildung 3.6: Widget-Eigenschaften die ihnen mit dem Glade-Werkzeug verliehen wurden, zugegriffen werden. Danach können diese ebenso benutzt und verändert werden, wie andere GTK+-Widgets auch. 3.5.3 Grafische Benutzerschnittstellen mit PyGtk PyGTK [Hen00b] ermöglicht die Verwendung von GTK+ in Python-Programmen. Dabei werden die GTK+-Konstrukte und -Konzepte in die entsprechenden Pythonkonzepte abgebildet. Jedes Widget wird durch eine eigene Python-Klasse repräsentiert. Dabei werden die Namen in der Form Gtk<WidgetName> beibehalten. Python ermöglicht als objektorientierte Sprache die direkte Zuordnung von Funktionen zu Klassen. Deshalb entfällt der Präfix der Funktionsnamen, der in GTK+ den Namen der Klasse repräsentiert. 48 3.5 Implementierung der grafischen Benutzerschnittstelle Die Klassen und ihre Funktionen sind in der Datei „gtk.py“30 definiert. In „GTK.py“ und „GDK.py“ sind diverse numerische Konstanten festgelegt, die auch mit ähnlichen Namen (mit Präfix GTK bzw. GDK) auch in GTK+ und GDK definiert sind. Auch die Verwendung von Glade-generierten Oberflächen ist mittels PyGTK möglich. Dazu wird der Weg der Generierung der Oberflächen mit libglade benutzt. Die Erzeugung von Python-Quelltext aus Gladedateien war zu dem Zeitpunkt, als es für diese Arbeit benötigt wurde, noch nicht möglich.31 Um libglade zu benutzen, muß die Datei „libglade.py“ importiert werden. In ihr werden die Klasse GladeXML sowie einige Hilfsfunktionen definiert. Mittels g=libglade.GladeXML(Gladedatei) werden alle Widgets, die in der Glade-Datei beschrieben sind, initialisiert. Widgets, die mit dem GladeWerkzeug „sichtbar“ gesetzt wurden, werden jetzt angezeigt. Beim Aufruf von g=libglade.GladeXML(Gladedatei,Widgetname) wird nur der Teil des Widget-Baumes erzeugt, dessen Wurzel das übergebene Widget ist.32 Durch den Aufruf von g.get widget(Widgetname) kann jetzt auf die verschiedenen Widgetinstanzen zugegriffen und diese benutzt werden. 3.5.4 Implementierungsdetails der Benutzerschnittstelle Die Benutzerschnittstelle des Modellierungswerkzeuges setzt sich aus mehreren Fenstern für verschiedene Aufgaben zusammen. Bis auf eine Ausnahme, auf die später noch eingegangen wird, wird für alle Fenster libglade benutzt. Dabei wird für jedes Fenster eine separate Glade-Datei verwendet. Neben den Klassen für die benötigten Fenster existiert eine weitere Klasse, die die gesamte WissenserwerbAnwendung repräsentiert. Letztere benutzt Instanzen der Fensterklassen und führt alle Operationen durch, die Veränderungen des Komponentenkatalogs verursachen. Die Eigenschaften der Klassen sind im folgenden näher beschrieben. Die Klasse CompModel Dies ist die Anwendungsklasse, sie beinhaltet jeweils eine Instanz der Fensterklassen: • ResListWindow: die Liste der Ressourcen, • CompListWindow: die Liste der Komponenten, • ResEditWindow: zum Editieren einer Ressource und 30 Die Datei „GTKinter.py“, auf die in der Literatur [vLF00] verwiesen wird, existiert nur noch aus Gründen der Kompatibilität mit älteren Versionen. Bei einem Import wird eine Warnung ausgegeben, die auf „gtk.py“ verweist. 31 Inzwischen ist mit GladePyC [Gla00] auch diese Möglichkeit vorhanden. 32 Widgetname ist der Name des Widgets, der mit Glade festgelegt wurde, nicht der Name der Widgetklasse. 49 3 Das Werkzeug zum Wissenserwerb • CompEditWindow: zum Editieren einer Komponente. Die Klasse CompModel erwartet bei der Initialisierung eine Implementierung der ModelStore-Schnittstelle (Abschnitt 3.3). Über diese wird dann auf einen Komponentenkatalog zugegriffen. Da in der Modellierungsanwendung oft Komponenten und Ressourcen über ihre Namen referenziert werden, werden zum Speichern dieser zwei assoziative Felder verwendet, die die Namen der jeweiligen Ressource/Komponente als Zugriffsschlüssel haben. Zu diesen Schlüsseln ist das zugehörige Objekt der Klasse Component bzw. Resource, die in Abschnitt 3.3 beschrieben wurden, abgespeichert. Neben den Fensterinstanzen und Datenfeldern bietet diese Klasse noch Funktionen, die Veränderungen des Modells ermöglichen. Zu diesen Veränderungen zählt das Laden bzw. Speichern des Modells sowie das Löschen, Editieren, Erzeugen und Kopieren von Ressourcen und Komponenten. Die Funktionen zum Laden und Speichern des Modells (load() und store()) rufen die entsprechenden Funktionen der ModelStore-Instanz auf. Ein Problem, das bei der Arbeit mit GTK+ oft auftritt, ist, daß nach dem Anzeigen eines Fensters der Programmfluß unterbrochen werden soll, bis das Fenster geschlossen wird.33 Wird ein Fenster innerhalb einer Funktion erzeugt, so gibt GTK+ nach dem Anzeigen des Fensters die Kontrolle an das erzeugende Programm zurück. Dieses fährt dann sofort an der Stelle im Programm fort, an der das Fenster erzeugt wurde. Soll die Ausführung unterbrochen werden, ist nach der Erzeugung des Fensters die Kontrolle explizit an GTK+ zu übergeben. Das geschieht entweder, in dem ein Ruf von gtk.mainloop() erfolgt, der durch gtk.mainquit() zu beenden ist, der die Callback-Funktion, mit deren Ausführung das Programm gerade beschäftigt ist, beendet wird. Um an der gewünschten Stelle in der Programmlogik fortzufahren, sind geeignete Callbacks für das Schließen des neuen Fensters notwendig. Sollen andere Fenster in der Zwischenzeit nicht auf Eingaben reagieren, so ist das neue Fenster als „modal“ einzustellen. (Durch Glade oder Aufruf von GTKWindow.set modal(GTK.true)) Ein Beispiel, wo dieses Verfahren zur Anwendung kommt, ist das Kopieren von Komponenten. Wenn der Benutzer das Kopieren einer Komponente in dem Fenster der Klasse CompListWindow auswählt, so wird in der zugehörigen CallbackFunktion die Funktion copy comp() der Klasse CompModel gerufen. Zuerst wird die Komponente mit dem übergebenen Namen aus dem Feld aller Komponenten gesucht, eine Kopie angelegt und diese in das Feld eingefügt: def copy comp(self,CompName): OldComp = self.CompList[CompName] NewCompName = "Kopie_von_" + CompName NewComp = Component(NewCompName,OldComp.bases, OldComp.provided, \ OldComp.consumed,OldComp.abstract, OldComp.pythonCode) self.CompList[NewCompName]=NewComp 33 GNOME erlaubt es, dies mit relativ einfachen Mitteln zu erreichen. Im Rahmen dieser Arbeit wird jedoch darauf verzichtet, um keine weitere Technologie-Abhängigkeit einzuführen [GNO00]. 50 3.5 Implementierung der grafischen Benutzerschnittstelle Jetzt werden alle Komponenten ausgewählt, die Spezialisierungen der Ursprungskomponente waren. Wenn diese Liste nicht leer ist, soll ein Fenster mit einer Checkliste angezeigt werden. In dieser kann der Benutzer auswählen, welche Komponenten nunmehr Spezialisierung der neuen Komponente sein sollen. Bei der Initialisierung des Fensters wird die Funktion für die zweite Phase des Kopierens übergeben. Näheres zu diesem Fenster findet sich im Abschnitt über die Klasse CheckListWindow. An dieser Stelle ist nur von Bedeutung, daß beim Schließen des Auswahlfensters die hier übergebene Funktion aufgerufen wird. List=[ ] for c in self.CompList.values(): if CompName in c.bases: List.append(c.name) if List: Text = "Welche der Komponenten soll Spezialisierung der " + \ "neuen Komponente sein statt wie bisher von " + CompName CheckWin=CheckListWindow(Text,"Kopie einer Komponente", List,self.copy comp stage2,NewCompName,CompName) else: self.copy comp stage2([ ],(NewCompName,CompName)) Falls die Liste nicht leer ist, übernimmt GTK+ nach Ende der Funktion copy comp() die Kontrolle und zeigt jetzt das Auswahlfenster an.34 Ist die Liste leer, wird gleich mit der zweiten Phase fortgefahren. In der zweiten Phase des Kopierens (copy comp stage2) werden für die Komponenten, die im Fenster ausgewählt wurden, die zu kopierende Komponente als Basisklasse aus- und die Kopie eingetragen. Danach wird das Fenster zum Editieren der kopierten Komponente angezeigt. def copy comp stage2(self, List, args): NewCompName= args[0] CompName = args[1] for n in List: c = self.CompList[n] c.bases.remove(CompName) c.bases.append(NewCompName) self.edit comp(NewCompName) Das Kopieren von Ressourcen funktioniert auf ähnliche Weise, erfordert jedoch zusätzliche Phasen, da noch zwei weitere Fenster angezeigt werden. In diesen kann jeweils aus der Liste der Komponenten, die die kopierte Ressource verbrauchen bzw. bereitstellen, ausgewählt werden. Auch das Löschen von Ressourcen und Komponenten erfolgt in zwei Phasen, wobei in der ersten ein Bestätigungsfenster erzeugt wird und in der zweiten die eigentliche Löschung erfolgt. 34 Das gilt nur unter der Voraussetzung, daß mit dem Ende dieser Funktion auch die übergeordnete Callback-Funktion beendet wird. Ansonsten wird erst mit dieser fortgefahren. 51 3 Das Werkzeug zum Wissenserwerb Mit edit res() und edit comp() wird das Fenster zum Ändern der Ressource bzw. Komponente geöffnet und die übergebene Ressource/Komponente hineingeladen. Wurden die Eigenschaften einer Komponente in diesem Fenster geändert, so wird von dort edit comp ok() aufgerufen. Dadurch werden die Änderungen dann in die lokale Kopie des Katalogs übernommen. Falls der Name geändert wurde, werden alle Referenzen auf den alten Namen in anderen Komponenten in den neuen geändert. Bei einer Namensänderung einer Ressource muß zusätzlich für alle Komponenten, die diese Ressource bereitstellen oder verbrauchen, die Referenz geändert werden. Die Klasse CheckListWindow An mehreren Stellen des Werkzeuges werden Fenster benötigt, in denen eine Entscheidungsfrage für mehrere gleichartige Objekte gestellt wird. Solche Fenster werden durch die Klasse CheckListWindow realisiert. Ein Fenster dieser Klasse besteht aus dem Text der Frage und einer Folge von Schaltern mit Beschriftungen. Mit diesen Schaltern läßt sich vom Benutzer dann einstellen, ob die Frage für die Beschriftung gilt.35 Abbildung 3.7 zeigt ein Beispiel für dieses Fenster. Abbildung 3.7: Beispiel von CheckListWindow Zur Initialisierung der Fenster (durch Aufruf von init ()) werden der Text der Abfrage, der Titel des Fensters, eine Liste der Beschriftungen für die Schalter, eine Funktion, die nach Schließen des Fensters abgearbeitet werden soll, sowie zusätzliche Argumente für diese Funktion übergeben. def init (self,Text,Title,CheckLabelList,Func,*Args): self.Win=gtk.GtkWindow(gtk.WINDOW DIALOG,Title) self.Win.set wmclass("","CompModel") self.Win.set modal(gtk.TRUE) self.VBox=gtk.GtkVBox(3) self.VBox.set homogeneous(gtk.FALSE) self.Win.add(self.VBox) 35 Dies ist eine Umsetzung der Formulartechnik analog zum Ankreuzen auf Papierformularen. 52 3.5 Implementierung der grafischen Benutzerschnittstelle self.Label=gtk.GtkLabel(Text) self.Label.set line wrap(gtk.TRUE) self.ButtonBox=gtk.GtkVButtonBox() for label in CheckLabelList: button = gtk.GtkCheckButton(label) self.ButtonBox.pack start(button) self.OkButton=gtk.GtkButton("Ok") self.VBox.pack start(self.Label) self.VBox.pack start(self.ButtonBox) self.VBox.pack start(self.OkButton) self.OkButton.connect("clicked",self.ok) Dieses Fenster wird, im Gegensatz zu den anderen der Anwendung, nicht aus einer Glade-Datei erzeugt, sondern aus den einzelnen Widgets konstruiert. Der Grund dafür ist, daß sich das Aussehen dieses Fensters zum Teil nicht vorher festlegen läßt36 . Obwohl es sich um ein relativ einfaches Fenster handelt, ist der Aufwand dafür schon hoch. Noch größer wäre er, wenn versucht würde, alle Eigenschaften, die mit Glade normalerweise gesetzt werden, anzupassen. Hier wird der Vorteil der Erzeugung von Oberflächen durch Werkzeuge sichtbar. Um zu verhindern, daß andere Benutzerinteraktionen in der Zwischenzeit erfolgen, wird das Fenster als „modal“ eingestellt, d. h. alle anderen Fenster der Anwendung reagieren nicht auf Benutzereingaben solange dieses Fenster geöffnet ist. Nach dem Schließen des Fensters wird die Funktion aufgerufen, die bei der Initialisierung übergeben wurde. Sie erhält als erstes Argument eine Liste der Beschriftungen von denjenigen Schaltern, die eingeschaltet wurden. Weitere Argumente werden übergeben, sofern diese bei der Initialisierung des Fensters verwendet wurden. Die Klasse ResListWindow Die Fenster dieser Klasse zeigen die Liste aller bekannten Ressourcen an.37 Außerdem sind Knöpfe zum Hinzufügen, Editieren, Kopieren und Löschen von Ressourcen vorhanden. Eine Abbildung dieses Fensters befindet sich im Anhang A.3 auf Seite 64. Für die Initialisierung wird die Instanz von CompModel übergeben, die dieses Fenster erzeugt hat. Dies ist notwendig, da die Datenhaltung von der Klasse 36 Mit einem Kunstgriff ließe sich auch dieses Fenster mit Glade erzeugen: An der Stelle, an der die Liste von Schaltern im Fenster stehen soll, wird eine Liste mit einem unsichtbaren Schalter eingefügt. Später werden dann die eigentlichen Schalter hinzugefügt. 37 Die Ressourcen, die in CompModel.toHide eingetragen sind, werden nicht mit angezeigt. Hintergrund ist, daß dies die Basistypen für die gezählten Ressourcen, Strukturressourcen usw. sind. Sollen diese auch von Benutzern verändert werden können, so ist CompModel.toHide entsprechend zu ändern. 53 3 Das Werkzeug zum Wissenserwerb CompModel übernommen wird und alle Aktionen, die sich auf das Modell auswirken, dort erfolgen. Die Funktion row select() dient als Callback für das Selektieren eines Eintrags in der Liste. Bei einem Doppelklick wird die Ressource editiert, ansonsten werden die Knöpfe zum Kopieren, Editieren und Löschen sensitiv d. h. aktivierbar. def row select(self,*args): if args[3]: if args[3].type == gtk.GDK. 2BUTTON PRESS: text= self.ResList.get text(args[1],0) self.mainClass.edit res(text) else: self.CopyButton.set sensitive(gtk.TRUE) self.DelButton.set sensitive(gtk.TRUE) self.EditButton.set sensitive(gtk.TRUE) Durch row unselect() werden dagegen diese Knöpfe wieder deaktiviert. Erfolgt eine Veränderung der Ressourcen in CompModel, so wird dort die Funktion refresh list() dieses Fensters aufgerufen und dadurch die Änderungen im Fenster sichtbar. Fenster der Klasse CompListWindow ähneln in Funktion und Aussehen stark den Fenstern dieser Klasse. Ihre Benutzung ist im Anhang A.1 beschrieben. Die Klasse ResEditWindow Mit dieser Klasse werden Fenster zum Editieren von Ressourcen erzeugt. In diesen Fenstern besteht die Möglichkeit, den Namen der Ressource zu ändern, Ressourcen zur Liste der Basis-Ressourcen hinzuzufügen oder aus dieser zu entfernen sowie den Python-Quelltext zu ändern. Außerdem werden ein Knopf zur Bestätigung sowie einer zum Zurücksetzen auf die Ausgangswerte angezeigt (Abbildung A.9 auf Seite 65). Die Gestaltung dieses Fensters ist eine Kombination aus Menüund Formulartechnik. Die Gliederung gleicht einem Papierfomular, der Name und der Quelltext sind frei änderbar. Das Eintragen der Basisklassen erfolgt dagegen in Menütechnik, da die Möglichkeiten hier durch die Menge der bekannten Ressourcen beschränkt sind. Neu gegenüber der bisherigen Vorgehensweise bei der Erzeugung von Fenstern sind folgende Zeilen: def . . . init (self,mainClass): self.ResBases.connect ("select_row", self.row select,self.DelBasesButton) self.ResBases2.connect("select_row", self.row select,self.AddBasesButton) 54 3.5 Implementierung der grafischen Benutzerschnittstelle self.ResBases.connect ("unselect_row", self.row unselect,self.DelBasesButton) self.ResBases2.connect("unselect_row", self.row unselect,self.AddBasesButton) Hier wird bei der Festlegung der Callbacks ein zusätzliches Argument benutzt, das beim Ruf der Callback-Funktion mit übergeben werden soll. Das erlaubt es, mehrere Callbacks mit gleicher Funktionalität auf unterschiedlichen Daten zusammenzufassen. Am Beispiel von row select() wird das deutlich. def row select(self,*args): if args[0].selection: args[−1].set sensitive(gtk.TRUE) Der Wert von args[0] ist die Liste38 , in der das Ereignis "select_row" auftrat. Mit args[−1] wird das letzte Argument abgefragt. Dies ist das ButtonWidget, das weiter oben beim Aufruf von connect() benutzt wurde. Dieser Knopf wird durch row select() aktiviert. Dadurch wird gewährleistet, daß bestimmte Knöpfe nur benutzbar sind, wenn ein Listeneintrag zur Bearbeitung ausgewählt wurde. Die Callback-Funktion hide() versteckt das Fenster, zerstört es aber nicht. Durch edit res() wird die übergebene Ressource in das Fenster geladen und dieses wieder angezeigt. def edit res(self,Res): . . . for r in self.mainClass.AllResList.keys(): if r in Res.bases: self.ResBases.append([r]) else: self.ResBases2.append([r]) self.ResCode.freeze() Der Aufruf von freeze() verhindert, daß der Bereich für den Python-Quellcode neugezeichnet wird, während er mit Text gefüllt wird. Nach dem Aufruf von thaw() werden alle Änderungen mit einem Mal sichtbar. self.ResCode.delete text(0,−1) self.ResCode.insert defaults(Res.pythonCode) self.ResCode.thaw() self.Win.show() Nach Betätigung des Ok-Knopfes wird die Funktion eval res() aufgerufen. An dieser Stelle können Plausibilitätüberprüfungen der Eingaben eingefügt werden. Bisher wird nur bei einer Namensänderung eine Bestätigung eingeholt. Anschließend werden in edit ok() die Eigenschaften der Ressource aus den einzelnen Teilen des Fensters gewonnen. Diese werden dann dem Aufruf von CompModel.edit res ok() übergeben. 38 das GTK+-Widget CList 55 3 Das Werkzeug zum Wissenserwerb Die Klasse CompEditWindow Das Fenster zum Ändern der Komponenten ähnelt in Aussehen und Verhalten sehr stark dem zum Ändern von Ressourcen. Zusätzlich vorhanden sind ein Schalter um festzulegen, ob die Komponente abstrakt ist (siehe 2.2), sowie Listen und Knöpfe zum Hinzufügen und Entfernen von verbrauchten bzw. bereitgestellten Ressourcen. Wie schon im Fenster zum Ändern von Ressourcen haben alle ListenWidgets eine gemeinsame Callback-Funktion für das Auswählen von Zeilen: row select(). Für die Listen mit Ressourcen, die in der aktuellen Einstellung verbraucht/bereitgestellt werden, existiert eine zweite Callback-Funktion, die bei einem Doppelklick ein neues Fenster vom Typ ModDialogWindow öffnet. In diesem kann dann der Parameter für die Ressource eingetragen werden.39 Die Callbacks für die Knöpfe zum Hinzufügen/Entfernen haben alle ein ähnliches Verhalten. Der Unterschied beim Hinzufügen von Ressourcen gegenüber dem von Basiskomponenten ist, daß dieselbe Ressource mehrmals hinzugefügt werden kann. Hier wäre sicherlich ebenfalls ein Zusammenfassen der Funktionen möglich. Die Funktionen edit comp(), eval comp() und edit ok() arbeiten auf vergleichbare Weise wie ihre Entsprechungen in der Klasse ResEditWindow. 39 Wünschenswert wäre es, diese Parameter in der Liste direkt einzutragen. Das ließe sich das einfach realisieren, wenn GTK+ es gestatten würde, in Zellen innerhalb einer Liste (GtkCList) beliebige andere Widgets (z. B. GtkEntry zum Ändern von einzeiligem Text) einzubetten. Dies ist für spätere Versionen von GTK+ auch geplant. 56 4 Ausblick In der vorliegenden Arbeit wird die nachträgliche Erweiterung eines Konfigurationssystems um ein Werkzeug zum Wissenserwerb beschrieben. Dabei lag der Schwerpunkt darauf, die grundsätzliche Machbarkeit zu zeigen und Lösungsansätze zu zeigen. Bei dem entwickelten Werkzeug handelt es sich darum nur um einen (funktionsfähigen) Prototypen. Um vom Prototypen zu einem fertigen Produkt zu kommen, sind weitere Untersuchungen zur Gestaltung der Benutzerschnittstelle durchzuführen. Dabei sind z. B. die Punkte Aufgabenangemessenheit, Selbstbeschreibungsfähigkeit, Erwartungskonformität, Steuerbarkeit und Fehlerrobustheit zu untersuchen. Besonders die Fehlerrobustheit wurde im Prototypen wenig berücksichtigt. Es erfolgen nur wenige Plausibilitätsüberprüfungen der Benutzereingaben. Die Implementierung des Prototypen erlaubt aber das einfache Einfügen solcher Überprüfungen. Durch die Verwendung von Glade bei der Konstruktion der Benutzerschnittstelle ist zumindest eine Umgruppierung der Fenster-Bestandteile einfach. Das Werkzeug erlaubt momentan nicht, den Katalog gleichzeitig durch verschiedene Nutzer zu bearbeiten. Soll dies ermöglicht werden, so ist die Veränderung der Architektur oder die Verwendung eines Relationalen Datenbank Management Systems zu erwägen. 57 Anhang 58 A Benutzerhandbuch des Wissenserwerb-Werkzeuges Sehr geehrter Nutzer, bei dem folgenden, kurzen Benutzerhandbuch wird davon ausgegangen, daß Sie bereits mit den theoretischen Hintergründen des Ressourcenorientierten Konfigurieren vertraut sind. Nähere Informationen finden Sie im ersten Teil dieser Arbeit (Abschnitt 1.3). In diesem Teil wird beschrieben, welche Möglichkeiten Sie haben, mit dem Werkzeug den Komponentenkatalog zu verändern. Die Benutzerschnittstelle des Modellierungswerkzeuges besteht im wesentlichen aus vier Fenstern. Ihr Aufbau und ihre Benutzung wird in den folgenden Abschnitten näher erläutert. Das Aussehen der Fenster kann auf Ihrem Rechner von den Abbildungen abweichen. A.1 Das Hauptfenster Wenn Sie das Werkzeug zur Wissenserfassung starten, so erscheint als erstes das Hauptfenster (Abb. A.1). Mit dem Schließen des Fensters wird auch die Anwendung beendet. Abbildung A.1: Das Hauptfenster des Werkzeuges mit einer selektierten Komponente 60 A.1 Das Hauptfenster Dieses Fenster besteht aus einer Menüleiste, einer Leiste mit Schaltern und der Liste aller im Katalog vorhandenen Komponenten (Abbildung A.1). Abbildung A.2: Das Katalog-Menü Abbildung A.3: Einblenden der Ressourcen Die Menüleiste beherbergt eine Menü mit Aktionen, die den gesamten Komponentenkatalog betreffen (Abb. A.2), sowie ein weiteres, um das Fenster mit den Ressourcen ein- und auszuschalten (Abb. A.3). Im Katalog-Menü können Sie den Katalog laden und speichern. In der momentanen Version werden die Dateien „resource.py“ und „component.py“, die im selben Verzeichnis liegen, in dem das Werkzeug gestartet wurde, als Katalog verwendet. Die Auswahl anderer Dateien ist momentan nicht möglich. Außerdem können Sie in diesem Menü auch die Arbeit mit dem Werkzeug beenden. Die Aktionen die im Katalog-Menü enthalten sind, können Sie auch durch Tastaturkürzel initiieren: <Strg>l1 zum Laden, <Strg>s zum Speichern und <Strg>q zum Beenden. Diese Tastaturkürzel funktionieren, solange dieses Fenster den „Tastaturfokus“2 hat. Unterhalb der Menüleiste finden Sie eine Schalterleiste mit Knöpfen zum Erzeugen einer neuen Komponente und zum Löschen, Ändern und sowie Kopieren vorhandener Komponenten. Die letzten drei Knöpfe sind nur benutzbar, wenn eine Komponente aus der Liste ausgewählt ist. Abbildung A.4: Anlegen einer neuen Komponente Der Schalter „Neue Komponente“ läßt ein Fenster erscheinen, das Sie nach dem Namen der Komponente fragt (Abb. A.4). Solange dieses Fenster sichtbar ist, reagieren andere Fenster nicht auf Eingaben. Nach Schließen dieses Fensters mit dem „Übernehmen“-Knopf erscheint die neue Komponente in der Liste der Komponenten im Hauptfenster. Sie können mit der Maus eine Komponente aus der Liste auswählen. Wenn Sie die Eigenschaften der ausgewählten Komponente verändern wollen, so können Sie 1 2 gleichzeitiges Betätigen der „Strg“- bzw. Ctrl“-Taste und des Buchstaben „l“ Das heißt, alle Tastendrücke werden an dieses Fenster gesendet. Das Umschalten des „Tastaturfokus“ ist abhängig von der Umgebung, in der Sie das Werkzeug betreiben 61 A Benutzerhandbuch des Wissenserwerb-Werkzeuges das mit dem „Komponente ändern“-Schalter oder einem Doppelklick auf die Komponente. Danach erscheint das Fenster zum Ändern von Komponenten (siehe A.2). In diesem können dann die Eigenschaften der ausgewählte Komponente verändert werden. Durch „Komponente löschen“ wird nach einer Sicherheitsabfrage die ausgewählte Komponente aus dem Katalog entfernt. Wenn Sie „Komponente kopieren“ auswählen, erscheint das Änderungsfenster mit einer Kopie der ausgewählten Komponente. Der Name wird nach dem Muster Kopie_von_<alterName>gebildet. Falls andere Komponenten Spezialisierungen der kopierten Komponente sind, wird zuvor ein Fenster angezeigt, in dem ausgewählt werden kann, welche von diesen Komponenten statt dessen Spezialisierung der Kopie sein sollen (Abb. A.5). Abbildung A.5: Kopieren einer Komponente A.2 Das Fenster zum Ändern von Komponenten In diesem Fenster (Abb. A.6) können Sie die Eigenschaften einer Komponenten verändern. Oben links befindet sich ein Eingabefeld für den Namen der Komponente. Daneben ist ein Ein-/Aus-Schalter. Wenn der Schalter eingeschaltet ist, bedeutet das, daß es sich um eine abstrakte Komponente handelt. Abstrakte Komponenten3 können in keiner Konfiguration benutzt werden sondern fassen nur die gemeinsamen Eigenschaften von einander ähnlichen Komponenten zusammen. Sie können den Zustand dieses Schalters einfach per Mausklick verändern. Den Hauptteil des Fensters nehmen drei Reihen mit jeweils zwei Listen und zwei Schaltern dazwischen ein. Dabei beinhalten die linken Listen die aktuell eingestellten Eigenschaften der Komponente (von oben nach unten: die Basiskomponenten4 , die von der Komponente verbrauchten Ressourcen und die bereitgestellten Ressourcen). In den Listen auf der rechten Seite sind die möglichen Werte für diese Eigenschaften enthalten. Mit den Schaltern zwischen den Listen können Sie Einträge aus den rechten zu den linken Listen hinzufügen und wieder entfernen. 3 4 auch Komponentenkategorien genannt Komponenten, von denen die bearbeitete eine Spezialisierung ist 62 A.2 Das Fenster zum Ändern von Komponenten Abbildung A.6: In diesem Fenster werden die Eigenschaften von Komponenten verändert Wenn Sie aus der Liste der möglichen Basiskomponenten eine zu den aktuellen Eigenschaften hinzufügen, wird diese aus der rechten Liste entfernt, da das nochmalige Hinzufügen der selben Komponente zu den Basiskomponenten nicht möglich ist. Anders hingegen bei den Listen der verbrauchten/bereitgestellten Ressourcen. Diese können durchaus mehrmals hinzugefügt werden (z. B. mit verschiedenen Parametern). Abbildung A.7: Parameter einer Ressource Wenn Sie die Parameter von bereitgestellten oder verbrauchten Ressourcen verändern wollen, so können sie das durch einen Doppelklick auf die Spalte mit der Ressource in der entsprechenden Liste auf der linken Seite erreichen. Dadurch wird ein separates Fenster geöffnet, in dem dieser Parameter eingegeben werden kann (Abb. A.7). Welche Werte dabei sinnvoll sind, hängt von der Art der Ressource ab. Momentan erfolgt keine Überprüfung der Werte. Im unteren Teil des Änderungsfensters wird Ihnen eventueller zusätzlicher Python-Quelltext zu der Komponente angezeigt. Dazu gehören z. B. die Festlegung weiterer Eigenschaften der Komponente. Diesen Quelltext sollten Sie nur ändern, 63 A Benutzerhandbuch des Wissenserwerb-Werkzeuges wenn Sie mit der Programmiersprache Python sowie der Arbeitsweise des Konfigurationssystems vertraut sind. Das Kapitel 2 gibt eine kleine Einführung in letzteres. Sind Sie mit den Veränderungen der Komponente zufrieden, so können Sie diese mit einem Knopfdruck („Ok“) in den Katalog übernehmen.5 Mit dem „Rückgängig“Knopf stellen Sie den Ausgangszustand beim Öffnen des Fensters wieder her. A.3 Die Liste der Ressourcen Abbildung A.8: Liste der Ressourcen Dieses Fenster (Abb. A.8) ähnelt in seinem Aufbau dem Hauptfenster (Abschnitt A.1). Unter einer Leiste mit vier Schaltern wird Ihnen die Liste der Ressourcen angezeigt. Auch die Aktionen, die Sie mit den Knöpfen aktivieren, gleichen denen des Hauptfensters. Statt Komponenten werden hier nur Ressourcen erzeugt, gelöscht, kopiert oder verändert. Wenn Sie eine Ressource kopieren, so sucht das Werkzeug alle Komponenten heraus, die diese Ressource verbrauchen oder bereitstellen. Es fragt Sie dann danach, welche dieser Komponenten statt dessen die Kopie verwenden sollen. Sie können das Fenster mit der Ressourcenliste jederzeit mit dem Knopf am unteren Rand schließen. 5 Bei einer Umbenennung der Komponente erfolgt noch eine Sicherheitsabfrage. 64 A.4 A.4 Ändern von Ressourcen Ändern von Ressourcen Die Eigenschaften einer Ressource können Sie in diesem Fenster bearbeiten. Ein großer Teil dieser Eigenschaften wird durch den Python-Quelltext im unteren Teil des Fenster bestimmt. Dieser sollten Sie allerdings nur verändern, wenn Sie sich über die Funktionsweise des Konfigurationssystems im klaren sind. Ansonsten können Sie nur den Namen der Ressource verändern und festlegen, von welchen Ressourcen diese eine Spezialisierung sein soll. Durch das Betätigen des „Ok“Knopfes übernehmen Sie die Veränderungen. Abbildung A.9: Fenster zum Ändern der Eigenschaften einer Ressource 65 B Ausgewählte Dateien B.1 Dateien aus dem Projekt Service Composition B.1.1 Schnittstelle des Konfigurationssystem – composer.idl #pragma prefix "hu-berlin.de" module SCO { //Component category names const string OperatingSystem = "OperatingSystem"; const string Hardware = "Hardware"; const string BasicHardware = "BasicHardware"; const string ExternHardware = "ExternHardware"; const string Service = "Service"; const string Software = "Software"; const string RuntimeEnvironment = "RuntimeEnvironment"; const string ServiceSurround = "ServiceSurround"; const string Product = "Product"; // Domain container component names const string Provider = "Provider"; const string CPE = "CPE"; // Component container const string Computer = "Computer"; }; module Composer{ interface interface interface interface typedef typedef typedef typedef typedef Component; ComponentDesc; Configuration; ServiceTemplate; sequence<Component> ComponentList; sequence<ComponentDesc> CDList; sequence<ServiceTemplate> STList; sequence<string> StringList; unsigned long ComponentID; exception UnknownComponent{}; exception Unresolvable { }; exception NotExisting{}; exception NotEmpty{}; exception Unsortable{ ComponentList best effort; }; interface Description{ string describe(in string what) raises(NotExisting); 66 B.1 Dateien aus dem Projekt Service Composition }; //A ComponentDesc describes similar components interface ComponentDesc:Description { string name(); string gui definition(); ComponentDesc super (); CDList variants(); Component create(); }; //A Component represents an instance of a component description. interface Component:Description{ ComponentDesc description (); ComponentID instance(); Component container(); ComponentList contained(); ComponentList sorted contained()raises(Unsortable); void add( in Component comp )raises(Unresolvable); // Remove component from container. Returns true on success boolean remove( in ComponentID comp ); // Return a list of all components that could fit into this component // without conflict, and can be used to satisfy required resources in // for the named service template CDList components for template (); }; // A service template serves as a starting point for a new // configuration interface ServiceTemplate:Description{ string name(); string gui definition(); }; //The repository knows all components providing a certain //resource, and can create instances of a named component. interface ComponentRepository{ Configuration new configuration(); Configuration configuration for template(in ServiceTemplate template); Configuration load configuration(in string category, in string config); ComponentDesc find component(in string name)raises(UnknownComponent); void debug(in string message); long debug level(in long newlevel); // returns old debugging level // Return a list of service template category names StringList service categories(); // Return a list of service templates per category STList service templates(in string category); // Return a list of categories StringList configuration categories (); // Return a list of configurations in a category StringList configurations (in string config category); // Delete existing configurations void delete configuration( in string category, in string toBeDeleted ) raises(NotExisting); void delete category( in string categoryName ) raises(NotEmpty, NotExisting); }; 67 B Ausgewählte Dateien //A configuration is a set of components. interface Configuration{ ComponentList selected(); Component root(); void add(in Component new)raises(Unresolvable); void remove(in ComponentID old); ComponentList unbalanced components(); // Filtering not needed anymore // CDList proposed components( in ComponentDesc filter ); // CDList needed components( in ComponentDesc filter ); void save(in string category, in string config); // Return a list of configuration names under which the components // in this configuration have been collectively tested StringList tested in (); // Return the template that the configuration is based on ServiceTemplate template(); }; // GUI callback interface Terminate{ oneway void endSession(); }; interface TINARepository:ComponentRepository, Terminate{ void register callback(in Terminate cb); }; // Factory callback interface TINACallback:Terminate{ void callback (in ComponentRepository repo, in Object session); string user(); }; }; B.1.2 Pythondateien des Komponentenkatalogs components.py Um eine übersichtlichere und kürzere Darstellung zu erreichen, wurden die Installationsanweisungen der meisten nun folgenden Komponenten entfernt. # $Id: components.py,v 1.16 1999/11/26 16:39:41 loewis Exp $ from SCOSupport import * from resources import * # Categories class Software(Component): consumes = [InComputer()] abstract = 1 class OperatingSystem (Software): abstract = 1 class Hardware(Component): abstract = 1 68 B.1 Dateien aus dem Projekt Service Composition class BasicHardware (Hardware): provides = [InComputer()] consumes = [InDomain()] abstract = 1 class Service (Component): consumes = [InComputer()] abstract = 1 class ServiceSurround(Component): consumes = [InComputer()] abstract = 1 class RuntimeEnvironment(Component): consumes = [InComputer()] abstract = 1 class Product(Software): abstract = 1 class Domain(Component): provides = [InDomain()] abstract = 1 # Domains class Provider(Domain): info = "resources/provider.txt" icon48 = "images/NeXT-WS.gif" provides = [ProviderSide()] class CPE(Domain): info = "resources/cpe.txt" icon48 = "images/Workstation.gif" provides = [CustomerSide()] # Network nodes # Even though this does not consume anything, # it is intended to go into Domains only class Computer(BasicHardware): icon48 = "images/Workstation.gif" info = "resources/computer.txt" provides = [Processor()] # Operating systems class WindowsNT (OperatingSystem): icon48 = "images/nt-floppy.gif" info = "resources/winnt.txt" name = "Windows NT" provides = [Win32()] consumes = [Processor()] class Windows98 (OperatingSystem): icon48 = "images/98-floppy.gif" info = "resources/win98.txt" name = "MS Windows 98" provides = [Win32()] consumes = [Processor()] class MacOS (OperatingSystem): 69 B Ausgewählte Dateien name = "MacOS 8.5" info = "resources/macos85.txt" icon48 = "images/mac-floppy.gif" provides = [MacABI()] consumes = [Processor()] class Solaris (OperatingSystem): name = "Sun Solaris" consumes = [Processor()] class Solaris25 (Solaris): name = "Sun Solaris 2.5.1" info = "resources/empty.txt" icon48 = "images/sun-floppy.gif" provides = [SolarisABI25()] class Solaris26 (Solaris): name = "Solaris 2.6" info = "resources/empty.txt" icon48 = "images/sun-floppy.gif" provides = [SolarisABI26()] # XXX MvL: Sollte gtar/gzip nicht Teil des Installationsprozesses # selbst sein? install = """ <component name="OS"> <location format="dir">/vobs/Tina/tina96/installer/repository/sunos-5.6</location> <packagedir>sunos</packagedir> </component> <component name="Gzip"> <location format="dir">/vobs/Tina/tina96/installer/repository/gzip-sol26</location> <packagedir>gzip</packagedir> </component> """ #Runtime Environment, CPE side class TestJar (Service): icon48 = "images/java-floppy.gif" name = "Java Test Services" info = "resources/testServices.txt" provides = [TTest1()] consumes = [TinaJava(), Java((1,1,5)), Swing((1,0,1))] install = """ """ class PAJar (ServiceSurround): name = "Java Provider Agent" info = "resources/pa.txt" icon48 = "images/pa-floppy.gif" provides = [TinaJava()] consumes = [Java((1,1,5)), CorbaJava("VisiBroker")] class ProductsJar (Product): info = "resources/javaProducts.txt" icon48 = "images/java-floppy.gif" name = "Java Products Package" provides = [CorbaJava("VisiBroker"), Swing((1,0,1))] consumes = [Java((1,1,5))] # Runtime Environment, Provider Side 70 B.1 Dateien aus dem Projekt Service Composition class DPEComponent Sol26(RuntimeEnvironment): consumes = [ProviderSide(), CorbaService("osagent"), CorbaService("Naming"),SolarisABI26(),DPEBootstrap()] class FokusPlatform(RuntimeEnvironment): name = "Platform" provides = [DPE()] consumes = [CorbaService("TheServer"), CorbaService("V-Sub-Server"), CorbaService("V-Ass-Server"),CorbaService("V-TM-Server"), CorbaService("OLS-Server"),CorbaService("PRF-Server"), CorbaService("CF-Server")] class DPECore(RuntimeEnvironment): name = "Misc. Platform Support" consumes = [ProviderSide(),SolarisABI26(),CorbaService("Versant")] provides = [DPEBootstrap()] install = """ <component name="MWP base"> <location format="dir">/vobs/Tina/tina96/installer/repository/mwp-base</location> <packagedir>mwp-base</packagedir> </component> <component name="launcher"> <location format="dir3>/vobs/Tina/tina96/installer/repository/perl-launcher</location> <packagedir>perl-launcher</packagedir> </component> <component name="config"> <location format="dir">/vobs/Tina/tina96/installer/repository/perl-config</location> <packagedir>config</packagedir> </component> """ class TheServer Sol26 (DPEComponent Sol26): name = "Access Session for Solaris 2.6" provides = [CorbaService("TheServer")] class Accounting (RuntimeEnvironment): name = "Accounting" provides = [CorbaService("Accounting")] consumes = [DPEBootstrap()] class OnlineSubscription (DPEComponent Sol26): name = "Online Subscription" provides = [CorbaService("Online Subscription")] consumes = [DPEBootstrap()] install = """ <component name="Online Subscription3> <location format="dir3>/vobs/Tina/tina96/installer/repository/ols-sol26</location> <packagedir>ols</packagedir> </component> """ class V Sub Server Sol26 (DPEComponent Sol26): name = "Subscription Server for Solaris 2.6" provides = [CorbaService("V-Sub-Server")] consumes = [CorbaService("Versant")] class V Ass Server Sol26 (DPEComponent Sol26): name = "Access Database for Solaris 2.6" provides = [CorbaService("V-Ass-Server")] consumes = [CorbaService("Versant")] 71 B Ausgewählte Dateien class V TM Server Sol26 (DPEComponent Sol26): name = "Type Manager for Solaris 2.6" provides = [CorbaService("V-TM-Server")] consumes = [CorbaService("Versant")] class OLS Server Sol26 (DPEComponent Sol26): name = "Online Subscribption for Solaris 2.6" provides = [CorbaService("OLS-Server")] class PRF Server Sol26 (DPEComponent Sol26): name = "Profiling for Solaris 2.6" provides = [CorbaService("PRF-Server")] class CF Server Sol26 (DPEComponent Sol26): name = "Capsule Finder for Solaris 2.6" provides = [CorbaService("CF-Server")] # TTest1 class TTest1 SF (Service): info = "resources/empty.txt" icon48 = "images/java-floppy.gif" name = "TTest1 Service Factory" provides = [CorbaService("TTest1SF")] consumes = [ProviderSide(),Java((1,1,5)), DPEBootstrap(), DPE()] class TTest1 Service (Service): info = "resources/empty.txt" icon48 = "images/java-floppy.gif" name = "TTest1 Service Implementation" # requires bootstrap services through service factory provides = [TTest1Provider()] consumes = [ProviderSide(), Java((1,1,5)), CorbaService("TTest1SF"), CorbaService("Accounting"), CorbaService("Online Subscription")] #Product class JDK115 (Product): info = "resources/jdk115.txt" name = "JDK 1.1.5 (Win32)" icon48 = "images/jdk-floppy-pure.gif" url = "http://www.sun.com/" provides = [Java((1,1,5))] consumes = [Win32()] class JDK115Sparc (Product): info = "resources/jdk115.txt" name = "JDK 1.1.5 (Solaris)" icon48 = "images/jdk-floppy-pure.gif" url = "http://www.sun.com/" provides = [Java((1,1,5))] consumes = [SolarisABI26()] class MRJ20 (Product): info = "resources/mrj20.txt" name = "MRJ 2.0" icon48 = "images/mrj-floppy.gif" provides = [Java((1,1,6))] consumes = [MacABI()] class Visigenic Nameservice Sol26(RuntimeEnvironment): 72 B.1 Dateien aus dem Projekt Service Composition info = "resources/empty.txt" icon48 = "images/java-floppy.gif" name = "VisiBroker 3.1 Nameservice for Solaris 2.6" consumes = [SolarisABI26(),DPEBootstrap()] provides = [CorbaService("Naming")] class Visigenic osagent Sol26(RuntimeEnvironment): info = "resources/empty.txt" icon48 = "images/java-floppy.gif" name = "VisiBroker 3.1 osagent for Solaris 2.6" consumes = [SolarisABI26(),DPEBootstrap()] provides = [CorbaService("osagent")] class Versant Sol26 (RuntimeEnvironment): name = "Versant for Solaris 2.6" consumes = [SolarisABI26()] provides = [CorbaService("Versant")] resources.py # $Id: resources.py,v 1.12 1999/11/24 18:54:00 loewis Exp $ from SCOSupport import * #balancing levels class BalanceAtBasicHardware: #If resource is not present at the PC level, it can’t be provided from #somewhere else. Still, not having it balanced is temporarily OK def must balance at(self, component): from components import BasicHardware return issubclass(component.desc.kl, BasicHardware) class BalanceAtDomain: def must balance at(self, component): from components import Domain return issubclass(component.desc.kl, Domain) # Component structure class InDomain(StructureResource): pass class InComputer(StructureResource): pass #ABI Variants class ABI(Resource, BalanceAtBasicHardware): def provider for (self, consumed): return isinstance(consumed,self. class ) class i486(ABI): pass class MacABI(ABI): pass class SolarisABI25(ABI): pass class SolarisABI26(ABI): 73 B Ausgewählte Dateien pass class Win32(ABI): pass #Versioned Software: class VersionedSoftware(Resource,BalanceAtBasicHardware): def init (self,version): self.version = version def provider for(self,consumed): return isinstance(consumed,self. class ) and \ self.version >= consumed.version class Java(VersionedSoftware): pass class Swing(VersionedSoftware): pass #Counted resources class Counted(Resource): def init (self,max): self.max=max self.current=0 def provider for(self,r): if self. class != r. class : return 0 if self.current+r.max<self.max: return 0 return 1 def consume(self,r): if not self.provider for(r): return 0 self.current = self.current−r.max self.consumers.append(r) return 1 def unconsume(self,r): self.consumers.remove(r) self.current = self.current + r.max def str (self): return "%s(%d)" % (self. class . name ,self.max) def exhausted(self): return self.current == self.max class VideoIn(Counted, BalanceAtBasicHardware): pass class VideoOut(Counted, BalanceAtBasicHardware): pass class SoundOut(Counted, BalanceAtBasicHardware): pass class Processor(Counted): def init (self): 74 B.1 Dateien aus dem Projekt Service Composition Counted. init (self,1) def must balance at(self, component): from components import BasicHardware return self.fail if not balanced at(component,BasicHardware) #Exact match resources class ExactMatch(Resource): def init (self, variant = None): self.variant = variant def provider for(self,consumed): return self. class == consumed. class self.variant == consumed.variant def and \ str (self): if self.variant: return "%s(%s)" % (self. class . name , self.variant) else: return self. class . name class ProviderSide(ExactMatch): # If a component requires to be on the provider side, grouping it # elsewhere won’t work def must balance at(self, component): from components import Domain return self.fail if not balanced at(component,Domain) class CustomerSide(ExactMatch): def must balance at(self, component): from components import Domain return self.fail if not balanced at(component,Domain) class TTest1(ExactMatch): pass class TTest1Provider(ExactMatch): pass class PA(ExactMatch, BalanceAtBasicHardware): pass class TinaJava(ExactMatch, BalanceAtBasicHardware): pass class CorbaJava(ExactMatch, BalanceAtBasicHardware): pass class CorbaService(ExactMatch, BalanceAtDomain): pass class DPE(ExactMatch, BalanceAtDomain): pass class DPEBootstrap(ExactMatch, BalanceAtDomain): pass class Webview(ExactMatch, BalanceAtBasicHardware): pass 75 B Ausgewählte Dateien class Driver(ExactMatch, BalanceAtBasicHardware): pass B.2 Mögliche XML-Darstellung des Komponentenkatalogs (Ausschnitt) <?xml version="1.0" standalone="yes" encoding="ISO-8859-1"?> <katalog> <komponente> <name>Software</name> <basisklasse>Component</basisklasse> <abstrakt/> <verbraucht> <ressourcen name> InComputer</ressourcen name> </verbraucht> </komponente> <komponente> <name>TestJar</name> <basisklasse>Service</basisklasse> <stellt bereit> <ressourcen name>TTest1</ressourcen name> </stellt bereit> <verbraucht> <ressourcen name>TinaJava</ressourcen name> </verbraucht> <verbraucht> <ressourcen name>Java</ressourcen name> <ressourcen parameter>(1,1,5)</ressourcen parameter> </verbraucht> <verbraucht> <ressourcen name>Swing</ressourcen name> <ressourcen parameter>(1,0,1)</ressourcen parameter> </verbraucht> <eigenschaft> <eigenschaft name>icon48</eigenschaft name> <eigenschaft wert>images/java−floppy.gif</eigenschaft wert> </eigenschaft> <eigenschaft> <eigenschaft name>name</eigenschaft name> <eigenschaft wert>Java Test Services</eigenschaft wert> </eigenschaft> <eigenschaft> <eigenschaft name>info</eigenschaft name> <eigenschaft wert>resources/testServices.txt</eigenschaft wert> </eigenschaft> </komponente> <ressource> <name>ExactMatch</name> <basisklasse>Resource</basisklasse> <funktion> <funk name>provider for</funk name> <parameter>consumed</parameter> <code> <![CDATA[return self. class ==consumed. class and self.variant ==consumed.variant]]> </code> </funktion> </ressource> </katalog> 76 B.2 Mögliche XML-Darstellung des Komponentenkatalogs (Ausschnitt) 77 Abkürzungsverzeichnis CORBA Common Object Request Broker Architecture CVS Concurrent Versions System, Versionsverwaltungssystem DOM Document Object Dokumenten Model, Schnittstelle zur Verarbeitung von XML- DTD Document Type Declaration, definiert die Struktur von XML-Dokumenten GDK GIMP Drawing Kit GMD Fokus Institut für Offene Kommunikationssysteme der GMD - Forschungszentrum Informationstechnik GmbH GNOME GNU Network Object Model Environment, OpenSource Arbeitsumgebung GNU LGPL GNU Library General Public License GTK+ GIMP Toolkit, Bibliothek für grafische Benutzerschnittstellen HTML Hypertext Markup Language HTTP Hypertext Transfer Protocol ODP Open Distributed Processing RDBMS Relationales Datenbank Management System SAX Simple API for XML, Schnittstelle zur Verarbeitung von XML-Dokumenten SCO Service Composition, Projekt zur werkzeugunterstützten Konfigurierung von TINA-Systemen SQL Structed Query Language, Datenmanipulationssprache TINA Telecommunications Information Networking Architecture, Architektur für verteilte Telekommunikationssysteme XML Extensible Markup Language 78 Literaturverzeichnis [ABC+ 98] A PPARAO, V IDUR, S TEVE B YRNE, M IKE C HAMPION, S COTT I SAACS, I AN J ACOBS, A RNAUD L E H ORS, G AVIN N ICOL, J ONATHAN R OBIE, R OBERT S UTOR, C HRIS W ILSON und L AUREN W OOD: Document Object Model (DOM) Level 1 Specification. Technischer Bericht, W3C, 1998. http://www.w3.org/TR/REC-DOM-Level-1. [AFF+ 98] A BARCA , C., P. FARLEY, J. F ORSLÖW, J. C. G ARCÍA, T. H AMADA, P. F. H ANSEN, S. H OGG, H. K AMATA, L. K RISTIANSEN, C. A. L ICCIAR DI , H.M ULDER , E. U TSUNOMIYA und M. YATES : Service Architecture. Technischer Bericht, TINA-C, 1998. [AFG+ 98] A BARCA , C., P. FARLEY, J. C. G ARCÍA, T. H AMADA, P. F. H ANSEN, P. H ELLEMANS, C. A. L ICCIARDI, K. N AKASHIRO und M. YATES: Service Component Specification. Technischer Bericht, TINA-C, 1998. [ASU92] A HO, A LFRED V., R AVI S ETHI und J EFFREY D. U LLMAN: Compilerbau. Addison–Wesley, 1992. [AW95] A BECKER , A NDREAS und H OLGER WACHE: Constraint-Processing in der Konfiguration. Technischer Bericht, DFKI GmbH Kaiserslautern, 1995. [BeO00] GTK+ for BeOS. http://www.gtk.org/beos, 2000. [BHO+ 88] B ALZERT, H ELMUT, H EINZ U. H OPPE, R EINHARD O PPERMANN, H EL MUT P ESCHKE , G ABRIELE R OHR und N ORBERT A. S TREITZ (Herausgeber): Einführung in die Software-Ergonomie, Band 1 der Reihe Mensch Computer Kommunikation : Grundwissen. de Gruyter, 1988. [BR96] B ÖRDING, J OSEF und J ÖRG R AHMER: Abgleich komplexer Ressourcen beim ressourcenorientierten Konfigurieren. In: Beiträge zum 10. Workshop Planen und Konfigurieren, 1996. [Büt97] B ÜTTNER , K LAUS: Rechnerunterstütztes Konfigurieren von Baukastenprodukten. Nummer 246 in Fortschrittberichte VDI Reihe 20. VDI Verlag, Düsseldorf, 1997. [CGS91] C UNIS, R OMAN, A NDREAS G ÜNTER und H ELMUT S TRECKER (Herausgeber): Das PLAKON-Buch. Nummer 266 in Informatik-Fachberichte. Springer-Verlag, 1991. 79 Literaturverzeichnis [DKBS96] Ð URÐANOVI Ć , I GOR, H ANS K LEINE B ÜNING und M ICHAEL S UER MANN : New Aspects and Applications of Resource-based Configuration. Technischer Bericht, Universität Paderborn und NEC Research Institute, 1996. [Dör47] D ÖRNER , H ARTMUT: Modelle für wissensbasiertes Konfigurieren. Doktorarbeit, Martin-Luther-Universität Halle-Wittenberg, 1947. [FOK01] GMD FOKUS – R & D – Competence Centers – PLATIN. http://www. fokus.gmd.de/research/cc/platin/content.html, 2001. [GKK99] G ÜNTER , A NDREAS, I NGO K RENZ und C HRISTIAN K ÜHN: Kommerzielle Software-Werkzeuge für die Konfigurierung technischer Systeme. Künstliche Intelligenz, (3), 99. [GLA] GLADE GTK+ User Interface Builder. http://glade.pn.org/. [Gla00] GladePyC. http://www.fcoutant.freesurf.fr/gladepyc.html, 2000. [GNO00] GNOME. http://www.gnome.org, 2000. [Goe01] G OEBEL , H ARTMUT: decompyle – A Python byte-code reverter. http: //goebel-consult.de/decompyle/, 2001. [GTK00] Gtk+ - The Gimp Toolkit. http://www.gtk.org, 2000. [Hei89] H EINRICH , M ICHAEL: Ein generisches Modell zur Konfiguration technischer Systeme aus modularen Komponenten. In: H EIN, M ANFRED et al. [HTH89], Seiten 49–58. [Hei93] H EINRICH , M ICHAEL: Ressourcenorientiertes Konfigurieren. Künstliche Intelligenz, (1), 1993. [Hen00a] H ENSTRIDGE , J AMES: Libglade. http://www.daa.com.au/~james/ gnome/#libglade, 2000. [Hen00b] H ENSTRIDGE , J AMES: PyGTK. http://www.daa.com.au/~james/ gnome/#pygtk, 2000. [Her98] H ERZOG, R OLF: Signalgeber, Widget-Bibliothek Gtk. iX, Seiten 142– 147, Oktober 1998. [Hül98] H ÜLLERMEIER , E IKE: Approximating cost functions in resource-based configuration. Technischer Bericht, Universität Paderborn, 1998. [Hor93] H ORZ , A LEXANDER (Herausgeber): Beiträge zum 7. Workshop Planen und Konfigurieren, Nummer 723 in Arbeitspapiere der GMD. Gesellschaft für Mathematik und Datenverarbeitung, 1993. 80 [HTH89] H EIN, M ANFRED, W OLFGANG T ANK und J OACHIM H ERZBERG (Herausgeber): Beiträge zum 3. Workshop Planen und Konfigurieren, Nummer 388 in Arbeitspapiere der GMD. Gesellschaft für Mathematik und Datenverarbeitung, 1989. [ITU95] ITU-T: Open Distributed Processing – Reference Model Part1 - Part4, 1995. ITU-T Rec. X.901 - X.904. [KG91] K OPISCH , M ANFRED und A NDREAS G ÜNTER: Configuration of a Passenger Aircraft Cabin based on Conceptual Hierarchy, Constraints and flexible Control. Technischer Bericht, Universität Hamburg, FB Informatik, 1991. [Kun00] K UNTZAGK , A NDREAS: WWW-basierte Informationen über eine Komponentendatenbank. Studienarbeit, Humboldt-Universität zu Berlin, 2000. [Lud01] L UDWIG, E LMAR: Kanalarbeit, GTK statt AWT/Swing in JavaProgrammen. iX, Seiten 127–130, Januar 2001. [Mac00] GTK+ for the MacOS. gtk-mac, 2000. [MU97] M ATTHIESEN, G ÜNTER und M ICHAEL U NTERSTEIN: Relationale Datenbanken und SQL. Addison–Wesley, 1997. [Pen99] P ENNINGTON, H AVOC: GTK+ / Gnome Application Development. New Riders Publishing, 1999. http://developer.gnome.org/doc/ GGAD/. [Pfi93] P FITZNER , K AI: Fallbasiertes Konfigurieren technischer Systeme. Doktorarbeit, Universität Hamburg, 1993. [PyD01] Python Database Modules. http://www.python.org/topics/ database/modules.html, 2001. [Sch97] S CHNEIDER , H ANS -J OCHEN (Herausgeber): Lexikon Informatik und Datenverarbeitung. Oldenbourg Verlag, 1997. [TIN01] TINA-C. http://www.tinac.com/, 2001. [vEB00] E IJK , P ETER VAN und A XEL B ELINFANTE: The Term Processor Kimwitu, Manual and Cookbook. Technischer Bericht, University of Twente, 2000. [vLF00] L ÖWIS, M ARTIN VON und N ILS F ISCHBECK: Python 2. Addison–Wesley, 2000. http://sourceforge.net/projects/ 81 Literaturverzeichnis [vLSN99] L ÖWIS, M ARTIN VON, F RANK S TOINSKI und B ERTRAM N EUBAUER: Projekt Service Composition, Meilenstein 6. nicht veröffentlicht, Januar 1999. [vR00] R OSSUM , G UIDO VAN: Python Reference Manual. http://www. python.org/doc/current/ref/ref.html, Oktober 2000. [vRDJ00] R OSSUM , G UIDO VAN und F RED L. D RAKE J R: Python Library Reference. http://www.python.org/doc/current/lib, Oktober 2000. [XML01] 82 Extensible Markup Language (XML). 2001. http://www.w3c.org/xml, Thesen zur Diplomarbeit 1. Die Methode des ressourcenorientierten Konfigurierens ist geeignet, TINADienste zu erstellen. 2. Die Erweiterung des bestehenden Konfigurationssystems um ein Werkzeug zum Wissenserwerb ermöglicht auch Nicht-Experten die Erfassung der Komponenten solcher TINA-Dienste. 3. Die Bibliothek GTK+ für grafische Benutzerschnittstellen ist geeignet, ein solches Werkzeug zu erstellen. 4. Die Konstruktion von Benutzerschnittstellen mit dem grafischen Werkzeug Glade erleichtert die Programmierung mit GTK+. 5. Die Verarbeitung des bestehenden Katalog-Formates als Pythondateien durch das Wissenserwerb-Werkzeug ist möglich. Erklärung Hiermit erkläre ich, daß ich die vorliegende Arbeit selbstständig, ohne fremde Hilfe und nur unter Nutzung der angegebenen Literatur und Hilfsmittel angefertigt habe. Ich erkläre mich damit einverstanden, daß meine Diplomarbeit öffentlich in der Universitätsbibliothek ausgestellt wird. Berlin, 13. März 2001 Andreas Kuntzagk