Entwicklung einer Steuerinstanz zur interaktiven Kopplung verteilter
Transcription
Entwicklung einer Steuerinstanz zur interaktiven Kopplung verteilter
Entwicklung einer Steuerinstanz zur interaktiven Kopplung verteilter Geräte Bernhard Wally DIPLOMARBEIT eingereicht am Fachhochschul-Masterstudiengang Digitale Medien in Hagenberg im Juni 2006 c Copyright 2006 Bernhard Wally Alle Rechte vorbehalten ii Erklärung Hiermit erkläre ich an Eides statt, dass ich die vorliegende Arbeit selbstständig und ohne fremde Hilfe verfasst, andere als die angegebenen Quellen und Hilfsmittel nicht benutzt und die aus anderen Quellen entnommenen Stellen als solche gekennzeichnet habe. Hagenberg, am 22. Juni 2006 Bernhard Wally iii Inhaltsverzeichnis Erklärung iii Vorwort vii Kurzfassung viii Abstract ix 1 Einleitung 1.1 Problematiken . . . . . . . . . . . . 1.2 Ausblick . . . . . . . . . . . . . . . . 1.3 Abgrenzung . . . . . . . . . . . . . . 1.3.1 Unified Generic Control Point 1.3.2 Janus . . . . . . . . . . . . . 1.3.3 Smart Home Server . . . . . 1.3.4 Shaman . . . . . . . . . . . . 1.3.5 Socam . . . . . . . . . . . . . 1.3.6 Eigener Prototyp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 3 3 4 4 4 4 5 2 Verteilte Systeme 2.1 Middleware . . . . . . . 2.1.1 CORBA . . . . . 2.1.2 Java RMI . . . . 2.1.3 .NET Remoting 2.1.4 XML-RPC . . . 2.2 Service-Plattformen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 7 7 9 10 10 11 . . . . . . 13 14 16 17 19 20 21 . . . . . . . . . . . . . . . . . . . . . . . . 3 Heterogene Systeme 3.1 Arten von Heterogenität . . . . 3.2 Methoden der Homogenisierung 3.3 OSGi . . . . . . . . . . . . . . . 3.3.1 Das OSGi-Framework . 3.3.2 Das OSGi-Bundle . . . 3.3.3 Dienste . . . . . . . . . iv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INHALTSVERZEICHNIS 3.3.4 v OSGi-Implementierungen . . . . . . . . . . . . . . . . 4 Anforderungen 4.1 Model . . . . . . . . 4.1.1 Komponenten 4.1.2 Kopplungen . 4.2 Control . . . . . . . 4.2.1 Komponenten 4.2.2 Kopplungen . 4.2.3 Schaltbild . . 4.3 View . . . . . . . . . 24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 26 26 33 37 38 39 39 39 5 Technologieauswahl 5.1 Grafische Programmierwerkzeuge 5.1.1 Virtools Dev . . . . . . . 5.1.2 Max . . . . . . . . . . . . 5.1.3 vvvv . . . . . . . . . . . . 5.1.4 Fazit . . . . . . . . . . . . 5.2 Programmiersprache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 40 40 43 45 46 47 . . . . . . . . . . . . . . . 49 51 51 55 58 60 61 61 64 66 70 73 73 76 79 83 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Umsetzung 6.1 Kern-Bundles . . . . . . . . . . 6.1.1 Component-Bundle . . . 6.1.2 Link-Bundle . . . . . . . 6.1.3 Converter-Bundle . . . . 6.1.4 Position-Bundle . . . . . 6.2 GUI-Komponenten . . . . . . . 6.2.1 GUI-Basis . . . . . . . . 6.2.2 Component Factories . . 6.2.3 Components und Links 6.2.4 Converters . . . . . . . 6.3 Erweiterungen . . . . . . . . . 6.3.1 Standard Ingredients . . 6.3.2 UPnP . . . . . . . . . . 6.3.3 Remote GUI . . . . . . 6.4 Szenario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Fazit 86 7.1 Ergebnisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 7.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 7.3 Schlussbemerkung . . . . . . . . . . . . . . . . . . . . . . . . 88 A Entwicklung der If-Component 89 INHALTSVERZEICHNIS B Inhalt der CD-ROM B.1 Diplomarbeit . . . . B.2 Literatur . . . . . . . B.3 Bilder und Grafiken B.4 Ausführbare Dateien B.5 Quelldateien . . . . . B.6 Externe Bibliotheken B.7 Abhängigkeiten . . . B.8 Beispiel-Mappings . B.9 Sonstige Dateien . . vi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 92 92 93 93 93 94 95 95 95 Abkürzungsverzeichnis 96 Literaturverzeichnis 98 Vorwort Der Entschluss, nach dem Bakkalaureats- ein Master-Studium in Hagenberg anzuhängen, war schnell gefasst und stand eigentlich nie außer Frage. Nach insgesamt fünf Jahren in Hagenberg ist meine Begeisterung für digitale Medien in keiner Weise gedämpft worden, und ich möchte mich auch in Zukunft in diesem Themenbereich betätigen. Das Studium wäre nicht ohne die Unterstützung meiner Eltern möglich gewesen, die mir nicht nur finanziell unter die Arme griffen, sondern mir mit viel Verständnis, unendlicher Geduld und ermutigenden Worten zur Seite standen. Ich möchte ihnen an dieser Stelle meinen herzlichsten Dank aussprechen. Meinem Vater danke ich darüber hinaus für die vielen tausend Kilometer die er meinetwegen zwischen Wien und Hagenberg zurückgelegt hat, sowie für sein Engagement beim Korrekturlesen dieser Diplomschrift. Auch meinen Freunden (allen voran meine liebe Anna) danke ich für ihre Treue, die sie in den letzten fünf Jahren bewiesen haben. Jedesmal, wenn ich in ihrem Kreise war, fühlte ich mich wohl und verstanden. Die geographische Distanz, die uns trennte, machte jedes Treffen zu einem besonderen Erlebnis, das ich ausgiebig genoss. Meinem Projektpartner, Jakob Doppler, danke ich für zwei wunderbare Jahre der Zusammenarbeit, in denen wir zu engen Freunden wurden. Gerne und mit Wehmut denke ich an die vielen, vielen Tage zurück, in denen wir gemeinsam in meinem Zimmer saßen und neue Ideen sponnen, einander mit Technologie-Tipps versorgten und nebenbei viel Spaß hatten. Zuletzt richtet sich mein Dank an meinen Diplomarbeits-Betreuer, DI Dr. Christoph Schaffer, der mir auch schon als Projekt-Coach in vorangegangenen Semestern beratend zur Seite stand. Seine Ideen und kritischen Anmerkungen leisteten einen wertvollen Beitrag zu dem Projekt, das in dieser Arbeit vorgestellt wird. Er schaffte es immer wieder, meine Neugier auf mir unbekannte Technologien zu lenken und erinnerte mich wiederholt daran, auch komplexe Vorgänge nicht einfach hinzunehmen, sondern bewusst zu hinterfragen. vii Kurzfassung Die Vernetzung des alltäglichen Lebens nimmt immer mehr zu. Die große Zahl an netzwerkfähigen Endgeräten und deren unterschiedliche Anforderungen, hat auch zu einem Zuwachs an Netzwerktechnologien und deren Kommunikationsprotokollen geführt. Um die damit verbundene zunehmende Heterogenität in den Griff zu bekommen, werden Standards entwickelt, die das Kommunizieren von im Netzwerk verteilten Geräten untereinander vereinfachen sollen. In dieser Arbeit wird die Entwicklung von Torem erläutert, einer Applikation, die es ermöglicht, die Funktionalitäten verschiedener verteilter Geräte zu nutzen, um andere verteilte Geräte zu steuern. Die Kopplung der Geräte miteinander wird Technologie-übergreifend realisiert, sodass z. B. eine Jalousie, die in ein Gebäudesteuerungs-System integriert ist, von einem Drucksensor gesteuert werden kann, der selbst keine Möglichkeit hat, mit der Jalousie direkt zu kommunizieren. Das Definieren der Kopplungen erfolgt über eine grafische Benutzeroberfläche, die diese Verbindungen auch visualisiert. Torem ist speziell auch für technisch nicht allzu versierte Personen ausgelegt, da die Netzwerkkommunikation komplett abstrahiert wird. Für den Benutzer werden die Funktionen der verteilten Geräte als Funktionsblöcke präsentiert, die miteinander verbunden werden können. viii Abstract Computer networks are extraordinarily prevalent in today’s world. The increasing amount of network-capable devices has led to an increase in network technologies and their communication protocols, since different devices define different requirements for networking. Standards are developed to address the involved heterogeneity and to simplify communication among distributed devices. This paper discusses the development of Torem, an application that allows distributed devices to use functions of other distributed devices via linking. The links are realized in a technology-independent manner so that a sunblind integrated into a home automation system can be controlled by a pressure sensor that could normally not communicate with the sunblind. The creation of the links is accomplished using a graphical user interface which is also responsible for visualizing the defined links. Torem was specifically designed for use by less technologically inclined individuals, in that the underlying network communication is completly transparent to the user. The functions of the distributed devices are displayed as building blocks that can be interconnected. ix Kapitel 1 Einleitung Ihr Mobiltelefon piepst – eine neue Kurzmitteilung: Das Backrohr hat die ” gewünschte Temperatur von 180◦ erreicht“. Sie gehen in die Küche, schieben den Kuchenteig ins Rohr und aktivieren, zurück in ihrem Arbeitszimmer, die elektronische Eieruhr. In 50 Minuten werden sie eine weitere Kurznachricht erhalten, die sie daran erinnern soll, den Kuchen zu inspizieren und gegebenenfalls aus dem Rohr zu nehmen. Auch wenn sie nun in den Garten gehen würden, auf den Kuchen würden sie mit Sicherheit nicht vergessen. Sie entscheiden sich jedoch, es sich in der Zwischenzeit ein wenig gemütlich zu machen. Nachdem im Menü des Mobiltelefons der richtige Eintrag gefunden ist, genügt ein weiterer Tastendruck und schon wird das Licht gedimmt, die Stereo-Anlage beginnt in angemessener Lautstärke ihr aktuelles Lieblingsalbum abzuspielen und der Wasserkocher schaltet sich ein, um Teewasser zu erhitzen. Dieses Szenario zeigt eine mögliche Anwendung für die Steuerung verteilter Geräte in einem Haushalt. Die einzelnen Geräte werden je nach Typ über drahtlose oder kabelgebundene Protokolle angesprochen und miteinander in Bezug gesetzt. Doch nicht nur im Haushalt bietet sich eine solche Kopplung von Geräten an. Auch bei örtlich ausgedehnten Multimedia-Installationen gilt es, die verschiedenen Komponenten mit Informationen zu versorgen oder von ihnen bereitgestellte Informationen auszulesen, um damit andere Geräte anzusteuern. So könnte etwa eine Videokamera, beim Eingang einer Firma installiert, einen Video-Beamer im Foyer mit Bilddaten versorgen, die von einer dritten Komponente – im Foyer verteilte Drucksensoren – manipuliert würden. Wie ein solcher Aufbau auch konkret aussieht – bei der Verwendung technologisch unterschiedlicher Komponenten, die miteinander über ein oder mehrere Netzwerke kommunizieren sollen, stellen sich einige Probleme, die es zu lösen gilt. Der nächste Abschnitt wird kurz darauf eingehen. 1 KAPITEL 1. EINLEITUNG 1.1 2 Problematiken Verteilte Anwendungen sind im Gegensatz zu einer lokal laufenden Applikation mit zusätzlichen Problemstellungen konfrontiert. Es gilt etwa festzustellen, ob die benötigten Netzwerkressourcen überhaupt verfügbar sind. Bei dynamisch erweiterbaren Netzwerken ist es wichtig herauszufinden, welche (kompatiblen) Komponenten zu einem gewissen Zeitpunkt im Netzwerk vorhanden sind, und wie diese angesprochen werden können. Auch sind Strategien zu überlegen, wie man mit dem plötzlichen Wegfall einer Ressource umgeht, und wie das System darauf reagiert, wenn die Ressource wieder verfügbar wird. Diese Problemstellungen werden unter dem Begriff ver” teilte Systeme“ zusammengefasst behandelt [28]. Doch es gibt auch andere Problematiken, die sich hauptsächlich aufgrund der unterschiedlichen Basistechnologien der einzelnen Komponenten ergeben. Daten in einem IP1 -basierten Netzwerk lassen sich recht einfach zwischen zwei oder mehreren Stationen austauschen. Auch zwischen Bluetooth2 -Geräten ist der Austausch von Daten spezifiziert. Doch um von einem Bluetooth-Gerät auf eine Komponente in einem IP-basierten Netzwerk zugreifen zu können, bedarf es schon etwas komplizierterer Verfahren3 . Ein aus unterschiedlichen miteinander verbundenen Netzwerken gebildetes Gesamtsystem nennt man auch heterogenes System“, wenn die Kommunika” tion zwischen den verschiedenen Netzwerken nicht direkt von den jeweiligen Protokollen unterstützt wird [3]. Glücklicherweise gibt es aber Konsortien und Organisationen, die sich sowohl mit verteilten, wie auch mit heterogenen Systemen auseinandersetzen und in diesen Bereichen (offene) Standards entwickeln sowie teilweise frei verfügbare Programmbibliotheken zur Verfügung stellen, die das Management solcher Systeme vereinfachen sollen. Verwendet oder erstellt man Komponenten, die auf einem offenen Standard basieren, erhöht sich natürlich die Chance, diese Komponenten auch in anderen Projekten wieder einsetzen zu können, oder gar auf Komponenten von Drittherstellern zurückgreifen zu können, die ebenfalls diesem Standard genügen. 1.2 Ausblick Im Zuge dieser Diplomarbeit sollen die Chancen und Problematiken von verteilten, heterogenen Applikationen erläutert und anhand einer Beispielimplementierung veranschaulicht werden. Besonderer Wert wird auf die Nutzung offener Standards und die mögliche Erweiterbarkeit der entwickelten 1 Internet Protocol. Ein weitverbreitetes Netzwerkprotokoll, das unter anderem die Grundlage für das Internet ist. 2 http://www.bluetooth.com/ 3 Es sei denn, das Bluetooth-Gerät unterstützt das Personal Area Networking Profile; dann bettet es sich als IP-Komponente ein. KAPITEL 1. EINLEITUNG 3 Applikation gelegt. Um die im Netzwerk verteilten Geräte und Dienste auszunutzen, wird eine grafische Benutzeroberfläche entwickelt, die es erlaubt, verteilte Geräte zu koppeln. So sollen Geräte, die voneinander eigentlich nichts wissen, zu einem Gesamtsystem verschmolzen werden, das für den Benutzer einen Mehrwert im Vergleich zur Summe der Einzelkomponenten darstellt. Für die Beispielimplementierung werden die beiden offenen Standards Universal Plug and Play 4 (UPnP) als Kommunikationsprotokoll und Open Services Gateway Initiative 5 (OSGi) als erweiterbare Applikationsbasis zum Einsatz kommen, auf deren Hintergrund in Kap. 2 bzw. 3 näher eingegangen wird. Das Kernthema dieser Arbeit, die Entwicklung einer Steuerinstanz zur Kopplung verteilter Geräte, wird in den Kap. 4, 5 und 6 behandelt. In Ersterem soll erklärt werden, welche Anforderungen an die zu entwickelnde Applikation gestellt werden, Zweiteres stellt vergleichbare Applikationen vor und prüft diese auf Verwendbarkeit, Letzteres beschreibt schließlich die Umsetzung. Kap. 7 zieht ein Resumee über die Beispielimplementierung und gibt einen Ausblick auf mögliche zukünftige Entwicklungen. 1.3 Abgrenzung Natürlich gibt es in dem in dieser Arbeit behandelten Themengebiet bereits Projekte, die ähnliche oder ergänzende Ziele haben. Einige dieser Projekt sollen im Folgenden kurz charakterisiert werden. 1.3.1 Unified Generic Control Point Der Unified Generic Control Point [1] ist eine kombinierte Steuerinstanz für UPnP- und Jini 6 -fähige Geräte, die es erlaubt, mit nur einer grafischen Benutzeroberfläche, die Geräte dieser beiden Welten zu steuern. Sogar Ereignisse, die in UPnP oder Jini generiert werden, können auf Geräte des jeweiligen anderen Protokolls weitergeleitet werden. Schließlich ist es noch möglich über einen Ereignisdialog“ zu definieren, ob auf ein bestimmtes ” Ereignis automatisch mit einer bestimmten Aktion reagiert werden soll. Das ermöglicht bspw. das Szenario, dass beim Einschalten des Lichts A auch das Licht B eingeschalten werden soll. Die Ereignisbehandlung ist jedoch nur für eine Folgeaktion ausgelegt und bietet keine Möglichkeit auf den Wert des Ereignisses zu reagieren – es ist nur bekannt, dass ein bestimmtes Ereignis ausgelöst wurde [1]. 4 http://www.upnp.org/ http://www.osgi.org/ 6 http://www.jini.org/ 5 KAPITEL 1. EINLEITUNG 1.3.2 4 Janus Für die Entwicklung eines Automobil-Cockpit-Simulators wird eine OSGibasierte Applikation konzipiert und umgesetzt, die vor allem auf Multimodalität ausgelegt ist [7]. Das bedeutet, dass die Ein- und Ausgabegeräte des Simulators mehrere Benutzerschnittstellen (z. B. Sprache, Text, Grafik) anbieten können. Durch Applikations-Plugins können diese Geräte miteinander in Verbindung gesetzt werden, wobei je nach Situation eine geeignete Benutzerschnittstelle verwendet wird, um dem Benutzer die Interaktion mit dem System zu ermöglichen. Die hier im Mittelpunkt stehende Multimodalität wird im eigenen Prototyp jedoch nicht berücksichtigt [7]. 1.3.3 Smart Home Server Der Smart Home Server [39] setzt, wie auch das eigene Projekt, auf OSGi und UPnP auf und stellt die Implementierung eines Servers für das intelli” gente Eigenheim“ 7 dar. Im Gegensatz zu dem in dieser Arbeit vorgestellten Ansatz werden die im Smart Home Server erkannten verteilten Geräte jedoch als unabhängig voneinander gesehen. Dementsprechend ist es zwar möglich, Emails abzurufen und die Jalousien der Wohnzimmerfenster fernzusteuern, eine Kopplung dieser beiden Dienste zur Laufzeit ist jedoch nicht vorgesehen und müsste bereits vorab definiert werden [39]. 1.3.4 Shaman Als zum eigenen Prototypen komplementäres Projekt soll Shaman [31], ein erweiterbares Java-basiertes Dienst-Gateway, erwähnt werden: Es integriert kleine netzwerkfähige Sensor-Aktuator-Module (SAM) in heterogene Netzwerke. Da SAMs typischerweise über sehr begrenzte Systemressourcen verfügen, werden gewisse Funktionalitäten, die für komplexe Service-Plattformen notwendig sind, vom leistungsstärkeren Gateway übernommen. Dadurch ist es möglich, SAMs über Technologien, wie z. B. Jini oder UPnP, anzusprechen, die normalerweise zu aufwändig für sie wären. Um die Funktionalität von Shaman im eigenen Prototypen verwenden zu können, könnte das Gateway-System als Plugin eingebunden werden [31]. 1.3.5 Socam Socam (Service-Oriented Context-Aware Middleware) [9] ist eine Middleware-Architektur, die auf einem Kontext-Modell, basierend auf der Web Ontology Language 8 (OWL), aufbaut und die Definition Kontext-spezifischer Szenarien erlaubt. Über Sensorwerte wird versucht, den aktuellen Status und 7 Im Englischen werden für die Thematik des intelligenten, vernetzten, selbstregulierenden Eigenheims die Begriffe Home Automation“ und Smart Home“ verwendet. ” ” 8 http://www.w3.org/2004/OWL/ KAPITEL 1. EINLEITUNG 5 damit den Kontext festzustellen, in dem sich ein bestimmter Teil des Systems befindet. Die Definitionen können mittels Vorwärts- und Rückwärtsverkettungen9 sowie einem hybriden Ansatz angegeben werden. Die Auswertung der Definitionen erfolgt zur Laufzeit und resultiert in der Generierung von Aktionen, die schließlich umgesetzt werden. Socam wurde als Sammlung von OSGi-Bundles entwickelt [9]. 1.3.6 Eigener Prototyp Die eigene Implementierung setzt sich vor allem mit dem Prozess der Kopplung sowie der Umsetzung der definierten Kopplungen von verteilten Geräten auseinander. Der Unified Generic Control Point zeigt anhand von UPnP und Jini, wie es möglich ist, zwei unterschiedliche Service-Plattformen miteinander zu kombinieren. Der beschriebene Ansatz könnte als Vorlage dazu dienen, um auch im eigenen Prototyp mehrere Protokolle unterstützen zu können. Janus hat seinen Schwerpunkt auf der Multimodalität, die in dieser Arbeit nicht behandelt wird. Die Idee der Kopplung der virtuellen Ein- und Ausgabegeräte ist jedoch ähnlich zu dem, was in dieser Arbeit erreicht werden soll. Im Gegensatz zum Smart Home Server soll es im eigenen Prototyp möglich sein, ein eingegangenes Email nach Schlüsselworten zu durchsuchen, um anschließend die Steuerung der Jalousie entsprechend vorzunehmen, wobei diese Definition zur Laufzeit erstellbar sein sollte. Shaman bietet sich als ideale Ergänzung an, um auch SAMs in die Kopplungsdomäne einbeziehen zu können und könnte nach einer Portierung in ein OSGi-Bundle zum eigenen Prototyp hinzugefügt werden. Socam spezialisiert sich vor allem auf das Erkennen und Anwenden des aktuellen Kontexts, in dem sich verschiedene Entitäten, die im System registriert sind, befinden. Bewusstsein über den Kontext der entdeckten verteilten Geräte ist in dieser Arbeit nicht von Bedeutung, könnte jedoch als Erweiterung hinzugefügt werden. Ebenso wäre der Ansatz der Vorwärts- und Rückwärtsverkettungen eine gute Möglichkeit, um verschiedene Geräte automatisiert miteinander zu verbinden. Wichtig für diese Arbeit ist jedoch vordergründig, dass der beschriebene Prototyp dem Benutzer die Mittel zu Verfügung stellt, eine möglichst große Zahl von Szenarien umzusetzen. Diese sollen dabei nach Möglichkeit nicht vom Verwaltungs-System des Prototyps beschränkt werden, sondern ausschließlich von den Möglichkeiten der zu koppelnden Geräte und der Fantasie des Benutzers. 9 engl. Forward- und Backward-Chaining Kapitel 2 Verteilte Systeme Die Grundlage für verteilte Systeme bilden Rechnernetze, die in [3] folgendermaßen charakterisiert werden: Die Anzahl und Nutzung von Rechnernetzen nimmt explosionsartig zu. Vor zwei Jahrzehnten hatten nur wenige Leute Zugang zu einem Netz. Heute ist die Computerkommunikation ein wesentlicher Bestandteil unserer Infrastruktur. Vernetzung wird in jedem Bereich des Geschäftslebens genutzt, wie Werbung, Fertigung, Versand, Planung, Rechnungswesen und Buchhaltung. Folglich haben die meisten Unternehmen mehrere Netze. Im Bildungswesen werden Rechnernetze von der Grundschule bis zur Universität eingesetzt. Regierungsämter auf Bundes-, Landesund Kommunalebene arbeiten ebenso mit Rechnernetzen wie militärische Organisationen. Kurz: Rechnernetze sind überall. Für gewöhnlich wird der Begriff Rechnernetze für mehrere miteinander verbundene autonome Computer verwendet. Der Unterschied zwischen Rechnernetzen und verteilten Systemen liegt darin, dass in einem verteilten System die Existenz mehrerer autonomer Rechner für den Benutzer nicht sichtbar ist [37]. Mit Benutzer“ ist hier nicht nur der Anwender eines Computer” Programms gemeint, sondern auch schon dessen Programmierer. Damit ein Rechnernetz abstrahiert und somit für den Programmierer unsichtbar“ ge” macht werden kann, bedarf es einer Infrastruktur, die diese Abstraktion vornimmt. Genau das ist die Aufgabe sogenannter Middleware 1 – sie bildet eine Zwischenschicht zwischen dem Programm und dem Netzwerk [28]. Middleware erleichtert also den Zugriff auf entfernte Ressourcen, indem sie den notwendigen Auf- und Abbau der Netzwerkverbindungen sowie die direkte Netzwerkkommunikation übernimmt. Darauf aufbauend bieten Service-Plattformen weitere Möglichkeiten, wie das Auffinden und Nutzen von verteilten Geräten2 über Hersteller-, Sprach- oder Plattformgrenzen hinweg. 1 2 Im Deutschen auch als Verteilungsplattform bezeichnet. Als verteiltes Gerät wird ein Gerät bezeichnet, das Teil eines verteilten Systems ist. 6 KAPITEL 2. VERTEILTE SYSTEME 2.1 7 Middleware In den letzten Jahren wurden mehrere Methoden entwickelt, die das Erstellen eines verteilten Systems basierend auf Middleware ermöglichen. Parallel zur Entwicklung der Programmiersprachen passte sich auch Middleware dahingehend an, objektorientierte Programmiersprachen zu unterstützen [3]. Nicht-objektorientierte Middleware lässt sich unter dem Begriff Remote Procedure Call (RPC) zusammenfassen. Obwohl objektorientierte Programmiersprachen auf dem Vormarsch sind, gibt es immer noch Anwendungsfälle, in denen RPC-Lösungen vorzuziehen sind, oder in denen eine Objektorientierung nicht viel Sinn macht. Zu diesen Fällen zählen vor allem InternetDienste, die meist eine Schnittstellenbeschreibung im Extensible Markup Language3 (XML) Format besitzen. Auf diese besondere Gattung der RPCs mittels XML-Beschreibung wird in Abs. 2.1.4 eingegangen. Ansonsten werden nur objektorientierte Lösungen beschrieben. 2.1.1 CORBA CORBA, kurz für Common Object Request Broker Architecture, ist eine weitverbreitete objektorientierte Middleware, die von der Object Management Group 4 (OMG) als offener Standard entworfen wurde. Wie alle anderen Spezifikationen der OMG ist auch CORBA kostenlos von der Website der OMG beziehbar5 . Die derzeit aktuelle Version der Spezifikation ist 3.0.3 vom 1. März 2004. Um in einer Anwendung CORBA verwenden zu können, muss zunächst definiert werden, auf welche Objekte entfernte Zugriffe durchgeführt werden sollen. Diese Objekte werden dann mit der Interface Definition Language 6 (IDL) als Schnittstellen definiert. Die somit erstellte IDL-Datei wird in weiterer Folge kompiliert – der IDL-Compiler sollte bei jeder CORBADistribution dabei sein – und es werden einige Quelltext-Dateien in der gewünschten Programmiersprache erzeugt, die schließlich im eigenen Programmcode verwendet werden können. Die Schlüsselkomponenten, die in diesem Prozess generiert werden, sind die Stubs und die Skeletons. Die Stubs abstrahieren die Kommunikation auf der Seite des Clients, während die Skeletons die Kommunikation auf der Seite des Servers abstrahieren (s. Abb. 2.1). Eine wichtige Eigenschaft von CORBA ist, dass die Kommunikation auch sprachübergreifend ausgeführt werden kann, da die IDL sprachunabhängig ist. Es ist also möglich, von einem Client-Programm, in C geschrieben, auf einen Server, der in Java implementiert ist, zuzugreifen. 3 http://www.w3.org/XML/ http://www.omg.org/ 5 Der genaue URL lautet: http://www.omg.org/technology/documents/corba spec catalog.htm 6 http://www.omg.org/gettingstarted/omg idl.htm 4 KAPITEL 2. VERTEILTE SYSTEME 8 Abbildung 2.1: Funktionsweise von CORBA (nach [19]). Es gibt mehrere Implementierungen der CORBA-Spezifikation für eine Vielzahl von Sprachen – einige frei verfügbare sind: • JavaTM IDL ist Teil des JavaTM 2 Platform Standard Edition Development Kit 7 (JDK) von Sun8 . Es genügt nach [35] der CORBAVersion 2.3.1 vom 7. Oktober 1999 und kann von beliebigen Javabasierten Programmen benutzt werden. • MICO9 – das Wort ist eine rekursive Abkürzung10 nach dem Vorbild von GNU11 und bedeutet Mico is Corba“ – ist eine Open Source ” Implementierung in C++. Im Juni 1999 wurde MICO, damals in der Version 2.2.7, offiziell als konform gemäß der CORBA-Version 2.1 bezeichnet [38]. • mico/E12 ist eine frei verfügbare Implementierung nach dem Vorbild von MICO, jedoch in der Programmiersprache Eiffel. Das E“ im ” Namen steht sowohl für Eiffel“ als auch für Education“ (dt. Ausbil” ” dung) – eine Anspielung auf den ursprünglichen Grund für die Entwicklung: Es wurde für Kurse und Software-Übungen benötigt [16]. • Fnorb13 ist eine experimentelle CORBA Implementierung in Python, die vom Centre of Excellence in Distributed Systems Technologies 14 (DSTC) geschaffen wurde und mittlerweile als Open Source 15 Projekt bei SourceForge.net16 weiterentwickelt wird. 7 http://java.sun.com/j2se/ http://www.sun.com/ 9 http://www.mico.org/ 10 Eine rekursive Abkürzung beinhaltet sich selbst als Teil der Abkürzung. Siehe auch: http://de.wikipedia.org/wiki/Rekursives Akronym 11 GNU steht für GNU is not Unix“ ” 12 http://www.math.uni-goettingen.de/micoe/ 13 http://fnorb.sourceforge.net/ 14 http://www.dstc.edu.au/ 15 Bei Open Source Projekten ist der Quellcode offen zugänglich und kann von jedermann eingesehen werden. 16 Eine der größten Gemeinschaften für die Entwicklung freier Software. Die Adresse der Website lautet http://www.sourceforge.net/. 8 KAPITEL 2. VERTEILTE SYSTEME 9 Neben den hier aufgelisteten Projekten gibt es noch eine Menge anderer, auch kommerzieller, Implementierungen – eine umfangreiche Liste findet sich in [2]. Jene Liste ist zwar nicht vollständig und beinhaltet auch einige ungültige Links, doch sie gibt einen guten Überblick über die Menge an CORBA-Implementierungen. Damit ein Client über CORBA eine Methode auf einem Objekt des Servers ausführen kann, muss er zunächst eine Instanz dieses Objekts erhalten. Eine Möglichkeit dafür ist, eine eigene Applikation, die einen Naming Service 17 beinhaltet zu starten, bei der sich zunächst die Server-Applikation registriert, und die dann einem Client auf Anfrage das richtige Objekt über eine Zeichenkette als Schlüssel vermittelt. 2.1.2 Java RMI JavaTM Remote Method Invocation18 ist ebenso wie JavaTM IDL Teil des JDK und kann somit in jeder Java-Applikation genutzt werden. Java RMI kann auf zwei unterschiedliche Arten genutzt werden, die sich im Übertragungsprotokoll und damit der Anwendungsmöglichkeit unterscheiden: 1. Java RMI über Internet InterORB Protocol (IIOP) erlaubt es, mit dem RMI API19 CORBA-kompatible Server- oder Client-Applikationen zu entwickeln. 2. Das Java Remote Method Protocol (JRMP) ist im Gegensatz zu Java RMI über IIOP auf die Java-interne Verwendung ausgelegt und dahingehend optimiert. Für Java-Versionen vor 5.0 mussten auch für JRMP Stubs manuell erzeugt werden20 , seit der Version 5.0 ist das nicht mehr notwendig – die benötigten Hilfsklassen werden zur Laufzeit automatisch erstellt. Beide Methoden unterstützen Distributed Garbage Collection 21 , jedoch wird in [36] davon abgeraten, diese bei Java RMI über IIOP zu benutzen und in [33] wird eingeräumt, dass es bei der Benutzung der Distributed Garbage Collection unter gewissen Umständen auch mit JRMP zu Problemen kommen kann. 17 Der Naming Service ist eine Möglichkeit, Objekte mit Namen zu Verknüpfen. Eine solche Verknüpfung heißt Name Binding [18] 18 Dt. der Methodenaufruf auf (netzwerktechnisch) entfernten Objekten. 19 Application Programming Interface – eine Auflistung und Beschreibung von Funktionen, die eine Software-Bibliothek zur Verfügung stellt. Im objektorientierten Sinn werden nicht Funktionen sondern Objekte und deren Methoden beschrieben. 20 Vor Version 1.2 war auch das Erzeugen von Skeletons noch notwendig – seit 1.2 sorgte ein zusätzliches Stub-Protokoll dafür, dass Skeletons nicht mehr verwendet werden mussten. 21 Garbage Collection (dt. automatische Speicherbereinigung) ist eine der herausragenden Eigenschaften von Java, die es Entwicklern ermöglicht, die Speicherverwaltung dem System zu überlassen. KAPITEL 2. VERTEILTE SYSTEME 10 Wie CORBA benötigt auch Java RMI eine RMI Remote Object Registry, die – wie der Naming Service von CORBA – einem Client über einen Namen ein Objekt vermittelt, das zuvor von einer Server-Applikation registriert wurde. 2.1.3 .NET Remoting .NET Remoting ist das Pendant zu Java RMI von Microsofts22 .NET Framework. Es ist sehr flexibel und kann, ähnlich wie Java RMI, mit zwei unterschiedlichen Protokollen verwendet werden: Die Daten werden entweder binär, und damit schneller, oder im XML-Format zwischen den Endpunkten transportiert. Die zweitere Methode ermöglicht die Interoperabilität mit anderen, von .NET Remoting unabhängigen, Kommunikationsprotokollen [17]. Wie bei CORBA und Java RMI muss auch bei .NET Remoting der Server die extern erreichbaren Objekte zunächst im Remoting Framework registrieren, bevor diese von Clients genutzt werden können. Ein wichtiger Unterschied zwischen .NET Remoting und Java RMI liegt in der Unterstützung von XML über HTTP (s. Abs. 2.1.4) bzw. CORBA. CORBA-Implementierungen existieren zwar für viele Plattformen und Programmiersprachen, jedoch nicht für alle. XML über HTTP hingegen basiert auf viel grundlegenderen Protokollen und kann daher leichter selbst implementiert werden. Außerdem werden Verbindungen über HTTP von nahezu jeder verbreiteten Programmiersprache unterstützt. .NET Remoting entspricht damit dem derzeitigen Trend zur verstärkten Nutzung von Web Services (s. Abs. 2.1.4), die auf dem Austausch von XML-Nachrichten basieren. Dabei soll aber nicht vergessen werden, dass es, neben anderen Programmiersprachen, auch für Java bereits Bibliotheken gibt, die die Benutzung von Web Services sowohl auf der Server- wie auch auf der Client-Seite vereinfachen. 2.1.4 XML-RPC XML-RPC steht für XML-basierte RPCs. Die verbreitetste Anwendungsform sind RPCs, die von HTTP23 -Anfragen getriggert werden. HTTP dient dabei als Transportprotokoll, der Inhalt der Anfrage wird XML-kompatibel formatiert. Auch für die XML-Nachrichten gibt es bereits einen weitverbreiteten Standard, das Simple Object Access Protocol (SOAP). Ein Beispiel soll diese Zusammenhänge verdeutlichen. Um mit der Funktion activateHeating per SOAP-kompatiblem XML-RPC die Heizung eines Gebäudes zu aktivieren, könnte folgende HTTP-Nachricht an den Server der Gebäudesteuerung versandt werden: 22 http://www.microsoft.com/ Hypertext Transfer Protocol – Das Standardtransferprotokoll des Internets. Eine genauere Beschreibung findet sich in [37] 23 KAPITEL 2. VERTEILTE SYSTEME 11 Listing 2.1: Beispiel für eine SOAP-Anfrage 1 2 3 4 POST HTTP /1.0 Host: 10.17.1.24 Content - Type: text / xml Content - length: 193 5 6 7 8 9 10 11 <? xml version = " 1.0 " ? > < env:Envelope xmlns:env = " http: // www . w3 . org /2003/05/ soap envelope " > < env:Body > < activateHeating xmlns = " http: // sample . at / heating " / > </ env:Body > </ env:Envelope > Die Zeilen 1 bis 4 beschreiben den HTTP-Header, Zeile 6 definiert den Beginn der XML-Nachricht. Die Zeilen 7, 8, 10 und 11 sind Implementierungen des SOAP-Standards, die Zeile 9 beinhaltet schließlich den tatsächlichen Aufruf der Funktion activateHeating, die keine Parameter benötigt. Die XML-Nachricht wird vom Server verarbeitet und die entprechende Funktion ausgeführt. Anschließend wird noch eine Antwort generiert und an die im HTTP-Header angegebene Adresse geschickt. Die Antwort kann entweder Rückgabewerte der Funktion beinhalten oder kundtun, dass der Aufruf der Funktion fehlgeschlagen ist. Die Fehlermeldung kann mit einer genaueren Beschreibung des Problems versehen werden. XML-RPC ist eine hervorragende Methode für die Kommunikation zwischen Anwendungen. Die dafür verfügbar gemachten Programmschnittstellen lassen sich als Web Services bezeichnen [41]. Das W3C24 hat einen Standard geschaffen, der Web Services genau spezifizieren lässt. Die Schnittstellen werden in der Web Services Description Language (WSDL), selbst natürlich XML-konform, beschrieben und Entwicklern von Applikationen, die diese Web Services nutzen wollen, zugänglich gemacht. Ein populäres Beispiel für die Anwendung von Webservices ist die von Google25 zur Verfügung gestellten Schnittstellen, die es ermöglichen, von eigenen Applikationen die umfangreichen Suchfunktionen von Google zu nutzen. 2.2 Service-Plattformen Service-Plattformen bauen auf Middleware-Lösungen auf und bieten eine weitere Vereinfachung für Entwickler und Benutzer von Service-Plattformbasierten Anwendungen. Die beiden wichtigsten Service-Plattformen sind UPnP und Jini. An dieser Stelle sollen nicht die genauen Funktionsweisen von UPnP und Jini erläutert werden, da dies in [5] bzw. [42] bereits ausführlich gemacht 24 25 World Wide Web Consortium – siehe auch: http://www.w3.org/ Derzeit die Suchmaschine im Internet. Siehe auch http://www.google.com/ KAPITEL 2. VERTEILTE SYSTEME 12 wird. Stattdessen werden die wichtigsten Prinzipien von UPnP dargestellt, die für das Verständnis der eigenen Implementierung, die in den Kap. 4, 5 und 6 folgt, notwendig sind. UPnP definiert zwei grundlegende Typen von Komponenten: Das Gerät (engl. Device) und die Steuerinstanz (engl. Control Point). Geräte beinhalten null oder mehrere Dienste (engl. Services), die wiederum null oder mehrere Aktionen (engl. Actions) und Statusvariablen (engl. State Variables) beinhalten. Ein Gerät kann außerdem rekursiv null oder mehrere Sub-Geräte mit jeweils wieder den selben Regeln beherbergen. Die genaue Spezifikation eines Gerätes wird in einer XML-Datei beschrieben. Während Geräte und Dienste lediglich eine Containerfunktion besitzen, liegt die wahre Funktionalität von UPnP-Geräten in den Aktionen und Statusvariablen. Aktionen entsprechen Funktionen, die über XML-RPCs ausgeführt werden. Statusvariablen gehen den umgekehrten Weg und informieren von sich aus interessierte Netzwerkteilnehmer über Änderungen von Stati von Diensten. Das Ausführen von Aktionen und Empfangen von Informationen der Statusvariablen obliegt den Steuerinstanzen. Es ist auch möglich, dass eine Netzwerkkomponente sowohl Gerät als auch Steuerinstanz ist. Eine interessante Eigenschaft von UPnP ist weiters, dass es keiner Registrierungsstelle für Geräte bedarf, wie es etwa bei CORBA, Java RMI und auch Jini notwendig ist. Stattdessen geben sich in ein Netzwerk eingebrachte UPnP-Geräte mittels einer spezifizierten Nachricht selbstständig zu erkennen. Steuerinstanzen sind in der Lage diese Nachricht zu empfangen und können aufgrund der XML-Beschreibungsdatei des Gerätes die Funktionalitäten des Gerätes auslesen und etwa in einer grafischen Benutzeroberfläche präsentieren. Kapitel 3 Heterogene Systeme Um heterogene Systeme besser verstehen zu können, sei das Open Systems Interconnection (OSI) Referenzmodell ins Gedächtnis gerufen. Es teilt die Kommunikation in einem Rechnernetz in sieben Schichten auf, die jeweils bestimmte Aufgaben erfüllen, wobei die Schicht j auf dem Rechner (auch Host) k mit der Schicht j auf dem Rechner l kommuniziert. Innerhalb eines Rechners kommuniziert die Schicht j über spezifizierte Schnittstellen mit den angrenzenden Schichten j − 1 und j + 1 [3]. Abb. 3.1 zeigt die sieben Schichten des OSI-Referenzmodells. Die unterste Schicht definiert das Protokoll des physikalischen Mediums. Hier wird bestimmt, ob als Übertragungsmedium elektrische Signale, Licht, Radiowellen o. ä. genutzt werden und wie digitale Signale auf diesem Übertragungskanal abgebildet werden. Die höherliegenden Schichten übernehmen dann Aufgaben wie das Paketieren (Sicherungsschicht), das Vermitteln (Vermitt- Abbildung 3.1: Das OSI-Referenzmodell (nach [37]). 13 KAPITEL 3. HETEROGENE SYSTEME 14 lungsschicht) oder das Transportieren (Transportschicht) der Daten [10]. Der Vorteil dieses Modells ist, dass eine Schicht ausgetauscht werden kann, wenn sie die selben Schnittstellen nach oben und unten besitzt und die selbe Funktionalität zur Verfügung stellt, ohne dass die anderen Schichten davon erfahren müssen oder gar an die neue Situation angepasst werden müssten. Damit die Kommunikation erfolgreich sein kann, muss die ersetzende Schicht das selbe Protokoll sprechen, wie die entsprechende Schicht auf dem entfernten Rechner. Die Austauschbarkeit der Schichten ist jedoch nicht nur äußerst komfortabel, sondern bringt auch Probleme mit sich, die auftauchen, wenn zwei oder mehrere Netze miteinander verbunden werden sollen, die sich in einer oder mehrerer Schichten unterscheiden. Den Zusammenschluss verschiedener Netze nennt man Netzverbund oder Verbundnetz (engl. Internetwork) [37]. Zwischen den zusammengeschlossenen Netzen müssen Übersetzer“ geschal” ten werden, die die notwendigen Konvertierungen zwischen den Protokollen vornehmen. Abhängig von der Schicht, auf der ein solcher Übersetzer wirkt, heißt er z. B. Repeater (Schicht 1), Bridge (Schicht 2) oder Router (Schicht 3). Für höhere Schichten hat sich der Begriff Gateway eingebürgert [37], der im weiteren Verlauf auch für die Schichten 1 bis 3 verwendet wird. Gateways werden jedoch nicht nur verwendet, um inkompatible Netze miteinander, sondern auch um gleichartige Netze untereinander zu einem größeren Netz zu verbinden. Die Gateways werden dann zu Verkehrsknotenpunkten, die je nach Typ entweder zur Optimierung der Datenübertragung beitragen können, oder empfangene Daten unbetrachtet zwischen Netzen hin- und herkopieren [14]. Abb. 3.2 zeigt ein populäres Beispiel für ein Verbundnetzwerk: Ein Ether1 net -basiertes Local Area Network (LAN) und ein Wireless LAN 2 (WLAN) werden über eine Wireless Bridge als Gateway miteinander verbunden. Daten, die auf der Ethernet-Seite eintreffen werden von der Bridge in WLANkompatible Pakete konvertiert und über eine Antenne ins drahtlosen Netzwerk eingebracht und umgekehrt. Ein Gerät auf der einen Seite kann mit einem Gerät auf der anderen Seite kommunizieren, ohne wissen zu müssen, dass eine Konvertierung vorgenommen wird. Lediglich die Schichten 1 und 2 sind von der Konvertierung betroffen. 3.1 Arten von Heterogenität Verbundnetze können in der Ausprägung der Heterogenität sehr unterschiedlich sein. Manche Unterschiede sind einfacher zu überbrücken, andere schwieriger. Jedenfalls gibt es eine große Zahl an Merkmalen, in denen sich die Teilnetze eines Netzverbundes unterscheiden können. Zur Verdeutlichung 1 2 Das weitverbreitetste Protokoll für verdrahtete LANs. Der Überbegriff für drahtlose LANs. KAPITEL 3. HETEROGENE SYSTEME 15 Abbildung 3.2: Ein Ethernet-LAN und ein WLAN werden über eine Wireless Bridge als Gateway zu einem Verbundnetz verknüpft. Das Gateway nimmt die notwendigen Konvertierungen auf den Schichten 1 und 2 des OSIReferenzmodells vor und sorgt für eine transparente Verbindung zwischen den beiden Netzen. Tabelle 3.1: Auswahl an Merkmalen der Vermittlungschicht, durch die sich Rechnernetze unterscheiden können (nach [37]). Merkmal Angebotener Dienst Adressierung Multicasting Paketgröße Dienstqualität Fehlerbehandlung Flusskontrolle Sicherheit Parameter Abrechnung, Kostenerfassung Mögliche Varianten Verbindungsorientiert oder verbindungslos Flach oder hierarchisch Vorhanden oder nicht (auch Broadcasting) Jedes Netz hat sein spezifisches Maximum Vorhanden oder nicht; viele verschiedene Klassen und Arten Zuverlässig, geordnet oder ungeordnet Schiebefenster, Ratensteuerung, andere oder keine Datenschutzregeln, Verschlüsselung usw. Verschiedene Timeouts, Flussspezifikationen usw. Nach Verbindungszeit, Paket, Byte oder keine soll Tab. 3.1 dienen, die lediglich eine Auswahl möglicher Unterscheidungsmerkmale in der Vermittlungsschicht (Schicht 3) des OSI-Referenzmodells darstellt. Es gibt genügend Gründe, warum überhaupt unterschiedliche Netze miteinander verbunden werden müssen, und nicht nur ein Netzwerktyp verwendet wird. Ein einleuchtender Grundsatz besagt, dass es keine bestimmte Netzwerktechnologie gibt, die sich für alle Bedürfnisse eignet [3]. Jede Technologie hat ihre spezifischen Eigenschaften, und der Einsatz leitet sich aus den Bedürfnissen der jeweiligen Situation ab. Je ähnlicher die Eigenschaften der Netze sind, desto einfacher ist die Realisierung des Verbundnetzes, da die Konvertierungsarbeit und in weiterer Folge auch die Fehleranfälligkeit der Gateways gering ist. KAPITEL 3. HETEROGENE SYSTEME 16 Abbildung 3.3: Zwei Halb-Gateways verbinden zwei Netze über ein neutrales Protokoll miteinander. Dass Verbundnetze aus heterogenen Teilnetzen nicht zu vermeiden sind und in vielen Fällen die optimale Lösung bieten, ist einleuchtend. Auf die Frage, welche Möglichkeiten es gibt, ein brauchbares Verbundnetz herzustellen, soll der folgende Abschnitt eingehen. 3.2 Methoden der Homogenisierung Der Grundaufbau ist immer der selbe: Mehrere Netze werden über Gateways miteinander verbunden, die den Datenaustausch über mehrere eventuell heterogene Netze ermöglichen. Ein Gateway kann dabei auch mehr als zwei Netze miteinander verbinden und auf mehr als nur einer Schicht des OSIReferenzmodells wirken. Ein Gateway kann wiederum, falls dies notwendig ist, in zwei HalbGateways geteilt werden. Ein Halb-Gateway übersetzt von einem Protokoll in ein neutrales anderes Protokoll, von welchem ein weiteres Halb-Gateway in ein drittes Protokoll übersetzt (s. Abb. 3.3). Ein Fall, wo das gewünscht sein kann, ist, wenn zwei Netze von unterschiedlichen Firmen miteinander verbunden werden sollen. In diesem Fall können sich die Firmen auf ein neutrales Protokoll einigen, in das eingehende Daten konvertiert werden, um von einem Halb-Gateway zum anderen transportiert zu werden [37]. Nach [37] ist für die interne Datenverarbeitung eines Verbundnetzes vor allem der Aspekt ausschlaggebend, ob es verbindungsorientiert arbeitet oder nicht, dementsprechend werden zwei Arten von Netzzusammenschlüssen definiert. In verbindungsorientierten Verbundnetzen wird für eine virtuelle Verbindung zwischen zwei Endpunkten eine Kette von Verbindungen aufgebaut, die jeweils zwei Gateways miteinander verbinden. Die betroffenen Gateways speichern die virtuellen Verbindungen in Tabellen ab und sorgen dafür, dass Pakete einer virtuellen Verbindung immer den selben Pfad zurücklegen, und dass die Reihenfolge der Pakete eingehalten wird. Ein verbindungsloser Netzverbund hingegen gibt weder eine Garantie über die Reihenfolge der Pakete noch über die Persistenz des internen Routings ab. Verschiedene Pakete, die von Host A zu Host B gelangen sollen, können verschiedene Wege gehen. Das erlaubt es den Gateways Bandbreiten- KAPITEL 3. HETEROGENE SYSTEME 17 Optimierung zu betreiben, da bei Überlastung einer Route auf eine andere ausgewichen werden kann. Die möglicherweise dadurch erreichte Bandbreitenerhöhung ist jedoch nicht kostenlos, sondern hat den Nachteil, dass Pakete einander überholen können und somit die Reihenfolge der Pakete nicht sichergestellt ist. Ist die Reihenfolge aber wichtig, so muss ein Protokoll einer höheren Schicht die Reihenfolge wieder herstellen. Abb. 3.4 illustriert diese beiden Arten von Verbundnetzen. Dabei ist zu beachten, dass das verbindungsorientierte Verbundnetz nur dann funktionieren kann, wenn alle Teilnetze ebenfalls verbindungsorientierte Protokolle zur Verfügung stellen. Bietet ein entstandenes Verbundnetz jedem Endpunkt die Möglichkeit Pakete an jeden anderen Endpunkt zu schicken, spricht man vom Vorhandensein eines Universaldienstes (engl. Universal Service). Ein heterogenes Verbundnetz, das durch die Kombination von Hard- und Software einen Universaldienst anbietet nennt man auch Internet. Dem Benutzer und den Anwendungsprogrammen wird die tatsächliche physische Netzstruktur komplett abstrahiert. 3.3 OSGi OSGi definiert eine standardisierte, komponentenbasierte Umgebung für vernetzte Dienste [21]. Das spezifizierende Gremium ist die OSGi Alliance, ein Zusammenschluss mehrerer Firmen und Organisationen, mit prominenten Mitgliedern wie BMW3 , Deutsche Telekom4 , IBM5 , Intel6 , Nokia7 , Siemens8 , Sun u. a. [24]. Die Kernkomponente, der für die Programmiersprache Java konzipierten OSGi-Dienstplattform (Service Platform), ist das OSGiFramework. Ursprünglich wurde es als Internet-Gateway für Anwendungen im Bereich der Home Automation entwickelt. Über ein OSGi-Framework kann einerseits den Anwendungen der Zugriff auf das Internet und andererseits die Steuerung der Geräte des Haushalts von externen Standorten ermöglicht werden. Zentraler Begriff ist der Dienst (engl. Service). Über auch nachträglich installierbare Software-Komponenten werden im Framework Dienste registriert, die von anderen Komponenten genutzt werden können. Zum Beispiel könnte eine Anwendung, die Nachrichten an verschiedene Teilnehmer schickt, auf einen SMS9 -Dienst oder einen Email-Dienst zurückgreifen, um Nachrichten an registrierte Teilnehmer zu schicken. Die Software, die den 3 http://www.bmw.com/ http://www.telekom3.de/ 5 http://www.ibm.com/ 6 http://www.intel.com/ 7 http://www.nokia.com/ 8 http://www.siemens.com/ 9 Short Message Service – Standardprotokoll zum Versenden von Textnachrichten auf Mobiltelefone. 4 KAPITEL 3. HETEROGENE SYSTEME Abbildung 3.4: (a) zeigt einen Netzverbund mit virtuellen verketteten Verbindungen. Einzelne Pakete zwischen den Endpunkten A und B werden immer über den selben Pfad transportiert. Für die beiden Endpunkte erweckt das Verbundnetz den Anschein einer persistenten Verbindung. (b) visualisiert einen verbindungslosen Netzverbund, in dem Datenpakete nach, für die Endpunkte, nicht vorhersehbaren Kriterien verschiedene Wege zurücklegen können. Pakete auf schnelleren Routen kommen daher eventuell früher an als Pakete auf langsameren Routen – die Reihenfolge der Pakete kann sich ändern. 18 KAPITEL 3. HETEROGENE SYSTEME 19 Abbildung 3.5: Ein OSGi-Framework aus Sicht der Verbundnetz-Thematik. Es ist ein zur Laufzeit erweiterbarer Container für Halb-Gateways, die als neutrales Protokoll Java-Objekte verwenden. In diesem Fall wird einer Benachrichtigungs-Software das Versenden von Nachrichten über SMS oder Email ermöglicht. Die tatsächliche Kommunikation mit einem SMSoder Email-Server wird vom jeweiligen Bundle abstrahiert. SMS-Dienst registriert, könnte von einem Mobilfunkbetreiber zur Verfügung gestellt werden. Man könnte ein OSGi-Framework somit auch als Container für Halb-Gateways sehen, deren neutrales Protokoll die Programmiersprache Java ist (s. Abb. 3.5). Das OSGi-Framework als Internet-Gateway war zwar das Ziel der ersten Version der OSGi-Spezifikation [20], im Laufe der letzen Jahre hat es sich allerdings auch für andere Bereiche als einsetzbar erwiesen [21]. Seit der ersten Version (Mai 2000) wurde die Spezifikation erweitert, um vor allem den Anforderungen der Automobilbranche und mobiler Geräte gerecht zu werden, für die eigene Expertengruppen (engl. Expert Groups) eingerichtet wurden: die Vehicle Expert Group 10 und die Mobile Expert Group 11 . Die aktuelle Version ist die vierte und wurde im Oktober 2005 verabschiedet. Sie heißt OSGi Service Platform, Release 4 CORE“ und teilt sich ” in zwei Teile: • CORE, veröffentlich in [22], beschreibt das Framework selbst und spezifiziert die Kernkomponenten. • COMPENDIUM beinhaltet die Spezifikationen nützlicher Dienste wie Protokollierung, Benutzeradministration und XML-Verarbeitung und wurde in [23] herausgegeben. 3.3.1 Das OSGi-Framework Ein OSGi-Framework ist eine Laufzeitumgebung für erweiterbare, herunterladbare Applikationen auf Basis der Java Laufzeitumgebung. Eine in einem 10 11 http://osgi.org/about/charter veg.asp?section=1 http://osgi.org/about/charter meg.asp?section=1 KAPITEL 3. HETEROGENE SYSTEME 20 OSGi-Framework installierte Anwendung heißt Bundle und ist ein Java Archive (JAR)12 mit erweitertem JAR-Manifest (s. Abs. 3.3.2). Der Clou dabei ist, dass eigenständige Applikationen im OSGi-Framework unabhängig voneinander laufen, aber bei Bedarf direkt über Objekt-Instanzen miteinander kommunizieren können. Es muss somit nur eine Java Virtual Machine 13 (JVM) gestartet werden, um mehrere Java-Applikationen laufen zu lassen, und die Kommunikation zwischen den Applikationen kann direkt erfolgen, ohne den Umweg über Protokolle wie CORBA oder RMI gehen zu müssen. OSGi macht sich dabei vor allem zwei Eigenschaften von Java zu Nutze: Das dynamische Laden von ausführbarem Code zur Laufzeit und das Konzept der multiplen Class Loader. Erstere erlaubt das Installieren von Bundles auch zur Laufzeit des Frameworks. Zweitere ermöglicht es, dass jedes Bundle seine Klassen mit einem eigenen Class Loader lädt, was dazu führt, dass die einzelnen Bundles komplett voneinander abgeschottet arbeiten können. Jedes Bundle kann jedoch Instanzen beliebiger Klassen im Framework registrieren, auf die dann auch von anderen Bundles direkt zugegriffen werden kann. Zur Laufzeit des Frameworks können Bundles installiert, deinstalliert, aktualisiert (z. B. zum Laden einer neuen Version), aufgefrischt (Löschen der temporär angelegten Dateien), gestartet und gestoppt werden – diese Eigenschaft entspricht dem Component Configurator Pattern, das in [30] beschrieben wird. Dadurch eignet sich OSGi hervorragend für Applikationen, die Plugins (in Form von Bundles) unterstützen wollen. Die Plugins werden als JAR-Dateien installiert und sind sofort verfügbar – die OSGi-basierte Applikation muss nicht neu gestartet werden. Ist eine neue Version eines Plugins erhältlich, kann diese jederzeit, auch zur Laufzeit der Applikation, die alte ersetzen oder parallel zur alten Version installiert werden. 3.3.2 Das OSGi-Bundle Ein Bundle ist eine als JAR-Datei vertriebene Applikation oder SoftwareBibliothek. Damit ein OSGi-Framework weiß, wie es mit einem Bundle umzugehen hat, sind einige Meta-Angaben in der Manifest-Datei14 des JARs notwendig. Die wichtigsten auf diese Art einstellbaren Eigenschaften sollen kurz erläutert werden. Eine vollständige Liste befindet sich in [22]: Bundle-Activator definiert den Einstiegspunkt für das Bundle, wenn es sich um eine Applikation handelt. Ist das Bundle eine Bibliothek, muss dieser Eintrag nicht angegeben werden. Die angegebene Klasse 12 Ein JAR ist ein ZIP-basiertes komprimiertes oder unkomprimiertes Archiv, kann kompilierte Klassen und Ressourcen beinhalten und eine eigenständige Applikation oder eine Software-Bibliothek darstellen. 13 Eine Instanz einer Java Laufzeitumgebung 14 Das Manifest ist die Datei MANIFEST.MF im Verzeichnis META-INF der JAR-Datei. KAPITEL 3. HETEROGENE SYSTEME 21 muss das Interface org.osgi.framework.BundleActivator implementieren, wo die Methode start definiert wird, die der main Methode in gewöhnlichen Applikationen entspricht. Beispiel: Bundle-Activator: net.bebedizo.foo.Activator Export-Package gibt an, welche Java-Packages für andere Bundles sichtbar sein sollen. Alle nicht hier angeführten Packages und die darin enthaltenen Klassen sind für andere Bundles unsichtbar. Jedes Package kann (und soll) seine eigene Versionsnummer haben, die unabhängig von der Version des Bundles ist. Einzelne Packages werden durch Beistriche getrennt. Beispiel: Export-Package: net.bebedizo.foo;version=2.3.1,net .bebedizo.bar;version=0.3.2 Import-Package ist die Möglichkeit eines Bundles zu definieren, welche Packages, die von anderen Bundles exportiert wurden, für die Funktionalität dieses Bundles notwendig sind. Beispiel: Import-Package: net.bebedizo.foo;version=2.3.1,net .bebedizo.bar Das vollständige Manifest des lauffähigen Bundles Example-Bundle“, ” das ein Package importiert, eines exportiert und von zwei anderen Bundles explizit abhängt, sieht folgendermaßen aus: Manifest-Version : 1.0 Bundle-ManifestVersion : 2 Bundle-Name : Example-Bundle Bundle-SymbolicName : net . bebedizo . osgi . example Bundle-Version : 1.3.12 Bundle-Activator : net . bebedizo . osgi . example . Activator Bundle-Vendor : Bernhard Wally Import-Package : org . osgi . framework ; version =1.3.0 Require-Bundle : org . eclipse . osgi . services , org . eclipse . osgi Export-Package : net . bebedizo . osgi . example . share ; version =1.1.0 3.3.3 Dienste Wie eingangs erwähnt, dreht sich bei OSGi alles um Dienste. Ein Dienst ist nichts anderes als ein Java-Objekt, das im Framework registriert wird. Für gewöhnlich wird man zunächst ein Interface, das die Methoden des Dienstes beschreibt, definieren und anschließend eine Implementierung des Interfaces im Framework registrieren. Die folgenden Code-Fragmente beschreiben die Schritte, die notwendig sind, um einen Dienst zu definieren, danach im Framework zu registrieren und schließlich zu nutzen. KAPITEL 3. HETEROGENE SYSTEME 22 Zunächst wird ein Interface (s. Lst. 3.1) und seine Implementierung (s. Lst. 3.2) definiert. In diesem Fall ein Service, das nur eine Methode beinhaltet, die Hello“ auf die Konsole ausgibt. ” Listing 3.1: Das HelloService Interface. package net . bebedizo . foo ; public interface HelloService { public void sayHello () ; } Listing 3.2: Die HelloService Implementierung. package net . bebedizo . foo ; public class HelloServiceImpl implements HelloService { public void sayHello () { System . out . println ( " Hello " ) ; } } Um nun ein HelloServiceImpl-Objekt im OSGi-Framework zu registrieren, dient die Methode registerService, die im für Bundles zentralen Interface org.osgi.framework.BundleContext definiert wird. Ein Objekt vom Typ BundleContext wird jedem Bundle bei dessen Start zur Verfügung gestellt. Der Rückgabewert der Methode registerService ist ein Objekt vom Typ org.osgi.framework.ServiceRegistration, das später zum Austragen des Dienstes aus dem OSGi-Framework verwendet werden kann (s. Lst. 3.3). Listing 3.3: Registrierung des HelloService im OSGi-Framework beim Start des Bundles. package net . bebedizo . foo ; // ... private ServiceRegistration registration ; public void start ( BundleContext bundleContext ) throws Exception { registration = bundleContext . registerService ( HelloService . class . getName () , new HelloServiceImpl () , null ) ; } // ... KAPITEL 3. HETEROGENE SYSTEME 23 Ab diesem Zeitpunk steht das HelloService anderen Bundles zur Verfügung. Um die Registrierung zu einem späteren Zeitpunkt (z. B. wenn das Bundle gestoppt wird) wieder zu annullieren, kann die Methode unregister des ServiceRegistration-Objekts aufgerufen werden (s. Lst. 3.4). Listing 3.4: Austragen des HelloService aus dem OSGi-Framework. package net . bebedizo . foo ; // ... public void stop ( BundleContext bundleContext ) throws Exception { registration . unregister () ; } // ... Wie in Lst. 3.3 ersichtlich, wird der Dienst unter dem Namen InterfaceKlasse, HelloService, registriert. Um diesen Dienst, also genauer gesagt das HelloServiceImpl-Objekt, das registriert wurde, von einem anderen Bundle aus nutzen zu können, muss es vom OSGi-Framework angefordert werden. Die notwendigen Schritte werden in Lst. 3.5 dargestellt. Listing 3.5: Nutzen des registrierten HelloService von einem anderen Bundle aus. package net . bebedizo . bar ; import net . bebedizo . foo . HelloService ; // ... public void start ( BundleContext bundleContext ) throws Exception { HelloService helloService = bundleContext . getService ( bundleContext . getServiceReference ( HelloService . class . getName () ) ) ; helloService . sayHello () ; } // ... Die Methoden registerService und getService sind also für den Austausch von Objekten zwischen verschiedenen Bundles zuständig. Mit diesem Mechanismus kann ein Bundle Schnittstellen definieren, die in einem Interface gesammelt werden. Andere Bundles beantragen vom OSGi-Framework eine Instanz vom Typ des Interfaces und können die darin definierten Methoden nutzen. KAPITEL 3. HETEROGENE SYSTEME 3.3.4 24 OSGi-Implementierungen Die OSGi-Spezifikation definiert lediglich eine Sammlung an Interfaces – lauffähige Versionen müssen daher selbst implementiert werden. Glücklicherweise kann mittlerweile aus einer Menge an Implementierungen gewählt werden – es gibt sowohl kommerzielle, als auch frei verfügbare. Zu den kommerziellen Produkten, die teilweise auch von der OSGi Alliance zertifiziert wurden, gehören jene von Atinav Inc.15 , Connected Systems16 , Gatespace Telematics AB17 , IBM und ProSyst Software18 [25]. Die drei wichtigsten frei verfügbaren Implementierungen sind: Knopflerfish 19 , Equinox 20 und Felix 21 (ehemals Oscar OSGi22 ). Felix ist eine eigenständige Implementierung ohne Verbindungen zu anderen Projekten, Knopflerfish stellt große Teile der Code-Basis für Ubiserv23 , das kommerzielle OSGi-Framework von Gatespace Telematics AB und Equinox bildet die Grundlage für die Java-Entwicklungsumgebung Eclipse24 und wurde von der OSGi-Alliance als Referenz-Implementierung der aktuellen Version, Release 4, definiert [6]. 15 http://www.atinav.com/ http://www.connectedsys.com/ 17 http://www.gatespacetelematics.com/ 18 http://www.prosyst.com/ 19 http://www.knopflerfish.org/ 20 http://www.eclipse.org/equinox/ 21 http://incubator.apache.org/felix/ 22 http://oscar.objectweb.org/ 23 http://www.gatespacetelematics.com/products/ubiserv osgi.shtml 24 http://www.eclipse.org/ 16 Kapitel 4 Anforderungen Die Anforderungen an den zu entwickelnden Prototyp werden zunächst grob definiert und anschließend nach den Regeln der Model-View-Control (MVC) Architektur verfeinert. MVC ist ein Programmier-Entwurfsmuster, bei dem die Darstellung (View), die Steuerung (Control) und die darzustellenden Daten selbst (Model) als drei verschiedene Komponenten gesehen werden, was die Modularisierung des Programm-Codes erleichtert [8]. Durch genau spezifizierten Schnittstellen zwischen diesen drei Komponenten ist es auch möglich, jede der Komponenten durch eine entsprechend passende andere zu ersetzen. Das erlaubt z. B. das Definieren verschiedener Darstellungsformen für ein und dasselbe Datenmodell. Der Prototyp soll eine Applikation mit grafischer Benutzeroberfläche (engl. Graphical User Interface, kurz GUI) sein, die es ermöglicht, ursprünglich unabhängige, verteilte Geräte miteinander in Bezug zu setzen. Verteilte Geräte bieten Schnittstellen an, über die gewisse Funktionalitäten des Gerätes genutzt werden können. Zum Beispiel kann ein Gerät, das den Zugriff auf ein Email-Postfach ermöglicht, Funktionen zum Lesen empfangener oder zum Versenden erstellter Emails anbieten. Ein anderes Gerät könnte einen Bewegungsmelder beinhalten und darüber informieren, wenn Bewegung detektiert würde. Diese beiden Geräte könnten zu einer Anwendung verschmolzen werden, in der Benachrichtigungs-Emails versandt werden, sobald eine Bewegung registriert wird. Das GUI soll die Geräte als Blöcke darstellen und dem Benutzer die Möglichkeit geben, eine Beziehung herzustellen, indem eine gerichtete Verbindungslinie gezeichnet wird (s. Abb. 4.1). Dieses Verhalten lässt sich leicht in eine MVC-Architektur abbilden: Die Geräte und die Verbindung bilden das Model, sie sind die Akteure, die gesteuert und dargestellt werden sollen. Die View ist das GUI, das die Daten repräsentiert und z. B. durch Mausinteraktion die Definition der Verbindungen ermöglicht. Sie leitet den Wunsch des Benutzers, der die Verbindung definiert hat, an die Control weiter, die für den tatsächlichen Verbindungsaufbau zwischen den Geräten sorgt. 25 KAPITEL 4. ANFORDERUNGEN 26 Abbildung 4.1: Die beiden Geräte werden durch die gerichtete Verbindungslinie miteinander in Bezug gesetzt. Das Verwaltungs-Programm, das diese Verbindung ermöglicht hat, muss sie in weiterer Folge umsetzen. Sobald Bewegung registriert wird, soll ein Email mit dem Text Einbruch?“ ” erstellt und versandt werden. Abbildung 4.2: Die Funktion, die die Schnittstelle Email versenden“ ” repräsentiert, benötigt zwei Eingangsparameter (links): die EmpfängerEmail-Adresse und die zu versendende Nachricht. Als Ausgangsparameter (rechts) wird ein boolscher Wert zurückgegeben: War der Versand erfolgreich? 4.1 Model Abb. 4.1 lässt jedoch noch einige Fragen offen, denn an wen wird das Email gesandt? Und wird, wenn der Bewegungsmelder zehn mal pro Sekunde eine Bewegung registriert, jedesmal ein Email generiert? Um diese Fragen zu beantworten, müssen zunächst die Schnittstellen der Geräte genauer definiert werden. 4.1.1 Komponenten Der Zugriff auf verteilte Geräte funktioniert über entfernte Funktions- oder Methodenaufrufe, wie in Abs. 2.1 erläutert. Jede Schnittstelle eines verteilten Gerätes kann somit als Funktionsaufruf betrachtet werden, der eine gewisse Anzahl an Eingangsparametern benötigt und als Ergebnis bestimmte Ausgangsparameter liefert (s. Abb. 4.2). Die Zahl der Parameter kann dabei auch null betragen. Anders ausgedrückt definiert dieser Funktionsblock drei Ports: Zwei Eingangs-Ports (engl. Input Ports), die Daten (in Form von Zeichenketten) annehmen und einen Ausgangs-Port (engl. Output Port), der Daten (boolsche Werte) zur Verfügung stellt. Nachdem sie mit Daten operieren, werden sie als Daten-Input-Ports (DIPs) und Daten-Output-Port (DOP) bezeichnet. Wird der Wert eines Eingangsparameters gesetzt, so kann gleichbedeutend gesagt werden: Dem DIP wird ein Wert zugewiesen“. Die Situation des ” Ausgangsparameters wird als der DOP stellt einen Wert zur Verfügung“ ” bezeichnet. KAPITEL 4. ANFORDERUNGEN 27 Abbildung 4.3: Diese Komponente bietet die Möglichkeit den Zeitpunkt des Funktionsaufrufes genau zu bestimmen: Wenn der rechteckig gekennzeichnete Funktionsaufruf-Port aktiviert wird. Daten-Ports werden zur besseren Unterscheidung weiterhin als Dreiecke dargestellt. Dieses Konstrukt – der Funktionsblock mit mehreren Ein- und Ausgangsparametern – wird in weiterer Folge als Komponente bezeichnet. Wird die von einer Komponente repräsentierte Funktionalität (in diesem Beispiel das Versenden des Emails) ausgeführt, so wird das Aktivierung der Komponente genannt. Nun stellt sich die Frage, wann die Sende-Funktion eigentlich aufgerufen wird. Der Aufruf könnte erfolgen, sobald einer der beiden Eingangsparameter definiert wurde, oder nur dann, wenn der Nachrichten-Parameter eine neue Zeichenkette erhalten hat. Beide Möglichkeiten sind aber nicht besonders flexibel, es muss daher eine andere Möglichkeit gefunden werden. Eine einfache Lösung ist es, den Zeitpunkt der Ausführung durch einen dritten Input-Port zu steuern. Dieser kann keine Daten annehmen, sondern wird aktiviert. Durch ihn können den DIPs beliebig oft Werte zugewiesen werden; die Funktion wird erst ausgeführt, sobald dieser spezielle Port aktiviert wird (s. Abb. 4.3). Es ist nun theoretisch möglich die Input-Ports der Komponente zu setzen oder zu aktivieren, doch woher kommen die Daten bzw. der Auslöser für den Funktionsaufruf? Der Benutzer könnte die Daten für die DIPs per Tastatur eingeben und mit einem Mausklick das Aktivieren einer Komponente verlangen. Diese Methode ist zwar praktisch, und dem Benutzer sollte auf jeden Fall die Möglichkeit gegeben werden manuell einzugreifen, jedoch wird auf diese Weise das Szenario mit dem Bewegungsmelder nicht verwirklicht werden können, da in diesem ein gewisser Automatismus verlangt wird. Das Aktivieren von Komponenten sollte vielmehr von anderen Komponenten aus ermöglicht werden. Schaltet man mehrere Komponenten hintereinander ergibt sich ein Programmfluss, der den sequentiellen Aufruf mehrerer Funktionen darstellt. Die Funktionsaufruf-Input-Ports werden deswegen auch Programmfluss-Input-Ports (PIP) genannt. Damit eine Komponente, die nach einer anderen geschaltet ist auch wirklich erst nach der vorhergehenden aktiviert wird, ist es sinnvoll, wenn jede Komponente neben dem PIP auch einen Programmfluss-Output-Port (POP) definiert (s. Abb. 4.4), um anzuzeigen, dass eine Funktion ausgeführt wurde. Doch nicht nur die PIPs sollten von anderen Komponenten aus (über deren POPs) aktiviert werden können. Auch die DIPs sollten Werte von DOPs KAPITEL 4. ANFORDERUNGEN 28 Abbildung 4.4: (a) Eine flexibel einsetzbare Komponente mit drei InputPorts (links) und zwei Output-Ports (rechts). Der mit Funktionsaufruf“ ge” kennzeichnete Port stellt den PIP dar, der mit Funktion ausgeführt“ be” zeichnete Port ist der POP. (b) zeigt die innere Funktionsweise dieser Komponente: Von außen nicht ersichtlich, wird zunächst aus Empfängeradresse und Nachricht ein Email erstellt, dann eine Netzwerkverbindung zu einem Email-Server aufgebaut und das Email verschickt. Die Antwort des Servers wird in einen boolschen Wert umgewandelt und an den DOP weitergeleitet. anderer Komponenten zugewiesen bekommen können, um Ausgangsparameter einer Funktion als Eingangsparameter einer anderen nutzen zu können. Zur Verdeutlichung soll Abb. 4.5 dienen, die zwei Komponenten über deren Ports miteinander koppelt. Kopplungen werden in Abs. 4.1.2 genauer spezifiziert. Eine Komponente durchläuft zusammenfassend folgende Stadien: Zunächst ist sie ein Funktionsblock mit Wert-losen DIPs, die im nächsten Schritt Werte zugewiesen bekommen. Danach kann die Komponente über den PIP aktiviert werden (manuell durch den Benutzer oder durch eine vorgeschaltete Komponente), was das Ausführen der dahinterliegenden Funktion, mit den aktuellen Werten der DIPs, bedeutet. Ist die Funktion ausgeführt, stellen die DOPs die Rückgabewerte der Funktion zur Verfügung und danach wird der POP aktiviert, um zu signalisieren, dass die Funktion ausgeführt wurde. Man kann eine Komponente als Blackbox mit einer Menge an Input- und Output-Ports sehen, mit der ausschließlich über die Ports interagiert wird. Sie kann intern beliebig aussehen und entfernte Funktionsaufrufe ebenso durchführen wie simple Funktionen (etwa eine Addition). KAPITEL 4. ANFORDERUNGEN 29 Abbildung 4.5: (a) zeigt eine neue Komponente, deren Funktionalität das Protokollieren von Daten ist (z. B. in eine Log-Datei). (b) stellt die Kopplung der Email-Komponente mit dieser dar. Der Ausgangsparameter der Email-Funktion wird als Eingangsparameter für die Protokollier-Funktion verwendet. Ist die Email-Funktion beendet und der Parameter über die Daten-Kopplung übertragen, wird die Protokollier-Komponente über die Programmfluss-Kopplung aktiviert. Erweiterte Komponenten Die oben vorgestellte Protokollier-Komponente ist ein relativ simpler Fall: Sie repräsentiert eine isolierte Funktion. Isoliert bedeutet in diesem Zusammhang, dass die Funktion an keinen Kontext gebunden ist, wie dies etwa bei Methoden von Objekten der Fall ist. Methoden stehen immer im Kontext des Objekt zu dem sie gehören. Die Protokollier-Funktion kann einfach aufgerufen werden, ohne Genaueres wissen zu müssen, schließlich wird der übergebene Wert einfach in eine Log-Datei geschrieben. Bei Kontext-behafteten Funktionen kann es angebracht sein, Funktionen des gleichen Kontextes in einer einzigen Komponente zu repräsentieren. Als Beispiel soll ein Array-Objekt (s. Lst. 4.1) dienen, mit dem über drei Methoden kommuniziert werden kann: • eine zum Definieren der Elemente, • eine zum Auslesen eines bestimmten Elements und • eine zum Abfragen der Größe des Arrays. Es ist grundsätzlich kein Problem diese drei Methoden als drei unterschiedliche Komponenten darzustellen (s. Abb. 4.6) und isoliert zu verwenden. Kompliziert wird es jedoch, wenn mehrere Array-Instanzen unterstützt werden sollen – die drei Methoden müssen jeweils einer Instanz des ArrayObjekts zugewiesen werden. Diese Semantik könnte durch eine zusammengesetzte Komponente realisiert werden, die mehrere Funktionen (oder in diesem Fall Methoden) vereint. Abb. 4.7 stellt eine solche Komponente dar. KAPITEL 4. ANFORDERUNGEN Listing 4.1: Die Array-Klasse in Java-Syntax. public class Array { private Object [] elements ; /* * Entspricht " Array setzen ". */ public void setArray ( Object [] elements ) { this . elements = elements ; } /* * Entspricht " Größe abfragen ". */ public int getSize () { return elements . length ; } /* * Entspricht " Element auslesen ". */ public Object getElement ( int index ) { return elements [ index ]; } } Abbildung 4.6: Die drei Methoden des Array-Objekts als isolierte Komponenten. 30 KAPITEL 4. ANFORDERUNGEN 31 Abbildung 4.7: Die drei Methoden des Array-Objekts in einer zusammengesetzten Komponente. Abbildung 4.8: Eine if-Anweisung als Komponente dargestellt. Wird der PIP ( Wenn“) aktiviert, so wird geprüft, ob der Zu prüfende Wert“ WAHR ” ” oder FALSCH ist. Ist er WAHR, so wird der obere POP ( Dann“), ansonsten der ” untere ( Sonst“) aktiviert. Ob gekoppelte Komponenten aktiviert werden, ” hängt davon ab, mit welchem POP sie verbunden sind. Auf ähnliche Weise lassen sich auch weitere Sprachelemente wie Schleifen (s. Abb. 4.15) und switch-Anweisungen in Komponenten überführen. Sie definiert für jede Methode ein PIP-POP-Paar und entsprechende DIPs und DOPs. Würde nun der Setzen des Arrays“-PIP aktiviert werden, so ” würde die Komponente nur die Methode zum Setzen des Arrays ausführen und dazu den Wert des Array“-DIP als Eingangsparameter verwenden. Da” nach würde die Komponente den Array gesetzt“-POP aktivieren. ” Eine zusammengesetzte Komponente hat den Vorteil, dass sie dem Benutzer den Zusammenhang mehrerer Funktionen verdeutlichen kann. Der Nachteil ist, dass zusammengesetzte Komponenten etwas unübersichtlicher sind und eine genaue Definition der Ports voraussetzen, um dem Benutzer vermitteln können, welche Ports zu welcher Sub-Funktion der Komponente gehören. Zur Steuerung des Programmflusses steht in den meisten Programmiersprachen eine if-Anweisung zur Verfügung, bei der der Programmfluss in eine von zwei möglichen Richtungen weitergeleitet wird. Auch solche Sprachelemente können als Komponente dargestellt werden, wie Abb. 4.8 zeigt. Ereignisse Die bisher betrachteten Komponenten beruhen alle auf dem selben Schema: Eine Komponente wird aktiviert, führt ihre Funktionalität unter Berücksichtigung der Eingangsparameter aus, stellt gegebenenfalls Ausgangsparameter zur Verfügung und aktiviert einen POP, um gekoppelte Komponenten zu ak- KAPITEL 4. ANFORDERUNGEN 32 Abbildung 4.9: Ein Ereignis als Komponente. Wird vom Bewegungsmelder eine Bewegung detektiert, so informiert er seine registrierten Beobachter (darunter diese Komponente) darüber. Die Komponente aktiviert daraufhin ihren POP und bildet damit die Quelle für einen neuen Programmfluss. Abbildung 4.10: Ein Ereignis, das von einem dimmbaren Licht ausgesandt wird. Diese Komponente stellt zusätzlich zum POP noch einen DOP mit dem aktuellen Dimm-Wert zur Verfügung. tivieren. Diese Art von Komponenten kann das eingangs erwähnte Beispiel von der Bewegungsmelder-Email-Applikation nicht umsetzen. Denn darin wird verlangt, dass der Bewegungsmelder von sich aus meldet, wenn eine Bewegung erkannt wird. Allgemein wird so ein Verhalten durch das Beobachter-Entwurfsmuster modelliert, das besagt, dass ein Subjekt (in diesem Fall das verteilte Gerät mit dem Bewegungsmelder) sogenannten Beobachtern (engl. Listeners) die Möglichkeit bietet bei sich registriert zu werden. Tritt eine Statusänderung im Subjekt auf, so werden alle registrierten Beobachter darüber informiert [8]. Den Beobachtern wird ein Ereignis (engl. Event) übermittelt. Um dieses Entwurfsmuster umsetzen zu können, müssen verteilte Geräte, wie jenes mit dem Bewegungsmelder, die Registrierung von Beobachtern unterstützen. Ist dies nicht der Fall, muss der Umweg über regelmäßiges Abfragen des Zustands (engl. Polling) genommen werden, sofern das Gerät eine Schnittstelle bietet, die das Auslesen des aktuellen Status ermöglicht. Ein Ereignis tritt, vom Standpunkt des Beobachters gesehen, spontan auf – er wird zu einem beliebigen Zeitpunkt vom Subjekt informiert. Um ein Ereignis als Komponente zu modellieren, muss sich eine solche Komponente als Beobachter beim entsprechenden Subjekt registrieren. Nachdem ein Ereignis vom Beobachter nicht erzwungen werden kann, bietet eine solche Komponente keine Input-Ports an (s. Abb. 4.9). Bei gewissen Ereignissen ist es jedoch nicht nur wichtig dass es aufgetreten ist, sondern auch was genau passiert ist. Das Ereignis liefert in diesem Fall noch einen Wert mit, der die Art des Ereignisses näher beschreibt. Ein dimmbares Licht, das als verteiltes Gerät abstrahiert werden kann, könnte z. B. bei jeder Änderung der Dimmstufe ein Ereignis mit dem aktuellen Dimm-Wert aussenden. Diese Situation in eine Komponente umzusetzen, ist wie in Abb. 4.10 dargestellt möglich. KAPITEL 4. ANFORDERUNGEN 33 Mit den vorgestellten zwei Arten von Komponenten (solche die Funktionen und solche die Ereignisse repräsentieren), lassen sich viele Szenarien in einem verteilten System umsetzen, indem die Komponenten miteinander gekoppelt werden, um zusammen eine neue Funktionalität zu definieren. Komponentenfabrik Um ein Schaltbild übersichtlich gestalten zu können, oder um (wie beim Array angeführt) verschiedene Kontexte zu realisieren, muss es möglich sein, jede Komponente mehrfach zu integrieren. Dazu wird jede Komponentenart einer Komponentenfabrik untergeordnet, die für die Erstellung und das Löschen dieser Komponentenart zuständig ist. Soll eine bestimmte Komponente zum Koppeln benötigt werden, wird eine Anfrage an die entsprechende Komponentenfabrik gestellt, die eine neue Instanz der Komponente erstellt. Diese Vorgangsweise entspricht dem Entwurfmuster Abstrakte Fabrik [8]. Die Ports der Komponente können nun mit den Ports anderer Komponenten gekoppelt werden. Auf diese Art können z. B. mehrere Array-Komponenten in das Schaltbild gebracht werden, die unterschiedliche Arrays repräsentieren. Soll eine Komponente gelöscht werden, so wird auch das über die zugehörige Komponentenfabrik geregelt, die den Lösch-Befehl umsetzt und evtl. notwendige Aufräumarbeiten durchführt. 4.1.2 Kopplungen Wie bereits angesprochen und in Abb. 4.5 dargestellt, ist es möglich, Komponenten miteinander zu koppeln. Dabei werden Zuordnungen von Ports erstellt, die einerseits den Programmfluss, andererseits den Datenfluss definieren. Kopplungen betreffen also nicht Komponenten (zumindest nicht direkt), sondern genauer betrachtet deren Ports. Je nach Port-Typ werden zwei Kopplungsarten unterschieden: Daten-Kopplungen (DK) und Programmfluss-Kopplungen (PK). Erstere sind für den Transport von Daten zwischen Daten-Ports zuständig, zweitere repräsentieren den Programmfluss. Sie geben also an, welche Funktion in Folge welcher anderen aufgerufen werden soll, indem sie Programmfluss-Ports miteinander verbinden. Abb. 4.11 zeigt den Wirkungsbereich der Kopplungen in einer etwas detailierteren Darstellung. Dabei ist ersichtlich, dass die DK die Daten nur bis zur Hülle“ der Komponente transportieren. Ob damit intern eine entfernte ” Funktion aufgerufen wird oder nicht, ist für die DK nicht weiter von Bedeutung. Selbiges gilt für die PKs: Sie aktivieren lediglich den Ziel-Port. Was dieser dann weiter macht, betrifft die PK nicht mehr. Bisher wurden immer nur Output-Ports an Input-Ports (aber nicht Output-Ports und Input-Ports jeweils untereinander) gekoppelt; für den Verlauf der Arbeit wird diese Einschränkung beibehalten, denn sie sorgt für ein konsistentes Erscheinungsbild der Kopplungen. Aus diesem Grund ist es KAPITEL 4. ANFORDERUNGEN 34 Abbildung 4.11: Detailierte Darstellung von Komponenten. Die linke Komponente wird von einem Licht als Beobachter über Dimm-Wertänderungen informiert und leitet dieses Ereignis an die Output-Ports weiter. Dort übernehmen die Kopplungen: Zunächst transportiert die DK den Dimm-Wert zum unteren DIP der rechten Komponente, der den Inhalt eines Emails definiert, danach aktiviert die PK den PIP der Email-Komponente (hiermit endet die Aufgabe der Kopplungen), worauf intern ein Email generiert wird (mit der fixen Empfängeradresse bernhard.wally@fh-hagenberg.at“), das an ” einen Email-Server weitergeleitet wird. Meldet der Email-Server den erfolgreichen Versandt des Emails, wird der DOP auf WAHR gesetzt, ansonsten auf FALSCH. Schließlich wird der POP aktiviert. auch nicht länger notwendig, die Kopplungen mit Pfeilen zu versehen, da die jeweilige Richtung der Verbindungen durch Output- und Input-Port definiert wird. Konvertierer Während PKs relativ unproblematisch sind, kann es bei DKs zu Komplikationen kommen, wenn die gekoppelten Ports auf verschiedenen Datentypen basieren. Zum Beispiel könnte der DOP eine Zeichenkette (String) zur Verfügung stellen, der DIP aber nur mit Ganzzahlen (Integer) umgehen können. Die Problemlösung kann im DOP, im DIP oder in der DK erfolgen. In dieser Arbeit ist es die DK, die mit einem Mechanismus ausgestattet wird, der Datentyp-Inkompatibilitäten durch Konvertierung der Daten auflösen soll. Werden zwei Daten-Ports miteinander gekoppelt, kann KAPITEL 4. ANFORDERUNGEN 35 die Kombination der Datentypen der beiden Ports zu einem der folgenden vier Fälle führen: • Identische Datentypen: Das ist der Idealfall, da hier keine weiteren Vorkehrungen getroffen werden müssen. Der Wert eines Ausgangsparameters kann direkt für einen Eingangsparameter verwendet werden. • Kompatible Datentypen: Der Datentyp des Ausgangsparameters repräsentiert eine Teilmenge des Eingangsparameters. In diesem Fall ist eine vollautomatische Konvertierung möglich. Ein Beispiel dafür ist etwa das Überführen einer 2 Byte Ganzzahl in eine 4 Byte Ganzzahl: Es findet kein Datenverlust statt. • Konvertierbare Datentypen: Hierzu zählen Konvertierungen, die mit einem potentiellen Datenverlust verbunden sind, wie etwa die Konvertierung einer Fließkommazahl in eine Ganzzahl. Ein anderes Beispiel ist die Konvertierung von boolschen Werten in Zahlenwerte, die man z. B. so vornehmen könnte, dass eine 0 im Zahlenraum dem boolschen FALSCH-Wert entspricht, eine Zahl ungleich 0 dem WAHR-Wert. Umgekehrt würde ein FALSCH in eine 0 und ein WAHR in eine 1 konvertiert werden. Für die in diese Kategorie fallenden Konvertierungen lassen sich also Standard-Szenarien erfinden, die jedoch nicht immer sinnvoll sind und gegebenenfalls angepasst werden müssen. • Inkompatible Datentypen: Sämtliche Konvertierungen die nicht ohne genaues Wissen des Kontexts durchgeführt werden können, fallen hier hinein, etwa die Konvertierung einer Uhrzeit in einen boolschen Wert. Um den Wert von einem Datentyp in den anderen überzuführen, gibt es keine passende immergültige Konvertierung. Eine mögliche Umwandlung eines Zeit-Wertes in einen boolschen Wert könnte wie folgt aussehen: WENN zeit vor 16:00 DANN gib WAHR zurück SONST gib FALSCH zurück WENN_ENDE Jede DK kann, wenn das notwendig ist, mit einem Konvertierer ausgestattet werden, der für die nötige Kompatiblität zwischen DOP und DIP sorgt. Abb. 4.12 zeigt, wo sich der Konvertierer im Schaltbild befindet. Mehrfachgekoppelte Ports Die von einem DOP zur Verfügung gestellten Daten sind unter Umständen nicht nur für einen DIP interessant, sondern für mehrere. Ebenfalls kann es gewünscht werden, dass ein DIP seine Daten nicht nur von einem DOP KAPITEL 4. ANFORDERUNGEN 36 Abbildung 4.12: Einbinden eines Konvertierers in die DK. (a) Zeigt die Kopplung der Wecker- an die Lichtschalter-Komponente. Wenn die Weckzeit vor 16:00 liegt, soll das Licht eingeschalten werden, ansonsten nicht. (b) stellt den in (a) rot umrahmten Teil dar und zeigt, dass der Konvertierer einen eingehenden Zeit-Wert in einen boolschen Wert umwandelt, um ihn kompatibel mit dem DIP der Lichtschalter-Komponente zu machen. Abbildung 4.13: Wird ein Email versendet, so wird der boolsche Rückgabewert dieser Funktion einerseits protokolliert, andererseits als Parameter für ein Licht (zum Ein- und Ausschalten) verwendet. Sobald der Wecker läutet“, ” wird die Uhrzeit protokolliert und das Licht ein- oder ausgeschalten, je nachdem welcher Wert derzeit dem DIP der Licht-Komponente zugewiesen ist. bezieht. Die Mehrfachkopplung von Ports soll explizit erlaubt werden, um keine funktionalen Einschränkungen zu riskieren. In Abb. 4.13 werden mehrfachgekoppelte Ports dargestellt. Die Mehrfachkopplungen werfen neue Fragen auf: In welcher Reihenfolge werden die DKs mit Daten versorgt? Was passiert, wenn zwei DKs gleichzeitig auf einen DIP zugreifen? Folgende Regeln werden dazu definiert: • Die Reihenfolge in der DKs Werte vom DOP zugewiesen bekommen ist nicht voraussagbar. Es ist aber auch nicht wichtig, ob eine DK die Daten zuerst bekommt oder nicht, da die Komponente des DOPs dafür zu sorgen hat, dass alle DKs ihre Werte an die entsprechenden DIPs transportiert haben, bevor der POP der Ausgangskomponente aktiviert wird und der Programmfluss weiterläuft. KAPITEL 4. ANFORDERUNGEN 37 Abbildung 4.14: Wird der Wecker aktiv, so werden folgende Aktionen parallel (in separaten Threads) ausgeführt: Der Lichtschalter wird betätigt und ein Email wird versandt. Das Email wird aber auch versandt, wenn sich die Dimmstufe des Lichts verändert hat. Zur besseren Übersicht werden die Daten-Kopplungen nicht dargestellt. • Wenn mehrere DKs gleichzeitig schreibend auf einen DIP zugreifen, so hat der DIP dafür zu sorgen, dass jede der Schreibaktionen atomar abläuft, um unvorhersagbare Programmfehler zu vermeiden. Die Atomarität könnte durch Programmiersprachelemente wie Mutices oder Semaphoren realisiert werden. Doch nicht nur Daten-Ports können mehrfachgekoppelt werden, auch bei Programmfluss-Ports sollen Mehrfachkopplungen möglich sein. Semantisch entsprechen mehrere PKs, die von einem POP ausgehen einer Gabelung des Programmflusses. Jede PK stellt einen neuen Thread dar; die Threads laufen parallel ab und ermöglichen so das gleichzeitige Ausführen mehrerer Programmfluss-Stränge. Abb. 4.14 soll diesen Sachverhalt verdeutlichen. Münden mehrere PKs in einen PIP, so entspricht das der Aktivierung des PIPs von verschiedenen Threads aus. Beispiel Zu guter Letzt soll noch Abb. 4.15 einen Gesamteindruck über die Komponenten und Kopplungen bieten, indem ein etwas größeres Szenario umgesetzt wird, das multiple Komponenteninstanzen, Schleifen und Mehrfachkopplungen beinhaltet. 4.2 Control Die Manipulation der Komponenten und Verbindungen erfolgt über genau spezifizierte, möglichst einfache Schnittstellen. So sollen etwa nur Basisdatentypen wie Zahlen und Zeichenketten zur Steuerung der Komponenten und Verknüpfungen genügen, um auch von entfernten Hosts Steuerungstätigkeiten ohne allzuviel Aufwand durchführen zu können. KAPITEL 4. ANFORDERUNGEN 38 Abbildung 4.15: Ändert sich die Dimm-Stufe des Lichtes, wird ein Array mit Email-Adressen und eines mit Telefonnummern durchlaufen, nachdem dessen Größe als Obergrenze der For-Schleife übernommen wurde, und für jeden jeweiligen Eintrag ein Email bzw. ein SMS verschickt. Außerdem wird der Dimm-Wert in einer Datei protokolliert. Daten-Kopplungen sind rot, Programmfluss-Kopplungen schwarz dargestellt. 4.2.1 Komponenten Die verfügbaren Komponentenfabriken müssen über einen Mechanismus einer Komponentenfabriks-Verwaltung bekannt gemacht werden, um diese auch nutzen zu können. Den Komponentenfabriken werden dann Kommandos zum Erstellen und Löschen von Komponenteninstanzen übermittelt. Zu beachten ist: Wird eine Komponente gelöscht, sind auch die Kopplungen zugehöriger Ports zu löschen, da sie ohne die Ports ihre Gültigkeit verlieren. Um das manuelle Setzen von DIP-Werten zu ermöglichen, muss der Schreib-Zugriff auf diese ermöglicht werden. Ebenso soll es durch den Zugriff auf PIPs möglich sein, Komponenten von Hand zu aktivieren. Nachdem verteilte Geräte gekoppelt werden sollen, kann es vorkommen, dass manche Geräte vorübergehend nicht zur Verfügung stehen. In diesem Fall sollten bereits erstellte Komponenteninstanzen nicht einfach gelöscht, sondern als offline markiert werden. Ist die betreffende Komponente zu einem späteren Zeitpunkt wieder verfügbar, so wird diese wieder als online markiert. Diese Unterscheidung ist einerseits für den Benutzer wichtig, um zu erkennen, dass gewisse Funktionalitäten evtl. nicht verwendet werden können, andererseits wüsste dann auch die Komponente selbst, dass die entfernte Ressource, die sie ansprechen soll, nicht existiert und lässt den entfernten Funktionsaufruf bleiben. In dieser Situation versiegen Programmflüsse – wenn sie in eine als offline markierte Komponente münden. KAPITEL 4. ANFORDERUNGEN 4.2.2 39 Kopplungen Die Steuerung der Kopplungen ist ähnlich jener der Komponenten: Kopplungen können erstellt und gelöscht werden. Weiters soll die Zuweisung eines Konvertierers zu einer DK ermöglicht werden. Die Definition der Konvertierer soll zur Laufzeit (über eine Art Scripting-System) möglich sein, um auftretende Probleme unkompliziert lösen zu können. 4.2.3 Schaltbild Das erstellte Schaltbild soll als XML-Datei gespeichert und bei Bedarf wieder geladen werden können. Die zuvor definierten Komponenteninstanzen, Kopplungen und Konvertierer sollen dabei automatisch wieder erstellt werden und ihren Dienst aufnehmen. 4.3 View Die Darstellung der Komponentenfabriken, Komponenten, Kopplungen und Konvertierern soll einerseits deren internen Zustand repräsentieren und andererseits Zugriff auf die Steuerung bieten, um Manipulationen vornehmen zu können. Eine grafische Benutzeroberfläche könnte die Komponenten als Blöcke mit Ein- und Ausgangs-Ports darstellen und die Verknüpfungen als Linien zwischen den Ports, so wie das auch in vorangegangenen Abbildungen gehandhabt wurde. Um über den Zustand der Komponenten informiert zu werden, bietet sich ein Ereignis-System an, bei dem die Darstellung als Beobachter auftritt. Informationen über vorhandene oder nicht länger vorhandene Komponenten und Komponentenfabriken können so der Darstellung ebenso übermittelt werden, wie der momentan gültige Konvertierer einer Kopplung oder der aktuelle Wert eines Daten-Ports. Die Darstellung kann auf diese Ereignisse reagieren und muss sich nicht selbst um den aktuellen Zustand des Systems kümmern. Somit wird die Zahl der Steuer-Aufrufe minimiert, da nur dann Ereignisse gesendet werden, wenn auch tatsächlich etwas passiert ist. Würde hingegen die Darstellung selbst verantwortlich sein, sich über den aktuellen Zustand des Systems zu informieren, fielen wohl gelegentlich Anfragen an, die keine Änderung des Systems feststellen würden. Kapitel 5 Technologieauswahl 5.1 Grafische Programmierwerkzeuge Die im vorigen Kapitel angeführten Anforderungen beschreiben im Grunde ein grafisches Programmierwerkzeug, bei dem der Programmfluss durch das Koppeln von Komponenten (der Ausdruck Komponente wird im weiteren Verlauf synonym mit Funktionsblock verwendet) definiert wird. Da es bereits solche Applikationen gibt, sollen einige Eigenschaften existierender Lösungen vorgestellt werden. Nachdem einige der Applikationen auch die Möglichkeit bieten, eigene Funktionsblöcke zu definieren, wird überprüft, ob es möglich ist, verteilte, dynamische Geräte (am Beispiel UPnP) als Programmerweiterungen einzubinden. Die hier behandelten Applikationen repräsentieren nicht eine vollständige Liste grafischer Programmiersysteme, sondern wurden als Beispiele herausgegriffen. 5.1.1 Virtools Dev Virtools Dev wird von Virtools1 vertrieben und bietet eine grafische Programmieroberfläche für interaktive 3D-Applikationen an, die Compositions (dt. Kompositionen) genannt werden. In einer Komposition werden Funktionsblöcke, die Behaviour Building Blocks, gekoppelt, die die Parameter der Kamera und der in der Szene vorhandenen Objekte sowie die Interaktionsmöglichkeiten definieren. Die Kopplungen müssen dabei nicht global gelten (aber auch solche Kopplungen gibt es), sondern können für jedes Objekt gesondert definiert werden. Eine zu einem Objekt gehörende Menge an Kopplungen (ein Objekt-gebundenes Schaltbild) von Building Blocks wird ebenso Script genannt wie global geltende Kopplungen. Eine Komposition besteht also aus Szenen, die (globale oder Objekt-gebundene) Scripts beinhalten, die wiederum Building Blocks und deren Kopplungen enthalten. Mehrere Building Blocks und die dazugehörenden Kopplungen können, 1 http://www.virtools.com/ 40 KAPITEL 5. TECHNOLOGIEAUSWAHL 41 Abbildung 5.1: Die Building Blocks werden in Virtools als graue Boxen dargestellt, die mit schwarzen (Behaviour Links) oder blau gestrichelten (Parameter Links) Linien verbunden werden. um die Übersichtlichkeit zu erhöhen, zu einem einzigen zusammengesetzten Building Block zusammengefasst werden. Abb. 5.1 zeigt, wie Building Blocks in Virtools Dev aussehen und wie diese gekoppelt werden können. Virtools Dev unterscheidet bei Kopplungen zwischen solchen, die den Programmfluss definieren (Behaviour Links) und solchen, die die Daten von Building Block zu Building Block transportieren (Parameter Links). Die Daten können auf diesem Weg modifiziert und bei Bedarf Script-übergreifend zur Verfügung gestellt werden. Nachdem Virtools Dev für die Erstellung von 3D-Szenen entwickelt wurde, sind die Behaviour Links Frame-gesteuert. Alle Verknüpfungen von Building Blocks, die nicht explizit verzögert werden, werden innerhalb des selben Frames berechnet. Als Entwickler einer Komposition ist man also dafür verantwortlich, dass rechenintensive Aufgaben über mehrere Frames verteilt werden, um die 3D-Szene flüssig abspielen zu können. Um das Repertoire der vorgegebenen Building Blocks zu erweitern, werden zwei Möglichkeiten angeboten [29]: • Leichtgewichtige Building Blocks: Benutzer mit ein wenig Programmierkenntnissen haben die Möglichkeit eigene leichtgewichtige“ ” Building Blocks zur Laufzeit zu erstellen. Dazu gibt es einen eigenen Building Block, Run VSL, der es ermöglicht mittels der Virtools Scripting Language (VSL) auf die Funktionen des Virtools Software Development Kits (Virtools SDK – siehe Schwergewichtige Building ” KAPITEL 5. TECHNOLOGIEAUSWAHL 42 Blocks“) zuzugreifen [32]. Für Run VSL gibt es einen integrierten Editor, der es erlaubt Ein- und Ausgangsparameter hinzuzufügen, zu entfernen und zu modifizieren. Der Typ der Parameter kann dabei aus einer Liste von mehr als 60 Datentypen gewählt werden. VSL ist von der Syntax her ähnlich wie C aufgebaut – wer also Erfahrung mit C oder Scripting-Sprachen wie JavaScript hat, sollte sich schnell mit VSL anfreunden können. Der Editor bietet eine Hervorhebung von Schlüsselwörtern (engl. Syntax Highlighting), automatische Vervollständigung von Funktionsaufrufen (engl. Auto Completion) sowie die Möglichkeit Abbruchstellen (engl. Break Points) zu definieren, um das Debuggen des Scripts zu erleichtern. VSL-Scripts eignen sich vor allem, um komplexe Parameter-Konvertierungen vorzunehmen oder etwa Schleifen kompakt und übersichtlich zu implementieren. • Schwergewichtige Building Blocks: Für Benutzer mit erweiterten Programmierkenntnissen eröffnet das Virtools SDK nahezu unbegrenzte Möglichkeiten für die Erweiterung von Virtools, sowie die Gelegenheit, die Funktionalitäten von Virtools in eigenen Applikationen zu nutzen. Der wichtigste Anwendungsfall ist die Möglichkeit, eigene Building Blocks zu definieren. Diese können beliebigen C++ Code ausführen und auch auf externe Bibliotheken zugreifen. Die definierten Building Blocks werden einzeln oder gesammelt in Dynamic Link Libraries 2 (DLLs) kompiliert und diese in den Ordner BuildingBlocks der Virtools Installation kopiert. Bei einem Neustart von Virtools Dev werden die DLLs automatisch eingebunden, und die selbst erstellten Building Blocks stehen zur Benutzung zur Verfügung. Um z. B. eine Aktion eines UPnP-Gerätes ausführen zu können, gäbe es unter anderem die Möglichkeiten • einen generischen Building Block zu schreiben, der als Input-Parameter die ID der auszuführenden Action sowie die Eingangsparameter in Form eines Arrays übernimmt und nach erfolgtem Aufruf die Ausgangsparameter als Array zur Verfügung stellt oder • für jede UPnP-Aktion einen eigenen Building Block zu schreiben, der diese Aktion genau repräsentiert und damit einen unkomplizierten Umgang mit der UPnP-Aktion bietet. Der Vorteil des generischen Building Blocks ist, dass damit beliebige UPnP-Aktionen angesprochen werden können, auch wenn diese zur Entwicklungszeit des Building Blocks noch nicht bekannt sind. Die Nachteile sind, dass 2 Für weitere Informationen zu DLLs siehe http://msdn2.microsoft.com/en-us/library/ 1ez7dh12(vs.80).aspx KAPITEL 5. TECHNOLOGIEAUSWAHL 43 • jeder Aktionsaufruf aufwändig auf- und nachbereitet werden muss, da die Eingangsparameter zunächst in Array-Form gebracht werden müssen und die Ausgangsparameter aus dem Array wieder ausgelesen werden müssen, um sie in einem Script weiterzuverwenden, sowie • die ID der Aktion dem Benutzer bekannt sein muss, um sie ansprechen zu können. Die Methode, für jede UPnP-Aktion einen eigenen Building Block zu schreiben hat den Vorteil, dass der Building Block genau die Aktion repräsentiert (hinsichtlich der Anzahl und des Typs der Parameter), jedoch den Nachteil, dass der Building Block im Voraus definiert worden sein muss. Es ist also nicht möglich, zur Laufzeit neuentdeckte UPnP-Geräte in Scripts einzubinden, sondern stattdessen muss zunächst ein entsprechender Building Block als DLL kompiliert werden und anschließend Virtools neu gestartet werden. Ein weiteres Problem stellen UPnP-Geräte dar, die bei einem Neustart ihre ID ändern. Der Building Block ist dann entweder unbrauchbar, weil er nur für eine gewisse ID funktioniert, oder er benötigt einen weiteren Eingangsparameter, mit dem die aktuelle ID des UPnP-Geräts eingestellt werden kann, wodurch der Benutzungskomfort stark geschwächt wird, da Details über das zu steuernde Gerät (die ID) bekannt sein müssen. Beide Methoden sind unbefriedigend, da sie entweder zu unflexibel oder zu kompliziert sind. Virtools Dev bietet leider keine Möglichkeit, zur Laufzeit dynamisch Building Blocks zu generieren und in der Benutzeroberfläche zur Verfügung zu stellen oder diese wieder verschwinden zu lassen. Auch gibt es keine Möglichkeit temporär abwesende Geräte visuell auszuzeichnen, um dem Benutzer intutiv mitteilen zu können, dass gewisse Ressourcen nicht verfügbar sind. Bis auf die fehlende Dynamik ist die von Virtools Dev verwendete Metapher für Building Blocks gut auf UPnP-Geräte übertragbar. Da fast alle Standard Building Blocks im Quellcode bei Virtools Dev mitgeliefert werden und da es zum SDK eine ausgezeichnete Dokumentation gibt, ist es möglich, die programmiertechnische Funktionsweise von Building Blocks verstehen zu lernen. 5.1.2 Max Max ist der Begriff für eine Familie von Produkten, die in der Tradition des Patcher Editors [12] stehen, der über MIDI3 - und Steuersignale Klänge erzeugen konnte. Während Patcher lediglich mit MIDI-Daten umgehen konnte, bieten die aktuellen Max-Produkte eine rigorose Echtzeit-Audiosignalverarbeitung an, wobei durch das Koppeln verschiedener Funktionsblöcke verschiedenste Klänge synthetisiert werden können. Die drei bekanntesten Pro3 Musical Instrument Digital Interface. Siehe auch: http://home.snafu.de/sicpaul/midi/ midi0a.htm KAPITEL 5. TECHNOLOGIEAUSWAHL 44 Abbildung 5.2: Ein simpler Patch (links), der einen Sub Patch (links als Object mit p“ bezeichnet) beinhaltet, der rechts dargestellt ist. Dieser Patch ” lädt eine Audiodatei und spielt sie ab. dukte sind PD4 , jMax5 und Max/MSP6 , die sich alle auf den Patcher Editor zurückführen lassen [12, 27]. Während Max/MSP kommerziell von Cycling 747 vertrieben wird, stehen PD und jMax unter einer BSD8 -artigen Lizenz bzw. der GNU GPL9 und sind inklusive Quellcode frei verfügbar. Bei Max werden die Funktionsblöcke einfach Objects genannt, ein Schaltbild heißt Patch. Patches können abgespeichert werden und sind teilweise unter den verschiedenen Produkten kompatibel. Die Kompatabilität hängt dabei vom Vorhandensein der gekoppelten Objects im jeweiligen Produkt ab. Um die Übersichtlichkeit zu wahren, können Teile eines Patches als Sub Patch zusammengefasst werden, der wiederum aus Sub Patches bestehen kann, usw. (s. Abb. 5.2) [26]. Jedes der Produkte kann durch externe Objects“ erweitert werden, und ” so existieren u. a. Erweiterungen, die z. B. das Erstellen und Manipulieren von 3D-Grafiken erlauben. Das Erstellen von externen Objects funktioniert ähnlich wie bei Virtools Dev über ein SDK, mit dem diese in C10 erstellt und kompiliert werden können. Durch die Kombination der Erweiterungen mit den bereits vorhandenen mächtigen Funktionen der Audiosignalverarbeitung können auf diese 4 http://www.puredata.info/ http://freesoftware.ircam.fr/rubrique.php3?id rubrique=14 6 http://www.cycling74.com/products/maxmsp 7 http://www.cycling74.com/ 8 http://www.opensource.org/licenses/bsd-license.php 9 GNU General Public Licence – die Lizenz für Open Source Software. Siehe auch: http://www.gnu.org/copyleft/gpl.html 10 PD unterstützt auch C++ und Fortran. 5 KAPITEL 5. TECHNOLOGIEAUSWAHL 45 Art beeindruckende Multimediainstallationen geschaffen werden. Dadurch finden Max-Produkte bei Live-Performances von Multimediakünstlern Verwendung. Dabei wird das jeweilige Max-Produkt als Werkzeug zur Signalverarbeitung verwendet, das einerseits die Klänge digitaler oder analoger Klangquellen manipuliert und mit synthetischen Klängen mischt und andererseits vorbereitete Videos durch aus dem Audiosignal extrahierte Parameter künstlerisch verändert, mixt und steuert11 . Sämtliche Max-Produkte weisen jedoch die selbe Schwäche wie Virtools Dev auf: Erweiterungen (externe Objects) lassen sich nicht zur Laufzeit hinzufügen oder löschen, sondern müssen beim Programmstart als kompilierte Bibliotheken zur Verfügung stehen. Die möglichen Workarounds sind gleich wie bei Virtools Dev: Entweder man entwickelt ein generisches Object, das beliebige UPnP-Aktionen ansprechen kann, oder pro UPnP-Aktion eines (s. Abs. 5.1.1). Nachdem Max-Produkte auch eigene grafische Elemente zulassen, wäre es allerdings möglich, die erstellten Sub-Patches (die den Aufruf des generischen Objects kapseln) oder Objects (für die jeweilige UPnPAktion) grafisch über den aktuellen Verfügbarkeitsstatus des entsprechenden UPnP-Geräts auszuzeichnen. 5.1.3 vvvv Im Gegensatz zu Max spezialisiert sich vvvv12 nicht auf Echtzeit-Audioverarbeitung, sondern hauptsächlich auf die Echtzeit-Videosynthese [15]. Das Produkt wird von Meso 13 , einer Bürogemeinschaft in Frankfurt/Main, entwickelt und steht für nicht-kommerzielle Projekte auf der Produkt-Website zum kostenlosen Download zur Verfügung [11]. vvvv wurde für Windows entwickelt und verwendet Graphics Device Interface14 (GDI) und DirectX15 Funktionen, um hochperformante Grafikverarbeitung zu gewährleisten. Die grafische Benutzeroberfläche präsentiert sich so spartanisch wie nur möglich: Startet man vvvv, öffnet sich ein leeres, graues Fenster, ohne Werkzeug- oder Menüleiste. Sämtliche Funktionen sind in Menüs und Auswahllisten versteckt, die über verschiedene Mausklicks aktiviert und deaktiviert werden können. Hat man sich an die ungewohnte Navigation gewöhnt, ist ein sehr effizientes Arbeiten möglich. Durch die fehlenden Leisten bietet das Fenster weiters die maximale Fläche an, auf der die Funktionsblöcke, Boxes oder Nodes genannt, angeordnet werden können (s. Abb. 5.3). 11 Unter http://www.cycling74.com/section/artists können Videos von Multimediainstallationen und Live-Performances angesehen werden, die mit Max/MSP als Steuerzentrale arbeiten. 12 http://vvvv.meso.net/ 13 http://www.meso.net/ 14 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/wingdistart 9ezp. asp 15 http://www.microsoft.com/windows/directx/ KAPITEL 5. TECHNOLOGIEAUSWAHL 46 Abbildung 5.3: Ein Patch, der links eine (sinnlose) Addition beinhaltet und rechts einen interaktiven GDI-Renderer, der die Textgröße abhängig von der y-Koordinate der Maus skaliert. Tabelle 5.1: Namensgebung der Elemente verschiedener grafischer Programmierumgebungen. Produkt Komponente Virtools Dev Max vvvv Building Block Object Box/Node Kopplung Schaltbild Behaviour Link, Parameter Link Patch Cord Connection Script, Composition Patch, Sub Patch Patch, Sub Patch Nachdem für vvvv keine Erweiterungen programmiert werden können, ist eine Einbindung von UPnP-Geräten nur indirekt möglich. UPnP-Aktionen könnten über die HTTP-Box ausgeführt werden, die Rückgabeparameter müssten in weiterer Folge aus der Aktions-Antwort des UPnP-Geräts ausgelesen werden. Das Problem bei dieser Methode ist, dass das Vorhandensein ebenso wie der URL des Gerätes bekannt sein muss. Der interessante Aspekt von UPnP, das automatische Erkennen von UPnP-Geräten im Netzwerk, verliert dabei jedoch ebenso seinen Zweck wie die Eigenschaft von verteilten Systemen, dass diese das darunterliegende Netzwerk abstrahieren. 5.1.4 Fazit Tabelle 5.1 listet eine Gegenüberstellung der Namen der Elemente der angeführten grafischen Programmierumgebungen auf, um die Begrifflichkeiten zu verdeutlichen. KAPITEL 5. TECHNOLOGIEAUSWAHL 47 Alle vorgestellten Programme weisen die selbe Eigenschaft auf: Sie unterstützen das dynamische Hinzufügen der Funktionsblöcke nicht, was aber gerade bei verteilten Geräten interessant wäre. Aufgrund der dynamischen Natur verteilter Systeme kann über die Verfügbarkeit und Menge der verteilten Geräte nicht immer im Voraus eine Aussage getroffen werden. Ein Programm, das die Vernetzung verteilter Geräte ermöglicht, sollte jedoch diese Dynamik auch in der grafischen Benutzeroberfläche widerspiegeln um so dem Benutzer auf einen Blick den aktuellen Zustand des Systems zu vermitteln. Für den eigenen Prototypen wird daher eine eigene Applikation entwickelt, die basierend auf den Konzepten der vorgestellten grafischen Programmierumgebungen ähnliche Funktionalitäten bereitstellen und zusätzlich die Dynamik verteilter Anwendungen ausreichend unterstützen soll. 5.2 Programmiersprache Nicht unbedingt notwendiger Weise, aber aus ästhetischem“ Interesse soll ” die zu entwickelnde Applikation plattformunabhängig sein. Nachdem in einer verteilten Anwendung die verteilten Geräte auf unterschiedlichen Plattformen realisiert sein können, passt es gut dazu, wenn auch die Steuereinheit nicht auf eine bestimmte Plattform festgelegt ist. Aufgrund der positiven Programmiererfahrung bieten sich C++ oder Java als Programmiersprachen an, wobei in C++ die wxWidgets16 -Bibliothek die Entwicklung einer plattformunabhängigen grafischen Benutzeroberfläche ermöglichen soll. wxWidgets ist ein Open Source Projekt und wird unter einer leicht modifizierten17 GNU Library General Public License18 (LGPL) vertrieben, die dem Anwender der Bibliothek zusätzliche Rechte einräumt. Sowohl für C++, als auch für Java existieren frei verfügbare UPnPImplementierungen, die die Kommunikation mit UPnP-Geräten drastisch vereinfachen. Dazu gehören z. B. das Intel UPnP SDK 19 , Cyberlink 20 und UPnPLib 21 . Nachdem im Vorfeld bereits einige thematisch verwandte Projekte in Java entwickelt worden sind, bietet sich Java als Programmiersprache für den eigenen Prototypen an. Ein zusätzliches Argument für Java ist die Verfügbarkeit des OSGi-Frameworks (die OSGi-Spezifikation wurde für Java entwickelt), da es 16 http://www.wxwidgets.org/ http://www.wxwidgets.org/about/licence3.txt 18 Auch: GNU Lesser General Public License. Siehe auch: http://www.gnu.org/licenses/ lgpl.html 19 Für Java und C++. Siehe auch: http://www.intel.com/technology/upnp/ 20 Für Java C, C++ und Perl. Siehe auch: http://www.cybergarage.org/ 21 Für Java. Siehe auch: http://www.sbbi.net/site/upnp/ 17 KAPITEL 5. TECHNOLOGIEAUSWAHL 48 • explizit für dynamische Anwendungen konzipiert wurde und daher als Applikationsgrundlage mehr als geeignet erscheint und • die nachträgliche Installation weiterer Technologien und Protokolle zur Laufzeit ermöglicht. Mit Swing22 steht weiters eine umfangreiche, an die eigenen Bedürfnisse leicht anpassbare Grafik-Bibliothek zur Verfügung. Die Entscheidung fällt somit zugunsten von Java. Als OSGi-Implementierung wird Equinox (s. Abs. 3.3.4) aus folgenden Gründen ausgewählt: • Die Qualität scheint sehr hoch zu sein, da es als Referenzimplementierung für die aktuelle OSGi Release 4 gilt. • Nachdem Eclipse-Plugins seit Version 3.0 als OSGi-Bundles zu entwickeln sind und Eclipse ein eigenes Plugin Development Environment (PDE) zur Entwicklung von Plugins anbietet, werden gewisse Hilfestellungen in der Entwicklung von Bundles (vor allem beim Management des Manifests) gegeben. • Eclipse bietet eine eigene Konfiguration an, dank der ausgewählte Bundle-Projekte mit einem Mausklick in einem Equinox-Framework gestartet werden können. • Die oben erwähnte Equinox-Umgebung kann auch im Debug-Modus gestartet werden, was das Finden von Fehlern stark beschleunigt. • In bisherigen Projekten stellte sich Equinox als zuverlässige Plattform dar, mit der es keinerlei Probleme gab. 22 Teil der Java Foundation Classes: http://java.sun.com/products/jfc/. Eine detailierte Einführung gibt es unter http://java.sun.com/docs/books/tutorial/uiswing/index.html Kapitel 6 Umsetzung Die Umsetzung des Prototyps, für den der Name Torem 1 gewählt wird, findet in folgender Entwicklungsumgebung statt: • Betriebssystem: Windows XP Professional2 und Gentoo Linux3 • Programmiersprache: Java 2 Standard Edition (J2SE) 5.0 • OSGi-Framework: Equinox (zuletzt: Equinox-3.2.0.v20060601) Da ein OSGi-Framework als Applikationsgrundlage dient, wird das Gesamtsystem in mehrere Bundles heruntergebrochen, die gewisse Teilaufgaben übernehmen (s. Abb. 6.1). Dabei lassen sich die Bundles in zwei Kategorien unterteilen: • Kern-Bundles repräsentieren Model und Control, wobei die ModelImplementierung mit zwei unterschiedlichen Ansätzen verfolgt wird: Das Component- und das Converter-Bundle stellen lediglich Interfaces zur Verfügung, die das Model beschreiben; konkrete Implementierungen müssen von Zusatz-Bundles verfügbar gemacht werden. Das Linkund das Position-Bundle definieren ihrerseits das Model, Kopplungen bzw. Koordinaten, komplett – Erweiterungen durch Zusatz-Bundles sind nicht möglich. Die Implementierung der Control erfolgt in Form von Manager-Klassen die als Dienste im OSGi-Framework registriert werden, um von anderen Kern- und Zusatz-Bundles genutzt werden zu können. • Zusatz-Bundles dienen dazu, zusätzliche Funktionalitäten in Torem einzubringen. Das können einerseits Model- und andererseits ViewImplementierungen sein. 1 Dieser Name hat keine besondere Bedeutung und ist auch keine Abkürzung. http://www.microsoft.com/ 3 http://www.gentoo.org/ 2 49 KAPITEL 6. UMSETZUNG 50 Abbildung 6.1: Aufbau des Prototyps. Die Kern-Bundles stellen Dienste zur Verfügung, die von anderen Bundles genutzt werden können, um SteuerKommandos abzugeben. Durch Zusatz-Bundles kann zusätzliche Funktionalität in das System eingebracht werden. Die Manager können von Bundles, die der View-Schicht entsprechen, benutzt werden, um Manipulationen der jeweiligen Models durch den Benutzer zu veranlassen. Innerhalb der Kern-Bundles kommunizieren die Manager untereinander, um das System konsistent zu halten. Zusatz-Bundles lassen sich in beliebiger Zahl installieren und sind sofort nach der Installation verfügbar – es ist nicht notwendig, einen Neustart des Frameworks vorzunehmen. Das OSGi-Framework erlaubt es auch, installierte Bundles zu stoppen und später wieder neu zu starten. Dadurch lässt sich z. B. der Wegfall von System-Ressourcen simulieren. Es ist weiters möglich, installierte Bundles zur Laufzeit zu aktualisieren, um z. B. fehlerhafte Bundles durch fehlerbereinigte zu ersetzen. Das Zusammenspiel von Model, View und Control in Torem wird in Abb. 6.2 dargestellt: Das Model wird ausschließlich über Steuerbefehle (engl. Control Commands) der Control manipuliert, die diese entweder selbsttätig ausführt oder die von der View verlangt werden. Die View bekommt die Informationen, die sie benötigt, um die korrekte Darstellung des Models zu garantieren, über Events mitgeteilt, die entweder von der Control oder vom Model selbst stammen. Für Probleme, die die Control nicht alleine lösen kann, gibt es auch noch einen Mechanismus, der es erlaubt, einen Benutzer nach Lösungsvorschlägen zu fragen (engl. User Request). Die View teilt der Control die Benutzerantwort (engl. User Response) mit, die sie dann weiterverarbeitet. KAPITEL 6. UMSETZUNG 51 Abbildung 6.2: Die MVC-Architektur von Torem. 6.1 Kern-Bundles Da die Kern-Bundles jeweils nur aus einem Java-Package (dt. Paket) bestehen, wird zu Beginn jeder Bundle-Beschreibung der Name des Packages angeführt. Im Text genannte Klassen werden in weiterer Folge nicht voll qualifiziert angegeben – die Java-Package-Deklaration entspricht somit der import-Anweisung einer Klassendefinition. Der Name der Kern-Bundles, unter denen sie im OSGi-Framework registriert werden, entspricht ebenfalls dem Package-Name. Die Quelldateien der beschriebenen Klassen befinden sich auf der beigelegten CD in folgendem Verzeichnis: /torem/source/<BundleName>/OSGI-OPT/<PackagePfad>/. 6.1.1 Component-Bundle Java-Package: net.bebedizo.torem.component Das Component-Bundle ist für die Umsetzung der Anforderungen an Model und Control der Komponenten zuständig, die in Abs. 4.1.1 bzw. 4.2.1 definiert wurden. Während die Control vollständig implementiert wird, werden für das Model lediglich Interfaces definiert. Es ist die Aufgabe von Zusatz-Bundles, konkrete Implementierungen vorzunehmen und dem OSGiFramework zuzuführen. Für die Umsetzung werden die englischen Begriffe der in Kap. 4 definierten Elemente verwendet, die folgendermaßen lauten: Component für die Komponente, Component Factory (CF) für die Komponentenfabrik, Port für den Port und Component Manager (CM) für die Umsetzung der Control. Die Beziehungen dieser Begriffe soll Abb. 6.3 verdeutlichen, in der auch die Schnittstellen zu View-Implementierungen berücksichtigt sind. KAPITEL 6. UMSETZUNG 52 Abbildung 6.3: Überblick über das Component-Bundle. Model Das Model des Component Bundles setzt sich aus den Components, den CFs und den Ports zusammen. Nachdem Torem in einer verteilten dynamischen Umgebung läuft, ist es nicht sinnvoll, eine generische Implementierung zu erstellen, die den Zugriff auf beliebige Ressourcen erlauben würde. Stattdessen werden Interfaces definiert, die von Zusatz-Bundles implementiert werden können, um CFs (und damit Components und Ports) in Torem zu integrieren. An dieser Stelle sei nur das Zusammenspiel dieser Komponenten beschrieben. Eine CF ist verantwortlich für das Erstellen, Löschen und Verwalten von Components und sollte immer nur eine Art von Components erzeugen (die Notwendigkeit einer CF wurde bereits in Abs. 4.1.1 beschrieben). Sie repräsentiert eine Art von Funktionalität, die aber erst durch das Anlegen von Component-Instanzen genutzt werden kann. Um eine CF in Torem verfügbar zu machen, genügt es, diese als Dienst im OSGi-Framework zu registrieren (das ist die Aufgabe von Zusatz-Bundles). Der CM wird darüber automatisch informiert, sowie er auch erfährt, wenn ein Zusatz-Bundle eine CF wieder entfernt. Eine registrierte CF bekommt vom CM eine eindeutige ID in Form einer Long-Zahl zugewiesen. Nachdem CFs von Zusatz-Bundles eingebracht werden, sind auch diese dafür verantwortlich, die CFs aktuell zu halten. Ein Bundle, das z. B. den Zugriff auf UPnP-Geräte ermöglicht, muss dafür sorgen, dass CFs, die UPnPAktionsaufrufe oder UPnP-Statusvariablen kapseln, wieder entfernt werden, sobald das zugehörige UPnP-Gerät nicht mehr im Netzwerk zur Verfügung steht. Die von einer CF erstellten Components erhalten ebenfalls vom CM eine eindeutige ID in Form einer Long-Zahl und sind von außen betrachtet nur eine Menge an Ports, die für Kopplungen verwendet werden können. KAPITEL 6. UMSETZUNG 53 Die IDs der Ports werden Component-intern, als Strings, verwaltet – die Ports verschiedener Components der selben CF haben also die selben IDs. Auf semantischer Ebene werden zwei Kategorien von Ports unterschieden: Programmfluss- und Daten-Ports. Jede der beiden Kategorien teilt sich wiederum in zwei Typen auf: Input- und Output-Ports. Das ergibt vier verschiedene Arten von Ports, die bei der Kopplung unterschiedlich behandelt werden müssen (s. Abs. 6.1.2). Wird ein Port aktiviert oder bekommt einen Wert zugewiesen, teilt er dies zwei Arten von Listeners, die sich bei Ports registrieren können, mit: • Port Listeners werden von den Kern-Bundles gestellt und dienen dem internen Datentransfer von Output- zu Input-Port und von InputPort zu Component. Ein Port Listener bekommt lediglich den Namen des Ports sowie den aktuellen Wert mitgeteilt. • Port Event Listeners werden mittels Port Events informiert, in die das entsprechende Port-Objekt integriert ist. Sie dienen der Aktualisierung von View-Implementierungen. Eine Component registriert sich bei ihren Input-Ports als Port Listener und kann auf Port-Aktivierungen und -Wertzuweisungen beliebig eingehen. Gewöhnlich wird eine Component auf die Wertzuweisung eines DIPs damit reagieren, dass sie diesen Wert selbst zwischenspeichert. Die Aktivierung eines PIPs wird hingegen das Ausführen der durch die Component beschriebenen Funktion zur Folge haben. Für die Implementierung einer Component gibt es (abgesehen vom Interface Component, das implementiert werden muss) keine verpflichtenden Bestimmungen, um die durch Components umsetzbaren Szenarien nicht einzuschränken. Sofern es möglich ist, sollten jedoch folgende Regeln eingehalten werden, da so dem Benutzer ein konsistentes Erscheinungsbild geboten wird und Missverständnisse vermieden werden können: • Wird ein PIP aktiviert, wird irgendwann als Reaktion darauf genau ein zugewiesener POP aktiviert (der Programmfluss soll nicht versiegen“ ” und sich auch nicht teilen). • Stellt eine Component DOPs zur Verfügung, so werden die DOPs vor dem POP aktiviert, um sicherzustellen, dass die Daten an gekoppelte DIPs weitergeleitet werden, bevor der Programmfluss weitergeleitet wird. Für CFs und Components gibt es je eine abstrakte Implementierung (ComponentFactoryPrototype bzw. AbstractComponent), die das Programmieren einer eigenen CF bzw. Component stark vereinfachen, sofern die eigenen Klassen davon abgeleitet werden. Für Ports steht eine vollfunktionstüchtige Implementierung mit der Klasse PortImpl zur Verfügung. Beispiele zum Erstellen eigener CFs und Components folgen in den Abs. 6.3.1 und 6.3.2. KAPITEL 6. UMSETZUNG 54 Control Die Control des Component-Bundles wird vom CM übernommen, der sich als Dienst im OSGi-Framework registriert. Der CM ist unter anderem dafür verantwortlich, zu erkennen, welche CFs derzeit online, und welche offline sind. Die Initiative bei einem Statuswechsel der CFs geht dabei von den CFs aus – der CM muss lediglich darauf reagieren. Um dem CM CFs zur Verfügung zu stellen, wird wiederum der DienstMechanismus des OSGi-Frameworks herangezogen. Der CM wird informiert, sobald eine CF als Dienst registriert wird, oder sich wieder abmeldet. Das OSGi-Framework übergibt dabei jeweils eine Referenz auf die registrierte oder abgemeldete CF. Der CM reagiert auf diese Nachrichten jeweils unterschiedlich: • Wenn sich eine CF abmeldet, wird geprüft, ob diese – noch immer vorhandene – Component-Instanzen erzeugt hat. Ist das der Fall, so wird die CF und alle erzeugten Components als offline markiert. Ist das aber nicht der Fall, so wird sie ohne weiteren Verwaltungsaufwand aus dem CM gelöscht und steht nicht länger zum Instanzieren von Components zur Verfügung. • Wenn eine CF registriert wird, wird zunächst überprüft, ob es eine als offline markierte (alte) CF gibt, die dieser entspricht. Die Prüfung erfolgt in zwei Schritten, die weiter unten erläutert werden. Existiert eine entsprechende CF, so wird die alte durch die neue ersetzt und die erzeugten Components der alten werden durch entsprechend erzeugte Component-Instanzen der neuen ersetzt. Die ersetzten Components und die neue CF werden als online markiert. Existiert keine entsprechende CF, so wird die neue CF einfach der Menge an bereits vorhandenen hinzugefügt und kann verwendet werden, um Components zu erzeugen. Um herauszufinden, ob eine neu registrierte einer als offline markierten CF entspricht, werden zwei Stufen der Übereinstimmung definiert. Die eine besagt, ob zwei CFs gleich (engl. equal), die zweite, ob die CFs einander ähnlich (engl. similar) sind. Gleichheit wird automatisch verarbeitet und die neue CF ersetzt die als offline markierte. Sind zwei CFs einander jedoch nur ähnlich, so muss ein Benutzer in die Entscheidung einbezogen werden, ob die neue CF die alte ersetzen soll. Die Kriterien für Gleichheit und Ähnlichkeit werden als Schlüssel-Wert-Paare in jeder CF gespeichert und sind von dort über die Methoden getEqualities bzw. getSimilarities abrufbar. Der Grund, warum der Begriff der Ähnlichkeit überhaupt eingeführt werden muss, liegt darin, dass die UPnP-Spezifikation vorsieht, dass jedes UPnP-Gerät eine einzigartige ID haben muss. Diese Definition führt dazu, dass einige Geräte beim Start eine zufällig generierte Zeichenkette als ID benutzen, was zur Folge hat, dass ein und das selbe Gerät bei jedem Neustart KAPITEL 6. UMSETZUNG 55 eine neue ID erzeugt. Eine strenge Gleichheits-Prüfung würde ein neugestartetes Gerät aufgrund der unterschiedlichen ID als unterschiedlich ansehen und keine Ersetzung vornehmen, was u. U. nicht im Sinne des Benutzers ist. Andererseits würde ein Nichtbeachten der ID dazu führen, das Geräte, die sich beabsichtigterweise nur aufgrund ihrer ID unterscheiden, als gleich erkannt würden. Die Möglichkeit, den Benutzer entscheiden zu lassen, scheint eine gute Möglichkeit zu sein, um dieses Problem zu umgehen. Um die Benutzerabfrage durchführen zu können, muss mindestens ein User Request Event Listener im OSGi-Framework registriert sein. Jeder der Listener wird um Rat bezüglich der Gleichheit gefragt, und sobald einer der Listener antwortet, wird die CF dementsprechend gesetzt. View Um eine View für das Component-Bundle zu implementieren, sollten folgende Interfaces genauer betrachtet werden: • ComponentManager zum Weiterleiten von Steuerbefehlen an CFs, Components und Ports; ist als Dienst im OSGi-Framework registriert. • ComponentEventListener und PortEventListener sind zu implementieren, um über den aktuellen Zustand des Models informiert zu werden. • UserRequestEventListener, um dem Benutzer die Möglichkeit zu geben, Ähnlichkeitsproblematiken zu lösen. 6.1.2 Link-Bundle Java-Package: net.bebedizo.torem.link Das Link-Bundle implementiert Model und Control der Kopplungen, die zwischen Ports hergestellt werden können, wobei in der Implementierung der engl. Begriff Link für Kopplung gewählt wird. Abb. 6.4 zeigt die Hauptbestandteile dieses Bundles. Model Ein Link (vollständig implementiert in der Klasse LinkImpl) dient der Kopplung eines DOPs mit einem DIP (Daten-Link) oder eines POPs mit einem PIP (Programmfluss-Link). Mischformen (DOP mit PIP oder POP mit DIP) sind ebensowenig zulässig, wie das Koppeln von Input-Ports und Output-Ports jeweils untereinander (s. Abs. 4.1.2). Die Unterscheidung zwischen Programmfluss- und Daten-Link geschieht auf einer rein semantischen Ebene. Programmiertechnisch sind die beiden Links äquivalent, wobei beim Programmfluss-Link als zu übertragendes Datum immer null herangezogen wird. Ein Link registriert sich als Port Listener bei dem Output-Port, der KAPITEL 6. UMSETZUNG 56 Abbildung 6.4: Die wichtigsten Bestandteile des Link-Bundles. das Datum bereitstellt, das an einen Input-Port weitergeleitet wird. Sobald ein Datum an den Input-Port transportiert wurde, werden registrierte Link Event Listeners mittels eines Link Events darüber informiert. Wie in Abs. 4.1.2 erwähnt, müssen Daten-Links gegebenenfalls Konvertierungen vornehmen. Dazu kann jedem Link ein Konvertierer (s. Abs. 6.1.3) zugeteilt werden, der vom Link aufgerufen wird, bevor das Datum an den DIP weitergeleitet wird. Ein Link prüft, sobald er angelegt wurde, oder jedesmal wenn ihm ein Konvertierer zugeteilt wurde, ob die Datentypen der Ports und (falls vorhanden) des Konvertierers zueinander kompatibel sind. Danach wird ein entsprechendes Link Event an registrierte Listener versandt. Ein Programmfluss-Link startet zur Aktivierung eines PIPs jedesmal einen neuen Thread (dt. Faden oder Ausführungsstrang [40]). Der Grund dafür soll rasch erläutert werden: Ein PIP informiert seine Component über die Methode portActivated darüber, dass nun die Component-spezifische Funktionalität auszuführen ist. Innerhalb von portActivated wird dann ein POP aktiviert, der über einen Programmfluss-Link wieder einen PIP aktiviert, usw. Würde die Aktivierung des PIPs nicht in einem eigenen Thread geschehen, würden sich die Methodenaufrufe ineinander schachteln und den Speicher des Computers auffüllen, bis ein Ende des Programmflusses kommt. Im Falle einer Schleife im Schaltbild ist dies jedoch nie der Fall, und deswegen erfolgt der Aufruf des PIPs in einem neuen Thread, da dadurch das Ineinanderschachteln verhindert wird. Control Der Link Manager (LM) dient dem Erzeugen und Löschen von Links sowie dem Zuweisen eines Konvertierers zu einem Link. Um einen Link anlegen zu können, werden dem LM die IDs der zu verbindenden Ports sowie die der zugehörigen Components mitgeteilt. Er sucht sich daraufhin vom CM KAPITEL 6. UMSETZUNG 57 die entsprechenden Port-Instanzen und registriert den Link als Port Listener beim Output-Port. Den zu koppelnden Input-Port speichert der Link intern ab, um Programmfluss und Daten weiterleiten zu können. Der LM wird seinerseits auch vom CM aufgerufen, falls eine Component gelöscht wird. Der CM sucht sich in diesem Fall alle Links, die mit Ports dieser Component verbunden waren, heraus und teilt dem LM mit, das diese Links zu löschen seien. Eine weitere wesentliche Funktionalität wurde dem LM zugeteilt: Das Speichern und Laden des erstellten Schaltbildes, für das im weiteren Verlauf der Begriff Mapping verwendet wird. Zum Speichern werden alle aktuell erzeugten Components und deren CFs sowie alle Links und deren Konvertierer in einen XML-String gepackt, der dann von einer View als XML-Datei gespeichert werden kann. Um ein Mapping zu laden, wird dem LM ein entsprechender XML-String übergeben. Dieser löscht daraufhin zunächst alle bestehenden Components und Links und erstellt anschließend neue, gemäß der XML-Definition. Um die Wiederherstellung möglichst benutzerfreundlich durchführen zu können, werden in das Mapping auch die Koordinaten, an denen sich die einzelnen Components in der View befinden (s. Abs. 6.1.4), sowie die Werte, die den Ports zum Zeitpunkt des Speicherns zugewiesen sind, gespeichert. Dadurch wird beim Laden einerseits das Layout und andererseits die Zuordnung der Werte zu den Ports, die andernfalls mit null initialisiert würden, sichergestellt. Dabei ist zu beachten, dass alle im Mapping definierten CFs zum Zeitpunkt des Ladens auch tatsächlich vorhanden sein sollten. Fehlt eine Ressource, so wird das Mapping nicht vollständig geladen. Das XML-Schema für die XML-Beschreibung eines Mappings ist auf der CD unter dem Pfad /torem/mapping.xsd einsehbar. View Eine View, die das Link-Bundle repräsentieren soll, muss vor allem die zwei folgenden Interfaces beachten: • LinkManager zum Weiterleiten von Steuerbefehlen an Links, sowie zum Laden und Speichern erstellter Mappings; ist im OSGi-Framework als Dienst registriert. • LinkEventListener kann implementiert werden, um über den aktuellen Zustand des Models informiert zu werden. Nicht vernachlässigt werden sollte die Tatsache, dass Links nur in Verbindung mit Components und deren Ports einen Sinn ergeben. Es ist daher sinnvoll, in einer View-Implementierung Links in Kombination mit den betroffenen Components darzustellen, und somit auch die Interfaces des Component-Bundles (s. Abs. 6.1.1) nicht außer Acht zu lassen. KAPITEL 6. UMSETZUNG 58 Abbildung 6.5: Das Converter-Bundle. 6.1.3 Converter-Bundle Java-Package: net.bebedizo.torem.converter Das Converter-Bundle sorgt für die Einbindung von Konvertierern, in weiterer Folge Converters genannt. Die Verwaltung der Converters wird vom Converter Manager übernommen. Abb. 6.5 gibt einen Überblick über die Elemente, die in diesem Bundle definiert werden. Model Die zu verwaltenden Objekte in diesem Bundle sind die Converters. Jeder Converter repräsentiert einen bestimmten Algorithmus, der die Konvertierung eines Objekts von einem Datentyp in einen anderen (oder auch wieder denselben) ermöglicht. In Java-Syntax ausgedrückt bedeutet es, dass er die Methode public Object convert( Object value ); des Interfaces Converter implementieren muss. Ein Converter wird durch den Input- und den OutputDatentyp sowie die convert Methode definiert. Ein Beispiel für einen Converter, der einen Wert von einer Zeichenkette (String) in eine Gleitkommazahl (Float) umwandelt, ist: // ... public Float convert ( Object value ) { return Float . parseFloat ( ( String ) value ) ; } // ... Control Die Control der Converters wird vom Converter Manager übernommen. Dieser bietet die Möglichkeit, Converters hinzuzufügen und zu löschen. Das Hinzufügen kann auf zwei unterschiedliche Arten erfolgen: KAPITEL 6. UMSETZUNG 59 • Über ein Zusatz-Bundle, das Converters als Dienste im OSGi-Framework registriert, oder • indem man einen Converter-Namen und den Algorithmus (als String) sowie den Input- und den Output-Datentyp (als Class-Objekte) dem Converter Manager übergibt, der sich daraufhin den Converter selbst erstellt (s. unten). Der Converter Manager erstellt für jeden Link, zu der ein Converter hinzugefügt werden soll, eine neue Instanz eines Converters. Darum speichert er nur das Class-Object der hinzugefügten Converters und ruft bei Bedarf die newInstance Methode auf. Jeder Converter muss demnach einen DefaultKonstruktor4 bereitstellen, damit dieser Aufruf funktioniert. Das Erstellen eines Converters über den Converter Manager (mittels der Methode createConverter) funktioniert über einen etwas komplizierteren Mechanismus, der in aller Kürze beschrieben werden soll: Beim Start des Converter-Bundles legt der Converter Manager in einem Verzeichnis, das ihm vom OSGi-Framework zur Verfügung gestellt wird, das Converter Interface sowie eine abstrakte Implementierung (AbstractConverter – beinhaltet alles außer der convert Methode) als Java-Quelldateien ab und kompiliert diese mit dem Java Compiler von Sun5 . Die kompilierten Klassen sind notwendig, um den Klassenpfad zu definieren, der für die nachfolgenden Converter-Kompilierungen gebraucht wird. Soll nun ein neuer Converter geschaffen werden, so nimmt der Converter Manager die übergebenen Parameter (Input- und Output-Datentyp, Script sowie der Name für den Converter) und baut sich damit eine neue Klasse, zunächst in Form eines (langen) Strings, der den Quelltext der Klasse repräsentiert, zusammen. Diese erweitert die zuvor erwähnte abstrakte Klasse. Den String speichert er anschließend als Datei ab und versucht sie – unter Zuhilfenahme des zuvor erwähnten Klassenpfades – zu kompilieren. Ist die Kompilierung erfolgreich, so wird die erstellte Klasse mittels eines java.net.URLClassLoaders geladen und steht ab sofort zur Verfügung. Klappt die Kompilierung nicht, wird die Zeile, wo ein Kompilierfehler vom Java Compiler angezeigt wurde, zurückgeliefert. View Eine View für das Converter-Bundle ist vor allem auf folgende zwei Interfaces angewiesen: • ConverterManager zum Erstellen und Löschen von Converters; ist als Dienst im OSGi-Framework registriert. 4 5 Ein Konstruktor, der keine Parameter definiert. Dieser liegt jedem JDK im JAR-Archiv <JDK>/lib/tools.jar als Java-Klasse bei. KAPITEL 6. UMSETZUNG 60 Abbildung 6.6: Das Position-Bundle. • ConverterEventListener sollte implementiert werden, um über hinzugefügte oder entfernte Converters informiert zu werden. 6.1.4 Position-Bundle Das Position-Bundle speichert mit Hilfe des Position Managers die Koordinaten, die die instanzierten Components in der View-Implementierung haben. Abb. 6.6 illustriert die Bestandteile des Position-Bundles. Model Die zu verwaltende Entität sind 2D-Koordinaten, die die Position der Components repräsentieren. Zur Verwaltung wird die Klasse java.awt.Point herangezogen. Control Der Position Manager verwaltet 2D-Koordinaten, die er Components zuweist. Nachdem Torem das gleichzeitige Vorhandensein mehrerer View-Implementierungen unterstützt, sorgt der Position Manager dafür, dass die Components in allen Views an der selben Stelle sind. View Folgende Interfaces werden vom Position Bundle definiert und können von View-Implementierungen verwendet werden: • PositionManager zum Mitteilen und Erfragen der Koordinaten der Components; ist im OSGi-Framework als Dienst registriert. • PositionEventListener kann implementiert werden, um über geänderte Koordinaten informiert zu werden. KAPITEL 6. UMSETZUNG 6.2 61 GUI-Komponenten Die in diesem Abschnitt vorgestellten GUI-Komponenten sind als BeispielImplementierungen für die View-Schicht der MVC-Architektur zu sehen. Die GUI-Bundles fallen in die Klasse der Zusatz-Bundles; sie sind für den Betrieb von Torem also nicht unbedingt notwendig. Eigene View-Implementierungen können anstatt der hier gelisteten eingebunden werden. Es ist möglich, nachdem mithilfe von GUI-Komponenten ein Mapping erstellt wurde, die GUIBundles zu stoppen, ohne den Betrieb der Kern-Bundles zu beeinflussen. Bezüglich der Namesgebung der Bundles und der Position der Quelldateien auf der CD gelten die selben Regeln wie in Abs. 6.1 festgehalten. 6.2.1 GUI-Basis Java-Package: net.bebedizo.torem.gui Als gemeinsame Basis für die anschließend definieren View-Implementierungen stellt dieses Bundle einen javax.swing.JFrame zur Verfügung, in den weitere Bundles eigene Inhalte in Form von javax.swing.JPanels beisteuern können. Der JFrame ist, wie in Abb. 6.7 dargestellt, in mehrere Bereiche unterteilt, die jeweils als javax.swing.JTabbedPane implementiert werden, wodurch mehrere Views pro Bereich als seperate Tabs (dt. Karteireiter) hinzugefügt werden können, ohne sich visuell in die Quere zu kommen. Startet man das Bundle ohne weitere GUI-Bundles, so ist der dargestellte JFrame leer. Erst durch zusätzliche Bundles werden die drei Bereiche mit Inhalten gefüllt. Dazu müssen die Bundles Dienste registrieren, die diesem Bundle vom OSGi-Framework übermittelt werden. Je nach Art des registrierten Dienstes wird dieser einem der drei Bereiche zugeordnet: • ComponentFactoriesVisualizer werden dem Bereich zur Darstellung der CFs (links oben) hinzugefügt, • ComponentLinkVisualizer werden in der unteren Hälfte der Applikation zur Anzeige gebracht und • AllPurposeVisualizer werden rechts oben dargestellt. Jeder dieser Dienste leitet sich von JPanel ab und wird in Form eines Tabs in den entsprechenden Bereich eingegliedert. Wird ein Dienst wieder abgemeldet, so wird der zugehörige Tab gelöscht. Um die Interoperabilität zwischen Views verschiedener Bundles zu gewährleisten, steht ein Listener-System zur Verfügung, das ebenfalls über die Dienst-Infrastruktur des OSGi-Frameworks verwaltet wird. Vier unterschiedliche Listeners dienen dem Datenaustausch, der über Benutzeraktivitäten informieren soll: KAPITEL 6. UMSETZUNG 62 Abbildung 6.7: Der hier dargestellte JFrame teilt sich in drei Bereiche, die für die Anzeige unterschiedlicher Views geeignet sind. Der Bereich links oben (gelb) soll Views zur Darstellung der verfügbaren CFs Platz bieten, rechts davon (rot) befindet sich ein Bereich, der verschiedenste Views beinhalten kann (z. B. eine Auflistung der definierten Converters). Die große Fläche unten (blau) ist für die Darstellung der Components und Links (des Mappings) reserviert. • ComponentFactorySelectionListener werden über die aktuell ausgewählte CF informiert, • ComponentSelectionListener erhalten eine Benachrichtigung über die aktuell im Mittelpunkt stehende Component, • ConverterSelectionListeners wird eine Link-ID und die dazugehörige Converter-ID übermittelt und • LinkSelectionListener werden informiert, sobald ein Link die besondere Aufmerksamkeit des Benutzers hat. Das Informieren der Listeners muss von jeder View selbst initiiert werden. Soll ein komplexeres Interoperationsmuster realisiert werden, ist es empfehlenswert, von diesem Listener-System abzusehen und eigene Schnittstellen für den View-übergreifenden Datentransfer zu definieren. KAPITEL 6. UMSETZUNG 63 Menü Unabhängig von den drei Bereichen enthält der JFrame ein Menü und eine Werkzeugleiste. Die dadurch ausführbaren Aktionen sind bei beiden GUIElementen gleich – lediglich das Beenden der Applikation geht nur über das Menü. Die redundaten vier Aktionen sind: • Das Speichern des aktuell erstellten Mappings in eine beliebige Datei (Speichern unter...), • das Laden eines gespeicherten Mappings aus einer Datei (Öffnen...), • das Speichern des aktuellen Mappings unter dem selben Namen, unter dem es zuvor gespeichert oder von dem es geladen wurde (Speichern) und • das Löschen aller erstellten Components und Links, um wieder ein leeres Blatt“ vor sich zu haben (Neu). ” User Requests Um Benutzeranfragen, die evtl. vom CM gestellt werden, beantworten zu können, registriert sich das Bundle als UserRequestEventListener im OSGiFramework, was dazu führt, dass User Requests an dieses Bundle versendet werden. Sobald ein User Request eintrifft, wird ein modaler Dialog geöffnet, der dem Benutzer den Text des Requests präsentiert und ihm die Möglichkeit gibt mit Ja“ oder Nein“ zu antworten. Die Antwort wird als boolscher ” ” Wert interpretiert und an den Sender des Requests zurückgeleitet. Lokalisierung Um die grafische Benutzeroberfläche problemlos in mehrere Sprachen übersetzen zu können, holen sich alle Menüeinträge und die Tooltips6 der Werkzeugleisteneinträge ihre darzustellenden Strings aus einer Datei-basierten Sprach-Datenbank. Diese fußt auf der Lokalisierungs-Infrastruktur (engl. Localization), die von Java über das java.util.ResourceBundle bereitgestellt wird. Für jede Sprache wird eine eigene Datei mit einem Namen nach dem Schema bundle_<Sprache>.properties erstellt, wobei <Sprache> der in [34] und [4] dargestellten Form folgen muss, in der Schlüssel-Wert-Paare gespeichert werden. Wird nun ein String zum Anlegen einer grafischen Komponente benötigt, so wird über das ResourceBundle nach einem passenden Eintrag für die aktuelle Systemsprache gesucht. Wie in [4] ausgeführt, verläuft die Suche vom Speziellen ins Generelle. Ein Beispiel: Angenommen, die aktuelle Systemsprache ist de_AT (Deutsch mit der Landeskennung Österreich) und es wird 6 Ein Tooltip ist eine Zeichenkette, die beim längeren Verweilen des Mauszeigers auf einer JComponent auftaucht [40]. KAPITEL 6. UMSETZUNG 64 nach einem Wort für den Schlüssel Open“ gesucht, dann wird zunächst in ” der Datei bundle_de_AT.properties, sofern diese vorhanden ist, nach einem Eintrag gesucht (der könnte z. B. Mach’s auf“ lauten). Schlägt die Suche ” fehl, ist die Datei bundle_de.properties an der Reihe. Gibt es auch diese Datei nicht oder findet sich darin kein passender Eintrag (z. B. Öffnen“) ” zum Schlüssel, wird die Ausweich-Datei bundle.properties zu Rate gezogen (in der schließlich die englische Version gefunden wird: Open“). ” Gemäß der Empfehlung in [22] werden die Lokalisierungs-Dateien im Ordner OSGI-INF/l10n als bundle*.properties abgelegt. Der Unterordner l10n steht für die gängige Abkürzung des englischen Wortes localization“ ” und bedeutet soviel wie l, dann 10 Buchstaben, dann n“. ” Die Lokalisierung kann beim Start des OSGi-Frameworks auch über die Kommandozeile definiert werden, wenn die systemweite Lokalisierung überschrieben werden soll. Es muss lediglich die Java-Systemeigenschaft osgi.nl definiert werden, in der eine beliebige Lokalisierung angegeben werden kann (z. B. -Dosgi.nl=de für Deutsch ohne Landeskennung). 6.2.2 Component Factories Java-Package: net.bebedizo.torem.gui.componentfactory.all Dieses Bundle definiert eine View zum Anzeigen aller verfügbarer CFs, die in einer Liste dargestellt werden und dem Benutzer – durch einen Doppelklick auf einen Listeneintrag – die Möglichkeit geben, eine Component von der zugehörigen CF erstellen zu lassen. ComponentFactorySelectionListeners werden aufgerufen, sobald ein Rechtsklick auf einen der Listeneinträge erfolgt. Abb. 6.8 zeigt einen Screenshot der View in deutscher Lokalisierung. Das Bundle registriert sich als ComponentFactoriesVisualizer im OSGiFramework und wird daher vom GUI-Basis-Bundle benachrichtigt, wenn • CFs registriert oder abgemeldet wurden (hinzufügen zur oder entfernen aus der Liste), • CFs als on- oder offline markiert wurden (entsprechenden Listeneintrag ausgrauen oder nicht), • CFs ersetzt wurden (entsprechenden Listeneintrag ersetzen) oder • eine Component in einer anderen View selektiert wurde (setzen des Fokus auf die zugehörige CF). Lokalisierung Auch die Darstellung der CFs findet lokalisiert statt. Da sich das Angebot der CFs ändern kann, werden die Lokalisierungen als eigenständige Fragment-Bundles realisiert. Ein Fragment-Bundle ist ein Bundle, dass als KAPITEL 6. UMSETZUNG 65 Abbildung 6.8: Die verfügbaren CFs werden in einer Liste dargestellt. Ein Doppelklick auf einen der Einträge erzeugt eine entsprechende ComponentInstanz. Klickt man auf einen Listeneintrag rechts, werden registrierte ComponentFactorySelectionListeners darüber informiert. Abbildung 6.9: Ein Host-Bundle (links oben) wird von einem FragmentBundle (links unten) erweitert. Danach sieht das Host-Bundle virtuell so aus, wie rechts dargestellt. Ergänzung zu einem anderen, bereits installierten, dient, das dann als HostBundle bezeichnet wird. Im Grunde wird das von der JAR-Datei repräsentierte Dateisystem des Host-Bundles mit dem des Fragment-Bundle verschmolzen (s. Abb. 6.9). Wird eine neue CF im OSGi-Framework registriert, zu der es noch keine Übersetzung gibt, so kann dieser Missstand durch Erweitern der SprachDatenbanken in den Fragment-Bundles nachgeholt werden. Änderungen im GUI würden jedoch erst nach einer Auffrischung des Component-FactoriesBundles selbst ersichtlich werden. Der Vorteil bei dieser Vorgehensweise liegt darin, dass es sehr einfach ist, zusätzliche Sprachen einzubinden. Das gelingt durch das Einbinden weiterer Fragment-Bundles mit der entsprechenden Lokalisierung. KAPITEL 6. UMSETZUNG 66 Abbildung 6.10: Components werden als graue Blöcke dargestellt, Programmfluss-Ports sind weiße Rechtecke, Daten-Ports weiße Dreiecke, jeweils schwarz umrandet. Die Links werden als Linien gezeichnet. Der Grund, warum bei diesem Bundle auf Fragment-Bundles gesetzt wird, beim GUI-Basis-Bundle aber nicht, liegt darin, dass beim GUI-BasisBundle die Übersetzung für die Sprachen Deutsch und Englisch komplett umsetzbar ist, da sich die Elemente nicht dynamisch ändern. Sollten neue Elemente in das GUI aufgenommen werden, muss das Bundle ohnehin neu kompiliert werden und entsprechende Ergänzungen der Sprach-Datenbanken könnten zu diesem Zeitpunkt eingefügt werden. Beim Component-FactoriesBundle können aber laufend neue Elemente zur Anzeige gebracht werden, weshalb eine externe Sprach-Datenbank sinnvoller ist. Neue Sprachen können beim GUI-Basis-Bundle jedoch ebenfalls über Fragment-Bundles hinzugefügt werden. 6.2.3 Components und Links Java-Package: net.bebedizo.torem.gui.componentlink.standard Das Component-Link-Bundle realisiert die View zum Anzeigen der erstellten Components und Links und ermöglicht das Hinzufügen, Löschen und Manipulieren von Links, sowie das Löschen von Components. Abb. 6.10 zeigt, wie die Elemente in dieser View angezeigt werden. Components Da sich das Bundle als ComponentLinkVisualizer registriert hat, bekommt es vom GUI-Basis-Bundle mitgeteilt, wenn Components erstellt oder gelöscht werden und kann diese zur Anzeige bringen oder auch wieder löschen. Die Darstellung der Components orientiert sich stark an den Visualisierungen der Überlegungen, die in Abs. 4.1.1 angestellt wurden. Eine Component KAPITEL 6. UMSETZUNG 67 Abbildung 6.11: Die Speicher-Component zeigt den gespeicherten Wert an, sofern dieser ungleich null ist. wird als graues Rechteck dargestellt, das auf der linken Seite die InputPorts und auf der rechten Seite die Output-Ports präsentiert. In der Mitte des Rechtecks wird der Name der Component dargestellt. Daten-Ports werden als Dreiecke, Programmfluss-Ports als Rechtecke visualisiert. Der Benutzer kann die Components beliebig durch Draggen (Klicken und Ziehen) mit der Maus positionieren. Ändert sich die Position einer Component, wird der Position Manager darüber informiert. Umgekehrt reagiert die Visualisierung auf Positionsänderungen von Components, die z. B. von anderen Views verursacht wurden. Sobald sich die Maus über eine Component bewegt, werden alle registrierten ComponentSelectionListener darüber informiert. Ein Rechtsklick auf eine Component lässt ein Aufklappmenü (engl. Popup Menu) erscheinen, das einen Eintrag zum Löschen der Component anbietet. Wählt man diesen Eintrag aus, wird die Methode deleteComponent des CM aufgerufen. Umgekehrt reagiert die View – nachdem sie im OSGi-Framework als ComponentFactorySelectionListener registriert ist – darauf, wenn in einer anderen View eine CF selektiert wurde. Tritt so ein Fall ein, werden alle Components, die von dieser CF erzeugt wurden, für zwei Sekunden rot hinterlegt. Diese Component-Link-View unterstützt außerdem die CF-Eigenschaft torem.cf.isHandlingValue. Das bedeutet, das Components einer CF, die diese Eigenschaft definiert hat, eine zusätzliche Information darstellen können. Als Wert der Eigenschaft muss die ID eines Ports angeben werden. Ändert sich der Wert des Ports mit der gegebenen ID, wird dieser Wert in der Component-Visualisierung dargestellt, wie Abb. 6.11 verdeutlichen soll. Ports Von der Visualisierung her sind Ports an die Components gebunden. Ändert sich die Position einer Component, bewegen sich alle dazugehörigen Ports mit. Der Name der Ports wird als Tooltip angezeigt und wird sichtbar, sobald die Maus über einen Port bewegt wird. Bei Daten-Ports werden zusätzlich der aktuelle Wert, der einem Port zugewiesen ist, sowie der Datentyp des Ports, in den Tooltip integriert (s. Abb. 6.12). Um Abseits von Links den Ports Werte zuweisen zu können oder Programmfluss-Ports zu aktivieren, genügt ein Doppelklick auf den gewünschten Port. Ist es ein Programmfluss-Port, erscheint eine Schaltfläche (engl. Button) mit dem Titel Ausführen“ (s. Abb. 6.13). Durch Drücken der Escape” KAPITEL 6. UMSETZUNG 68 Abbildung 6.12: Die Visualisierung des Port-Zustandes wird über einen Tooltip gelöst. Der Name des Ports wird zusammen mit dem Datentyp und dem aktuellen Wert dargestellt. Abbildung 6.13: Mit einem Klick auf den Button wird der dahinterliegende Port aktiviert. Abbildung 6.14: Das Textfeld zum Setzen des Wertes eines Ports. Taste verschwindet der Button, ohne eine Aktion auszuführen. Betätigt man den Button aber, so wird die Methode setPortValue des CM mit dem Wert null aufgerufen. Handelt es sich bei dem Port jedoch um einen Daten-Port, erscheint nach dem Doppelklick ein Textfeld, in das ein Wert eingegeben werden kann (s. Abb. 6.14). Auch diese Aktion kann durch Drücken der Escape-Taste abgebrochen werden. Ist bereits ein Wert für den Port definiert, wird dieser Wert als Voreinstellung übernommen. Der eingegebene String wird, entsprechend dem Datentyp des Ports, umgewandelt und ebenfalls über die Methode setPortValue dem CM übergeben. Die Umwandlung wird mit Hilfe der Klasse ConversionHelper, die im Component-Bundle implementiert wird, durchgeführt. Über Port-Wertänderungen wird dieses Bundle vom GUI-Basis-Bundle informiert. Ein solches Ereignis führt dazu, dass der Tooltip der zugehörigen Port-Visualisierung aktualisiert wird. Links Die Darstellung der Links erfolgt durch Zeichnen von Linien, die die betroffenen Ports miteinander verbinden. Ändert sich die Position der zu verbindenden Ports, wird auch die Link-Visualisierung dementsprechend aktuali- KAPITEL 6. UMSETZUNG 69 Abbildung 6.15: Der Textbereich zeigt den Quellcode des dem gelb markierten Link zugeordneten Converters an. siert. Je nach Zustand des Links wird dieser mit unterschiedlichen Farben visualisiert: • Schwarz: Programmfluss-Links sowie Daten-Links, die keiner Konvertierung bedürfen, werden schwarz gezeichnet. • Grün: Wird ein Programmfluss-Link aktiviert, oder transportiert ein Datenfluss-Link ein Datum, wird dies der Visualisierung über das GUIBasis-Bundle mitgeteilt, welche daraufhin den Link für 500 Millisekunden grün zeichnet. So kann der Benutzer sehen, wann ein Link etwas zu tun hat. • Blau: Enthält ein Daten-Link einen Converter, so wird er blau dargestellt. Zusätzlich wird der Quellcode der convert-Methode in Form einer javax.swing.JTextArea (dt. Textbereich) angezeigt, wenn die Maus über einen Link positioniert wird (s. Abb. 6.15). • Rot: Ungültige Daten-Links – das sind jene, deren Port-Datentypen zueinander inkompatibel sind und denen noch kein geeigneter Converter zugewiesen wurde – werden rot dargestellt. • Gelb: Wird die Maus über einen Link bewegt, wird dieser gelb markiert. Um einen Link zu löschen, genügt ein Rechtsklick, wenn sich die Maus über dem Link befindet. Aus dem erscheinenden Popup-Menü kann der Eintrag Löschen“ verwendet werden, um die Methode removeLink des LM auf” zurufen. Das Zuweisen eines Converters zu einem Link funktioniert ebenfalls über das Popup-Menü und den Eintrag Bearbeiten“, der allerdings nur bei ” Daten-Links zur Verfügung steht. Die Auswahl dieses Eintrags führt dazu, dass alle registrierten ConverterSelectionListener darüber informiert werden, dass ein Link bearbeitet werden soll. Diese View verwendet die selbe Methode der Lokalisierung wie die Component-Factories-View in Abs. 6.2.2: Die Sprach-Datenbank-Dateien werden über Fragment-Bundles definiert. KAPITEL 6. UMSETZUNG 6.2.4 70 Converters Java-Package: net.bebedizo.torem.gui.converter.standard Die Visualisierung der Converters ist die erste, die in der Kategorie All ” Purpose Visualizers“ und damit im rechten oberen Bereich des JFrames zur Anzeige gebracht wird. Vom GUI-Basis-Bundle wird sie informiert, sobald Converters erstellt oder gelöscht werden. Die Darstellung ist in vier Bereiche unterteilt: Eine Titelleiste oben, in der der Name für ein Converter-Script vergeben werden kann, eine Liste links, die alle verfügbaren Converters auflistet und ein Textbereich rechts, der das Schreiben eigener Converters erlaubt. In diesem Textbereich kann auch der Quelltext der convert-Methode des in der Liste ausgewählten Converters angezeigt werden. Unten befinden sich noch Buttons, die beim Betätigen die Kommunikation zum Converter Manager übernehmen. Die Converter-View unterscheidet zwischen zwei Stati, in denen sie sich befinden kann. Im Link-gebundenen Status betreffen Converter-Änderungen einen bestimmten Link, im Link-ungebundenen Status können Converter ganz allgemein manipuliert werden, ohne die den Links zugeteilten Converters zu verändern. Link-ungebundener Status In diesem Status werden im unteren Bereich drei Buttons angezeigt, die dem Benutzer die Möglichkeit geben, Befehle an den Converter Manager abzusetzen: • Neu: Dieser Button dient zum Erzeugen eines neuen Converters. Das Betätigen dieses Buttons öffnet einen Dialog, in dem der Benutzer aufgefordert wird, Ausgangs- und Ziel-Datentyp festzulegen. Danach erscheint im Textbereich ein Quellcode-Fragment, dass als Orientierungshilfe dienen soll und, sofern das möglich ist, die Konvertierung des Ausgangs-Datentyps in einen primitiven Datentyp vornimmt, um Berechnungen durchführen zu können. Soll bspw. eine Konvertierung von Boolean zu Integer vorgenommen werden, wird der Java-Quellcode boolean b = (Boolean)value; in das Textfeld geschrieben. Der Benutzer kann nun seinen Konvertierungs-Algorithmus in Java-Syntax angeben (s. Abb. 6.16). • Speichern: Mit diesem Button wird der im Textbereich definierte Quellcode dem Converter Manager gemeinsam mit den ausgewählten Datentypen sowie dem Inhalt des oberen Textfelds als Name übergeben (s. Abs. 6.1.3). Zunächst wird geprüft, ob der Quellcode kompilierbar ist. Ist er das nicht, wird dem Benutzer die Zeile eines Syntax-Fehlers angegeben und der Hintergrund des Textbereiches blinkt kurz rot auf. KAPITEL 6. UMSETZUNG 71 Abbildung 6.16: Die Converter-View im Link-ungebundenen Status. Links ist die Liste der verfügbaren Converters, rechts das derzeit entwickelte Script und oben kann ein Name für das Script gewählt werden. Unten befinden sich die Buttons zum Absetzen der Steuerbefehle. Ist der Quelltext kompilierbar, wird er der Liste der verfügbaren Converters über ein vom Converter Manager generiertes ConverterEvent hinzugefügt. • Löschen: Über diesen Button wird der aktuell in der Liste ausgewählte Converter vom Converter Manager gelöscht und steht nicht länger zur Verfügung. Sollten bereits Links diesen Converter als ih” ren“ Converter benutzen, so sind sie vom Löschvorgang nicht betroffen. Bei diesen Links bleibt der Converter erhalten. Link-gebundener Status Nachdem das Bundle als ConverterSelectionListener im OSGi-Framework registriert ist, wird gelegentlich die Methode converterSelected aufgerufen. Das ist z. B. der Fall, wenn in der Component-Link-View der Eintrag Bear” beiten“ aus dem Popoup-Menü eines Daten-Links ausgewählt wird. Wird diese Methode aufgerufen, so wechselt die Converter-View in den Linkgebundenen Status. Visuell macht sich das dadurch bemerkbar, dass die View rot umrandet wird, und dass nun vier Buttons zur Verfügung stehen, wie in Abb. 6.17 dargestellt. Aktionen die in diesem Status durchgeführt werden, betreffen den Link, der hinter der ID steckt, die in der Methode converterSelected übergeben wurde. Nachdem der Converter in Bezug zu einem Link steht, sind Ausgangsund Ziel-Datentyp bereits vorgegeben. Um dem Benutzer die Zuweisung eines existierenden Converters zu vereinfachen, wird die Liste der Converters KAPITEL 6. UMSETZUNG 72 Abbildung 6.17: Die Converter-View, diesmal im Link-gebundenen Status. Deutlich sichtbar ist die rote Umrandung, mit der dieser Status markiert wird. Die Liste der Converters ist auf einen einzigen passenden Eintrag geschrumpft. auf jene beschränkt, die kompatibel zu den benötigten Datentypen sind. Alle anderen Converters werden ausgeblendet. Der Benutzer hat nun die Möglichkeit, einen Converter aus der Liste auszuwählen, oder einen eigenen zu definieren, wobei die selben Regeln wie zuvor erklärt gelten. Die vier Buttons haben folgende Bedeutung: • Neu: Dieser Button hat die selbe Funktionalität wie der Neu-Button im ungebundenen Status, jedoch mit dem Unterschied, dass der Benutzer die Datentypen nicht festlegen muss, da diese ja durch die beiden gekoppelten Ports des Links definiert sind. • Übernehmen: Übernehmen sorgt dafür, dass der durch den im Textbereich vorhandenen Quelltext definierte Converter dem Link zugeordnet wird. Entspricht das Script einem bereits existierenden Converter, so wird eine neue Instanz dessen angelegt und dem Link zugeordnet. Existiert kein Converter mit dem dargestellten Quelltext, so wird nach dem selben Prinzip wie beim Speichern eines ungebundenen Scripts vorgegangen. Ist die Kompilation erfolgreich, wird eine Instanz des neu generierten Converters dem Link zugeordnet. • Löschen: Mit dem Löschen-Button wird dem Link der Wert null als Converter gesetzt. Das ist gleichbedeutend mit dem Löschen des Converters aus dem Link. • Abbrechen: Dieser Button beendet den Link-gebundenen Status zugunsten des Link-ungebundenen. Die rote Umrandung verschwindet, und es sind wieder die drei Buttons, die zuvor beschrieben wurden, zu sehen. KAPITEL 6. UMSETZUNG 6.3 73 Erweiterungen Die in diesem Abschnitt beschriebenen Bundles stellen nützliche Erweiterungen in Form von Zusatz-Bundles dar. Teilweise werden CFs und Converters (also Erweiterungen in der Model-Schicht) dem OSGi-Framework hinzugefügt, teilweise werden neue View-Implementierungen definiert. 6.3.1 Standard Ingredients Java-Package: net.bebedizo.torem.standardingredients Die Standard Ingredients sind einerseits eine Sammlung an Component Factories, die Components für wichtige Grundfunktionen zur Verfügung stellen (If-Anweisungen, Schleifen, Arrays, usw.) und andererseits eine Sammlung vieler Converters, die Standard-Konvertierungen zwischen den primitiven Datentypen und String anbieten. Ein eigenes GUI-Bundle (s. unten) sorgt für die geeignete Darstellung der CFs. Component Factories Java-Package: net.bebedizo.torem.standardingredients.component Für folgende Components werden CFs definiert (in Klammern der Name in der deutschen Lokalisierung) – einen visuellen Index bietet Abb. 6.18: • Addition (Addition): Addiert zwei double-Werte miteinander. • Array (Array): Repräsentiert ein String-Array. Die Definition des Arrays erfolgt über eine XML-Zeichenkette im Format: <tl><element value="Wert1"/>...<element value="Wert2"/></tl>. • ChangeDetection (Änderungs-Detektion): Nimmt nacheinander Werte auf und liefert true oder false, je nachdem ob sich zwei aufeinanderfolgende Werte unterscheiden oder nicht (wird über die equals-Methode entschieden). • Delay (Verzögerung): Verzögert den Programmfluss um die angegebene Zahl von Millisekunden. Definiert torem.cf.isHandlingValue zur Anzeige der Millisekunden. • Equality (Gleichheit): Testet die beiden Eingangswerte auf Gleichheit mittels der equals-Methode. • For (For-Schleife): Repräsentiert eine for-Schleife. Es können der Startwert, der Endwert und die Schrittweite der Laufvariablen als Ganzzahl-Werte angegeben werden. Der Schleife-Aus-POP dient dem Definieren eines Programmflusses, der innerhalb eines Schleifendurchlaufes ausgeführt werden soll und sollte im Schleife-Ein-PIP münden. KAPITEL 6. UMSETZUNG 74 Ist die definierte Anzahl an Schleifendurchläufen durchgeführt worden, wird der Beendet-POP aktiviert. • If (Wenn): Diese Component stellt eine if-Anweisung dar. Wenn der Eingangswert true ist, wird der obere POP aktiviert, ansonsten der untere. • Inversion (Umkehrung): Invertiert den (boolschen) Eingangswert. • Logging (Logging): Dient der Protokollierung eines Wertes. Der Wert wird mittels der toString-Methode in einen String konvertiert. Diese Component definiert keine Programmfluss-Ports – jeder Wert, der den DIP erreicht, wird protokolliert. • RepeatedInvocation (Wiederholter Aufruf): Geeignet als Quelle eines Programmflusses. Nach der Aktivierung des Start-PIPs wird der POP alle Pause Millisekunden aktiviert, bis der Stop-PIP zum Stoppen verwendet wird. Definiert die Eigenschaft torem.cf.isHandlingValue für die Anzeige der Millisekunden, die zwischen den POP-Aktivierungen gewartet wird. • Storage (Speicher): Speichert einen Wert vom Typ Object zwischen. Definiert die torem.cf.isHandlingValue-Eigenschaft für den zwischengespeicherten Wert. • Switch (Auswahl): Simuliert eine switch-Anweisung, die auf IntegerZahlen für die Fallunterscheidungen basiert. Die Anzahl der Fälle ist derzeit mit sechs begrenzt – eine wünschenswerte Weiterentwicklung wäre es, die Anzahl beim Erstellen der Component bestimmen zu können. Zusätzlich gibt es noch den Fall, der eintritt, wenn keiner der sechs zuvor getesteten Fälle eintritt. Die Output-Ports sind alle POPs, da es die Aufgabe der switch-Anweisung ist, den Programmfluss zu teilen. • While (While-Schleife): Eine while-Schleife, die so lange läuft, bis der Eingangsparameter auf false gesetzt wird. Der Schleife-Aus-POP sollte nachdem die notwendigen Components dazwischengeschalten wurden, im Betreten-PIP münden, um die Schleife zu definieren. Sämtliche Components leiten sich von der Klasse AbstractComponent ab, die im Component-Bundle definiert wird. Um diese Components in Torem nutzen zu können, müssen CFs registriert werden, die für die Erzeugung der Components sorgen. Dazu dient in diesem Bundle jeweils eine Instanz der Klasse SampleComponentFactory, die von ComponentFactoryPrototype abgeleitet ist. Bei den CFs dieses Bundles wird die Eigenschaft torem.cf.type auf Default gesetzt, was von der zugehörigen View genutzt wird, um diese CFs zu erkennen. Stellvertretend für die anderen Components wird in Anhang A die Entwicklung der If-Component näher erläutert. KAPITEL 6. UMSETZUNG 75 Abbildung 6.18: Die 13 Components der Standard Ingredients. Converters Java-Package: net.bebedizo.torem.standardingredients.converter Insgesamt werden 65 Converters implementiert und im OSGi-Framework registriert. Diese dienen als Standard-Implementierungen für die Konvertierungen zwischen den primitiven Datentypen und Strings. Auf eine Auflistung der Converters wird an dieser Stelle aufgrund der großen Anzahl verzichtet. Da der Quelltext der convert-Methoden jeweils nur eine Zeile lang ist hält sich die Komplexität der Converters in Grenzen. View Java-Package: net.bebedizo.torem.gui.componentfactory.standard Diese View gleicht der Component-Factories-View aus Abs. 6.2.2, bis auf die Anzahl der dargestellten CFs. Es werden nur jene CFs dargestellt, die als Wert der torem.cf.type-Eigenschaft Default definiert haben. Im Gegensatz dazu zeigt die Component-Factories-View alle verfügbaren CFs an. Diese View verwendet ebenfalls Fragment-Bundles (s. Abs. 6.2.2), um die Mehrsprachigkeit umzusetzen. KAPITEL 6. UMSETZUNG 6.3.2 76 UPnP Die Einbindung des UPnP-Protokolls erfolgt über zwei Bundles, wobei eines das Definieren und Registrieren der CFs übernimmt (Model) und das andere für eine übersichtliche Auswahlliste der UPnP-CFs sorgt (View). Component Factories Java-Package: net.bebedizo.osgi.service.upnp.controlpoint In [23] wird die Einbindung von UPnP-Geräten ins OSGi-Framework spezifiziert. Für diese Spezifikation gibt es eine Open Source Implementierung, die unter der LGPL lizensiert ist: der Domoware UPnP Base Driver7 . Er verwendet die Java-Variante von Cyberlink als UPnP-Bibliothek. Dieses Bundle repräsentiert einen UPnP Control Point, der vom UPnP Base Driver über entdeckte sowie sich abmeldende UPnP-Geräte informiert wird. Ein Geräte Manager (engl. Device Manager) sorgt für das Einbinden der Geräte in Torem, indem für jede Aktion und jede Statusvariable eines Gerätes eine CF erstellt und im Framework registriert wird. Der Ablauf des Device Managers, wenn ein UPnP-Gerät registriert wird, sieht folgendermaßen aus: WIEDERHOLE für jeden UPnP - Dienst innerhalb des Gerätes : WIEDERHOLE für jede UPnP - Aktion innerhalb des aktuellen Dienstes : erstelle eine Aktions - CF registriere die Aktions - CF im OSGi - Framework WIEDERHOLE_ENDE WIEDERHOLE für jede UPnP - Statusvariable ( SV ) innerhalb des aktuellen Dienstes : WENN die SV Statusänderungen an Abonnenten verschickt DANN erstelle eine SV - CF registriere die SV - CF als Abonnent bei der SV registriere die SV - CF im OSGi - Framework WENN_ENDE WIEDERHOLE_ENDE WIEDERHOLE_ENDE Meldet sich ein Gerät ab, wird folgender Algorithmus angewandt: WIEDERHOLE für jede Aktions - CF die unter diesem Gerät registriert wurde : melde diese Aktions - CF vom OSGi - Framework ab WIEDERHOLE_ENDE WIEDERHOLE für jede SV - CF die unter diesem Gerät registriert wurde : 7 http://domoware.isti.cnr.it/documentation.html KAPITEL 6. UMSETZUNG 77 melde diese SV - CF vom OSGi - Framework ab kündige das Abonnement der SV - CF bei der SV WIEDERHOLE_ENDE Um der Gleichheits-Ähnlichkeits-Problematik beizukommen, werden folgende Eigenschaften von UPnP-Geräten als Similarites (wenn diese Werte übereinstimmen, gelten zwei CFs als ähnlich) in den CFs gespeichert: • Die ID des Dienstes dem die CF angehört und • der Gerätetyp des zugehörigen Gerätes. Folgende Eigenschaften werden als Equalities (wenn diese Werte und die Similarities übereinstimmen, gelten zwei CFs als gleich) gespeichert: • Die ID, • der Friendly Name“ (ein kurzer menschenlesbarer String zur Beschrei” bung des Gerätes), • der Modellname, • die Seriennummer, • die Modellnummer und • die Modellbeschreibung des Gerätes. UPnP-Aktions-Components werden gebildet, indem jeder Eingangsparameter der Aktion in einen DIP und jeder Ausgangsparameter in einen DOP abgebildet wird. Wird einem der DIPs ein Wert zugewiesen, so wird dieser in einem java.util.Dictionary-Objekt in der Component gespeichert, das später zum Ausführen der Aktion verwendet wird. Zusätzlich wird ein PIP definiert, dessen Aktivierung intern so umgesetzt wird, dass die invokeMethode der UPnP-Aktion aufgerufen wird – mit dem zuvor angesprochenen Dictionary-Objekt als Parameter. Dieser Methodenaufruf wird vom UPnP Base Driver mittels der Cyberlink-Bibliothek in einen UPnP-kompatiblen HTTP-Request umgewandelt und an das UPnP-Gerät gesandt. Die Antwort des UPnP-Gerätes wird ebenfalls als Dictionary-Objekt an die UPnPAktions-Component zurückgeliefert, wo dessen Inhalt an die DOPs weitergegeben wird. Abschließend wird der ebenfalls erstellte POP aktiviert, um den Programmfluss weiterlaufen zu lassen. Abb. 6.19 zeigt den schematischen Aufbau einer UPnP-Aktions-Component. UPnP-Statusvariablen-Components definieren einen DOP und einen POP. Wird die zugehörige CF über eine Statusänderung der entsprechenden Statusvariable informiert, leitet die CF dieses Ereignis an alle instanzierten Components weiter. Innerhalb der Component wird zunächst dem DOP KAPITEL 6. UMSETZUNG 78 Abbildung 6.19: Die UPnP-Aktions-Component setzt die Eingansparameter zu einer SOAP-Nachricht zusammen und schickt diese an das UPnPGerät. Dessen SOAP-Antwort wird anschließend geparst und an die DOPs weitergeleitet, bevor der POP aktiviert wird. Abbildung 6.20: Das vom UPnP-Gerät geschickte Ereignis im SOAPFormat wird geparst und der Wert des Ereignisses dem DOP übermittelt. Danach wird der POP aktiviert. der neue Wert zugewiesen und anschließend der POP aktiviert. Die UPnPStatusvariablen-Components sind reine Ereignis-Components und besitzen keine Input-Ports. Der interne Aufbau einer UPnP-Statusvariablen-Component sieht wie in Abb. 6.20 gezeigt aus. View Java-Package: net.bebedizo.torem.gui.componentfactory.upnp Um die verfügbaren UPnP-Components – sowohl Aktionen als auch Statusvariablen – übersichtlich anzeigen zu können, wird eine Baumstruktur als Darstellungsform gewählt, da diese dem hierarchischen Aufbau eines UPnPGerätes entspricht. Jedes UPnP-Gerät besteht aus drei Ebenen: In Ebene 0 befindet sich das Gerät selbst, Ebene 1 stellt die Dienste innerhab eines KAPITEL 6. UMSETZUNG 79 Abbildung 6.21: Die UPnP-Geräte werden als Wurzel-Knoten untereinander angeordnet und sind blau eingefärbt. Die untergeordneten UPnP-Dienste sind rot und eingerückt visualisiert und beinhalten UPnP-Aktionen (grau) und UPnP-Statusvariablen (grün). Die weißen Dreiecke dienen dem Falten der Hierarchie-Ebenen, um die Übersichtlichkeit zu erhöhen. Der Dienst SwitchPower“ ist bspw. gefalten. ” Gerätes dar und in Ebene 3 folgen die Aktionen und Statusvariablen der Dienste (s. Abb. 6.21). Die entdeckten UPnP-Geräte werden untereinander gelistet und bieten Schaltflächen zum Minimieren der einzelnen Hierarchie-Ebenen an, um die Übersichtlichkeit zu gewährleisten. Ein Doppelklick auf eine Aktion oder Statusvariable sorgt für die Instanzierung einer entsprechenden Component über den CM. Rechtsklicken auf eine CF schickt ein Ereignis an alle registrierten ComponentFactorySelectionListeners. Werden CFs als offline markiert, werden sie mit grauem Hintergrund dargestellt. Dieses Ausgrauen“ zieht sich bis in die oberste Hierarchieebene ” (s. Abb. 6.22). Sobald die CFs wieder online markiert werden, bekommt der Baum seine Farbe zurück. Nachdem diese View als ComponentSelectionListener registriert ist, erhält sie Benachrichtigungen, wenn eine Component selektiert wurde. In so einem Fall wird die zugehörige CF sowie die übergeordneten Elemente (Dienst und Gerät) für 1000 Millisekunden rot eingefärbt (s. Abb. 6.23). Das ermöglicht das leichtere Zuordnen von Components zu CFs (praktisch für den Fall, dass zwei UPnP-Aktionen den gleich Namen haben, aber von unterschiedlichen UPnP-Geräten zur Verfügung gestellt werden. 6.3.3 Remote GUI Das GUI soll nicht nur im selben OSGi-Framework wie die Kern-Bundles lauffähig sein, sondern auch auf einem entfernten Host. Damit ist es mög- KAPITEL 6. UMSETZUNG 80 Abbildung 6.22: Das UPnP-Geräte Light (MC-NB19)“ steht nicht länger ” zur Verfügung. Die zugehörigen UPnP-CFs, die bereits Components instanziert haben, werden grau dargestellt. Abbildung 6.23: Wird eine Component markiert, werden die zugehörigen UPnP-CFs rot eingefärbt. Somit kann schnell erkannt werden, welche Component welchem UPnP-Gerät zuzuordnen ist. lich, einen Server, der sich um die Umsetzung des definierten Mappings kümmert, auf einem Host laufen zu haben, zu dem sich eine oder mehrere GUI-Applikationen verbinden, die auf anderen Host ausgeführt werden, um das Mapping zu modifizieren. Die folgenden drei Bundles beschreiben die Implementierung einer Server-Client-Architektur, die es erlaubt, die zuvor beschriebenen View-Bundles ohne Änderungen sowohl lokal als auch entfernt laufend zu verwenden. KAPITEL 6. UMSETZUNG 81 Abbildung 6.24: Instanzdiagramm des Netzwerk-Bundles. Netzwerk-Infrastruktur Java-Package: net.bebedizo.util.connection Dieses Bundle implementiert Klassen die die Netzwerk-Kommunikation über TCP8 /IP ermöglichen. Dazu zählt erstens ein Server, der auf einem Port horcht und hereinkommende Verbindungen annimmt, zweitens eine Connection-Klasse, die die Kommunikation zwischen Server und Client übernimmt und drittens eine Nachrichten-Klasse, die die zu übermittelnden Nachrichten spezifiziert. Kommunikation Der Server wird mit Hilfe des java.net.ServerSocket in der Klasse TcpConnectionListener implementiert, und legt für jede angenommene Verbindung ein Objekt der java.net.Socket-basierten Kommunikationsklasse TcpObjectConnection an, das jeweils für die Netzwerkkommunikation in einem isolierten Thread läuft. Jede der erstellten Connections leitet eingehende Nachrichten, die den Beginn einer Kommunikation darstellen, an den ihr zugewiesenen NotificationProcessor weiter. Nachrichten, die Antworten auf eine zuvor gestellte Anfrage sind, werden an den ResponseHandler weitergeleitet (s. Abb. 6.24). Nachrichten Die Nachrichten werden in Form von serialisierten Objekten zwischen Server und Client ausgetauscht. Ein solches Nachrichten-Objekt besteht aus einer fortlaufenden ID, die dazu dient, Anfragen und Antworten über das Netzwerk zuordnen zu können, einer Integer-Zahl, die den Typ der Nachricht definiert und dem Objekt, das übermittelt werden soll. Die Klasse net.bebedizo.util.connection.message.GenericMessage implementiert die8 Transport Control Protocol. Details dazu finden sich in [13] KAPITEL 6. UMSETZUNG 82 ses Nachrichten-Objekt für Torem. In dieser Klasse sind auch alle 33 für Torem notwendigen Nachrichten-Typen definiert. Server Java-Package: net.bebedizo.torem.network.server Das Server-Bundle startet einen TcpConnectionListener, der auf eingehende Verbindungen wartet und entsprechend der oben gezeigten Vorgangsweise für eingehende Verbindungen jeweils eine TcpObjectConnection erzeugt. Als NotificationProcessor dient die Klasse AllEventListener, in der die gesamte Logik des Server-Bundles implementiert ist. Um die Anfragen von Clients durchführen zu können, hält sich der AllEventListener Referenzen zu den vier Managern der Kern-Bundles, die er über das Dienst-System des OSGi-Frameworks erhält. Ist mit einer Anfrage das Ausführen einer Manager-Methode verbunden, die einen Rückgabewert liefert, so wird der Rückgabewert als Nachricht an den Client zurück-übermittelt. Wird jedoch eine Methode ohne Rückgabewert aufgerufen, erhält der Client darüber keine Benachrichtigung. Um die Clients über Model-Änderungen auf dem Laufenden zu halten, registriert sich der AllEventListener als Listener für alle in den KernBundles definierten Ereignisse. Tritt ein Ereignis auf, teilt er dies allen verbundenen Clients mit. Für das Server-seitige OSGi-Framework simuliert der Server damit im Grunde eine View-Implementierung, die über jedes Ereignis informiert werden möchte. Client Java-Package: net.bebedizo.torem.network.client Der Client verbindet sich über IP und Port zu einem Server und kann diesem die in der GenericMessage-Klasse definierten Nachrichten schicken, die auf der Server-Seite in Methoden-Aufrufe von Manager-Klassen umgesetzt werden. Die Logik des Clients konzentriert sich in einer einzigen Klasse, dem AllManagersProvider. Diese vereint alle vier Manager der Kern-Bundles und registriert sich dementsprechend im OSGi-Framework. Anderen Bundles im Client-seitigen OSGi-Framework wird das Gefühl vermittelt, direkt mit den Managern zu kommunizieren. Wird eine Manager-Methode des AllManagersProvider aufgerufen, wandelt dieser die damit verbundene Anfrage in eine Nachricht um und sendet diese an den Server. Definiert die Methode einen Rückgabewert, wartet der AllManagersProvider auf die entsprechende Antwort vom Server und gibt den darin enthaltenen Wert zurück, ansonsten wird die Methode sofort nach dem Absetzen der Nachricht beendet. KAPITEL 6. UMSETZUNG 83 Abbildung 6.25: Der Nachrichtenfluss in der Server-Client-Architektur. Ereignis-Nachrichten, die vom Server an die Clients versandt werden, leitet der AllManagersProvider an die im Client-seitigen OSGi-Framework registrierten Event Listeners weiter. Werden mehrere GUIs gleichzeitig betrieben, so wird die Component-Link-View aufgrund des Position Managers auch über Netzwerkgrenzen hinweg konsistent gehalten. Abb. 6.25 zeigt den Nachrichtenfluss dieser Server-Client-Architektur. Der hier gewählte Ansatz ermöglicht auch einen Mischbetrieb, in dem es sowohl ein lokales GUI am Server, als auch entfernte GUIs gibt. 6.4 Szenario Abschließend soll ein kleines Szenario umgesetzt werden, um die Brauchbarkeit des entwickelten Prototyps im laufenden Betrieb zu testen. Eine mit Drucksensoren ausgestattete Holzplatte soll dazu dienen, ein MedienAbspielgerät (engl. Media Player) zu steuern. Drückt man mit dem Finger leicht auf einen der Drucksensoren, soll eine gewisse Funktion (das Abspielen starten, stoppen, pausieren, ...) des Media Players ausgeführt werden. Abb. 6.26 zeigt den schematischen Aufbau dieser Installation. Die Platte mit den Sensoren ist über TCP/IP ansprechbar, und so wurde für das Auslesen der aktuellen Druckwerte ein UPnP-Gerät entwickelt, dass unter anderem die UPnP-Aktion GetSensor anbietet, über welche die Werte der Drucksensoren als String bereitgestellt werden. Intern kommuniziert das UPnP-Gerät mit der Sensor-Platte über ein proprietäres Protokoll. Der Media Player wurde mit dem Java Media Framework 9 (JMF) ebenfalls als UPnP-Gerät entwickelt und bietet entsprechende UPnP-Aktionen für die an ihm durchführbaren Funktionen an. Zur Umsetzung mit Torem wurden Components der Standard Ingredients (s. Abs. 6.3.1) mit den UPnP-Aktionen gekoppelt. Abb. 6.27 zeigt 9 http://java.sun.com/products/java-media/jmf/ KAPITEL 6. UMSETZUNG 84 Abbildung 6.26: Eine Platte mit neun Drucksensoren ist über TCP/IP ansprechbar – ein entsprechendes UPnP-Gerät wurde entwickelt, um die Kommunikation mit der Sensorplatte zu abstrahieren. Torem liest die SensorWerte aus, ordnet sie den UPnP-Aktionen des Media Players zu und führt beim Drücken eines Sensors die zugehörige Aktion aus. das entworfene Mapping in der Component-Link-View (s. Abs. 6.2.3): Die UPnP-Aktion GetSensor wird alle 200 ms aufgerufen, und die zurückgegebenen Sensorwerte werden in einem Array gespeichert. Nun werden die ersten sechs Einträge benutzt, um die sechs Funktionen des Media Players anzusteuern. Ein Drucksensor gilt als gedrückt“, wenn der Sensorwert unter ” 2000 fällt (im ungedrückten Zustand bewegen sich die Werte im Bereich von 3500). Für jeden der Sensorwerte wird ein eigener Programmfluss definiert, der das Ansteuern der zugewiesenen UPnP-Aktion übernimmt. Da die Pause-Funktion des Media Players zwischen Pause und Play wechselt, je nachdem in welchem Zustand der Media Player gerade ist, wird mit ein paar zusätzlichen Components sichergestellt, dass ein zu langes Drücken des Pause-Drucksensors nicht mehrfach an den Media Player gesandt wird. Stattdessen müssen zumindest 2500 Millisekunden vergehen, bis der Wert des Drucksensors wieder beachtet wird. Die XML-Datei für dieses Szenario kann auf der CD unter dem Pfad /samples/sensorPlayer.xml eingesehen werden. KAPITEL 6. UMSETZUNG Abbildung 6.27: Die Umsetzung des oben genannten Szenarios. Bis auf die Bedingung, ab welchem Wert ein Sensor als gedrückt gilt, musste keine Zeile Code selbst programmiert werden. Die Bedingung wurde mittels der Converter-View erstellt un dem Daten-Link zwischen Array und Wenn (rechts oben) zugewiesen. 85 Kapitel 7 Fazit An dieser Stelle werden die Erfahrungen und Erkenntnisse, die im Laufe dieser Arbeit gewonnen wurden, sowie Vorschläge zur Weiterentwicklung angeführt. 7.1 Ergebnisse Der entwickelte Prototyp, Torem, konnte bei der Implementierung eines einfachen Szenarios (s. Abs. 6.4) seine Brauchbarkeit unter Beweis stellen. In kurzer Zeit wurden die Components und Links so angeordnet, dass sie die gewünschte Funktionalität umsetzten. Wie bei anderen grafischen Programmierumgebungen, ist jedoch eine gewisse Einarbeitungszeit nicht von der Hand zu weisen, die benötigt wird, um mit der Funktionsweise der Components zurechtzukommen. Mit Torem konnte auch gezeigt werden, dass sich das OSGi-Framework für die Entwicklung einer dynamischen Applikation hervorragend eignet, und dass durch die Aufteilung der Programmlogik in mehrere Bundles eine saubere Trennung der verschiedenen Logik-Bausteine gefördert wird. Durch die Einbindung von UPnP eröffnet sich mit einem Schlag eine große Palette an Geräten, die von Torem aus angesteuert werden können. Das interessante an UPnP ist, dass es sich nicht um eine Java-spezifische Technologie handelt, sondern dass damit ein Sprach-unabhängiges Protokoll gewonnen werden konnte. Die Einbettung von UPnP dient in dieser Arbeit als Beispiel für die Einbindung eines von Java nicht direkt unterstützten Protokolls. Es war eine durchaus fordernde Aufgabe, ein Model zu erarbeiten, dass die Integration möglichst vieler verschiedener Technologien ermöglicht. Die Entscheidung fiel zuguterletzt auf die Entwicklung eines sehr lockeren Komponenten-Begriffs, der jeder Komponenten-Implementierung sehr viel Freiraum lässt, was das Umgehen mit Port-Wertzuweisungen angeht. Es wäre möglich, auf Programmfluss-Ports komplett zu verzichten und nur mit Da86 KAPITEL 7. FAZIT 87 ten-Kopplungen zu arbeiten. Die Unterscheidung zwischen Programm- und Datenfluss erweist sich allerdings als praktisch, da so eine bessere Steuerung der Komponenten-Zugriffe erreicht wird. Der aktuelle Entwicklungsstand von Torem ist in einem zufriedenstellenden Stadium. Torem läuft zuverlässig auch über mehrere Tage hindurch und könnte daher auch für längerfristige Installationen eingesetzt werden. Dabei bietet sich der Server-Client-Betrieb an, in dem Clients nur dann gestartet werden, wenn Änderungen vorgenommen werden müssen, oder um den aktuellen Status der Components und Links einzusehen. Der Server läuft ohne graphische Oberfläche unauffällig im Hintergrund und erledigt die Durchführung der Kopplungen. 7.2 Ausblick Durch die Erweiterbarkeit von Torem ist es möglich, weitere Technologien und Protokolle zu integrieren. Die Einbindung von Jini-fähigen Geräten sollte bspw. recht einfach möglich sein, da Torem eine Java-basierte Applikation ist. Andere Protokolle, wie der European Installation Bus1 (EIB) bedürfen evtl. eines höheren Aufwandes, eine Integration sollte dennoch möglich sein: Die indirekte Einbindung von EIB-Geräten wurde durch einen UPnP-EIB Treiber bereits ermöglicht, der die Geräte einer EIB-Installation in das UPnP-Netzwerk exportiert. Die Nutzung über UPnP wäre also bereits möglich. Der UPnP-EIB-Treiber liegt als OSGi-Bundle auf der CD im Verzeichnis /torem/source/net.bebedizo.osgi.service.upnp.eib/ bei. Neben der Weiterentwicklung durch die Einbindung zusätzlicher Protokolle ist auch die Verbesserung des GUIs ein Thema. Alle von den Managern der Kern-Bundles angebotenen Methoden lassen sich zwar nutzen, doch die Benutzerfreundlichkeit ist teilweilse noch nicht zufriedenstellend hoch. Vor allem die Darstellung von und die Interaktion mit den Components und Links könnte Verbesserungen gut verkraften. Einerseits sollte die Gruppierung mehrerer Components in eine Super-Component ermöglicht werden (wobei das auch Änderungen am Model und der Control mit sich ziehen würde), um das Schaltbild übersichtlich gestalten zu können. Andererseits wäre es von Vorteil die gekoppelten Ports nicht mit einer direkten Linie zu verbinden, sondern dafür einen ausgeklügelten Wegfindungs-Algorithmus zu verwenden, um Links nicht quer über andere Components zu zeichnen. Ein weiterer interessanter Aspekt wäre es, die in [5] vorgestellten Möglichkeiten des GUI-Renderings in die Component-Link-View einzubauen. Somit wäre eine intuitivere Interaktion mit den Geräten möglich, da anstatt der derzeit verwendeten Textfelder zur Werteingabe je nach Port-Typ passende GUI-Elemente, wie Auswahlboxen, Regler und Auswahllisten, darge1 Mittlerweile in KNX als Nachfolger-Technologie aufgegangen. Siehe auch: http://www. konnex.org/ KAPITEL 7. FAZIT 88 stellt werden könnten. Das GUI würde dann mehr zur interaktiven Nutzung inspirieren und nicht nur den automatisierten Modus in den Vordergrund stellen. Diese Erweiterung in Kombination mit einer angepassten ComponentLink-View könnte auch für eine multimediale Installation interessant sein, bei der die Besucher über einen großen, Berührungs-sensitiven Bildschirm Kopplungen erstellen und löschen könnten, um damit eine Multimedia-Anwendung zu steuern. 7.3 Schlussbemerkung Die Stärke von Torem liegt in der Entwicklung von Prototypen. Durch das Koppeln der Komponenten kann recht schnell eine bestimmte Funktionalität getestet werden, ohne ein Programmier-Projekt anlegen zu müssen, die externen Bibliotheken zu konfigurieren, usw. Programmen, die hohe Leistungen bieten müssen, wird der generische Ansatz von Torem u. U. nicht leistungsfähig oder konfigurierbar genug sein. Solche Programme sind mit einer eigenständigen Implementierung vermutlich besser bedient. Doch für Systeme, die keine optimierte Kommunikation zwischen den Komponenten benötigen, kann mit Hilfe von Torem eine vollständige, stabile Applikation erstellt werden, die durch das Abspeichern in eine XML-Datei leicht von Host zu Host portierbar ist. Anhang A Entwicklung der If-Component An dieser Stelle wird die Entwicklung der If-Component (s. Abb. A.1) detailiert erläutert, um einen Einblick zu geben, wie eigene Components implementiert werden können. Der komplette Quellcode sowie viele weitere Components sind auf der CD im Verzeichnis des Standard-Ingredients-Bundles zu finden. Das Grundgerüst der Klasse If sieht so aus: package net . bebedizo . torem . standardingredients . component ; import import import import net . bebedizo . torem . component . AbstractComponent ; net . bebedizo . torem . component . ComponentFactory ; net . bebedizo . torem . component . Port ; net . bebedizo . torem . component . PortImpl ; import org . osgi . util . tracker . ServiceTracker ; public class If extends AbstractComponent { // Private Instanzvariablen public If ( long id , ComponentFactory componentFactory , ServiceTracker listenerTracker ) { // Anlegen der Ports } public void portActivated ( String portId , Object val ) { // Behandeln von Wert - Zuweisungen und der // Aktivierung . Die Logik - Implementierung . } } 89 ANHANG A. ENTWICKLUNG DER IF-COMPONENT 90 Abbildung A.1: Die If-Component, wie sie (in der deutschen Lokalisierung) in der Component-Link-View dargestellt wird. Die Ports dieser Component werden als private Instanzvariablen angelegt: // Der PIP , der die Component aktiviert . private Port test ; // Der DIP , der die Bedingung für die // if - Anweisung repräsentiert . private Port condition ; // Der POP , der bei true aktiviert wird . private Port truePort ; // Der POP , der bei false aktiviert wird . private Port falsePort ; // Interner Zwischenspeicher für den Wert den der // condition - Port zuletzt zugewiesen bekam . private boolean c ; Der Konstruktor erzeugt die vier Ports und registriert die Component selbst als PortListener bei den Input-Ports. public If ( long id , ComponentFactory componentFactory , ServiceTracker listenerTracker ) { super ( id , componentFactory , componentFactory . getName () ) ; // Anlegen der Ports . test = new PortImpl ( " 0 test " , id , " test " , true , null , listenerTracker ) ; condition = new PortImpl ( " condition " , id , " condition " , true , Boolean . class , listenerTracker ) ; truePort = new PortImpl ( " 0 true " , id , " true " , false , null , listenerTracker ) ; falsePort = new PortImpl ( " 1 false " , id , " false " , false , null , listenerTracker ) ; // Registrieren als PortListener test . addPortListener ( this ) ; condition . addPortListener ( this ) ; ANHANG A. ENTWICKLUNG DER IF-COMPONENT 91 // Damit die Ports via // getInputPorts und // getOutputPorts // von außen sichtbar sind . inputPortMap . put ( test . getId () , test ) ; inputPortMap . put ( condition . getId () , condition ) ; outputPortMap . put ( truePort . getId () , truePort ) ; outputPortMap . put ( falsePort . getId () , falsePort ) ; } Bisher war alles nur Teil der äußerlichen Repräsentation. Nun folgt die Implementierung der Logik dieser Component. Die Methode portActivated wird bei jeder Wertzuweisung des condition-Ports und bei jeder Aktivierung des test-Ports aufgerufen. public void portActivated ( String portId , Object val ) { if ( portId . equals ( condition . getId () ) ) { // Wenn der condition - Port einen neuen Wert erhielt c = ( Boolean ) val ; } else if ( portId . equals ( test . getId () ) ) { // Wenn der test - Port aktiviert wurde if ( c ) { // Aktiviere den true - Port , falls // c == true gilt truePort . activate ( null ) ; } else { // Aktiviere den false - Port , falls // c == false gilt falsePort . activate ( null ) ; } } } Anhang B Inhalt der CD-ROM File System: ISO9660 Mode: Single-Session (CD-ROM) B.1 Diplomarbeit Pfad: / DA.dvi . . DA.pdf . DA.ps . . README B.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Diplomarbeit (DVI1 -Datei) Diplomarbeit (PDF2 -Datei) Diplomarbeit (PostScript-Datei) Hinweise zur Benutzung von Torem Literatur Die referenzierten Online-Quellen, nach Kapiteln sortiert als PDFs und im HTML3 -Format. Pfad: /literatur/ index.html . . . . . . . . */ . . . . . . . . . . . . 1 Mini-Website zum komfortablen Navigieren zwischen den Online-Literaturquellen Online-Literatur zum entsprechenden Kapitel Device Independent – ein Dateiformat. Siehe auch: http://www.math.umd.edu/ ∼asnowden/comp-cont/dvi.html 2 Portable Document Format. Siehe auch: http://partners.adobe.com/public/developer/ pdf/index reference.html 3 Hypertext Markup Language. Siehe auch: http://www.w3.org/MarkUp/ 92 ANHANG B. INHALT DER CD-ROM B.3 Bilder und Grafiken Pfad: /bilder/ *.eps . . . . . . . . . . . Bilder und Grafiken dieser Diplomarbeit im EPS4 -Format. B.4 Ausführbare Dateien Pfad: /torem/ Torem-1.0-Setup.exe . . Torem-1.0.zip . . . . . . B.5 93 Installationsprogramm für Torem Die Vollinstallation als ZIP-komprimierte Datei Quelldateien Die einzelnen Bundles wurden als separate Plugin-Projekte für Eclipse entwickelt. Die folgende Auflistung verwendet eine abgekürzte Schreibweise der Ordner (die den Namen der Bundles entsprechen), da die Ordnernamen teilweise sehr lang sind. Neben dem Auslassen ([...]) ganzer Package-Teile werden folgende Abkürzungen verwendet: cl für componentlink, cf für componentfactory, conv für converter und stdingredients für standardingredients. Pfad: /torem/source/ build.xml . . . . . . . . it.[...].basedriver/ . . . it.[...].extra/ . . . . . net.[...].upnp/ . . . . net.[...].controlpoint/ . . . . net.[...].eib/ . . . . . . . net.[...].component/ net.[...].converter/ . net.[...].gui/ . . . . . net.[...].all/ . . . . . 4 . . . . . . . . Eine Ant5 -Datei zum Kompilieren und Erstellen der Bundles Das UPnP Base Driver Bundle Das UPnP Base Driver Zusatz-Bundle Das UPnP-Basisfunktions-Bundle Das UPnP Bundle (zur Integration von UPnP-Geräten in Torem) Das UPnP-EIB Bundle (zur Integration von EIB-Geräten in UPnP) Das Component Bundle Das Converter Bundle Das GUI-Basis-Bundle Das Component-Factory-GUI-Bundle für die Anzeige aller CFs Encapsulated Postscript. Siehe auch: http://partners.adobe.com/public/developer/en/ ps/5002.EPSF Spec.pdf 5 http://ant.apache.org/ ANHANG B. INHALT DER CD-ROM net.[...].all.de/ . . . . . net.[...].all.en/ . . . . . net.[...].cf.standard/ . . net.[...].cf.standard.de/ net.[...].cf.standard.en/ net.[...].cf.upnp/ . . . . net.[...].cl.standard/ . . net.[...].cl.standard.de/ net.[...].cl.standard.en/ net.[...].conv.standard/ net.[...].logger/ . . . . . net.[...].link/ . . . . . . net.[...].client/ . . . . . net.[...].server/ . . . . . net.[...].position/ . . . . net.[...].stdingredients/ net.[...].connection/ . . org.[...].xerces/ . . . . . org.[...].upnp/ . . . . . org.kxml2/ . . . . . . . Die deutsche Lokalisierung für das All-Component-Factory-GUI-Bundle Die englische Lokalisierung für das All-Component-Factory-GUI-Bundle Das Component-Factory-GUI-Bundle für die Anzeige der Standard Ingredient CFs Die deutsche Lokalisierung für das Standard-Component-Factory-GUI-Bundle Die englische Lokalisierung für das Standard-Component-Factory-GUI-Bundle Das Component-Factory-GUI-Bundle für die Anzeige der UPnP-CFs Das Component-Link-Bundle Die deutsche Lokalisierung für das Component-Link-Bundle Die englische Lokalisierung für das Component-Link-Bundle Das Converter-GUI-Bundle Ein Bundle zum Visualisieren der geloggten Werte Das Link Bundle Das Network-Client-Bundle Das Network-Server-Bundle Das Position Bundle Das Standard Ingredients Bundle Das Netzwerk-Basis-Bundle Das Apache Xerces6 Bundle Das Cyberlink-Bibliothek-Bundle Das kxml27 Bundle B.6 Externe Bibliotheken Pfad: /torem/lib/ osgi.core.jar . . . . . . . osgi.compendium.jar . . 6 7 http://xerces.apache.org/ http://www.kxml.org/ 94 Der CORE-Teil der OSGi-Spezifikation Der COMPENDIUM-Teil der OSGi-Spezifikation ANHANG B. INHALT DER CD-ROM B.7 Abhängigkeiten Pfad: /dependencies/ Falcon/ . . . . . . . . . Eiblet/ . . . . . . . . . . Beinhaltet notwendige Dateien zum Nutzen des UPnP-EIB-Treibers Beinhaltet notwendige Dateien zum Nutzen des UPnP-EIB-Treibers B.8 Beispiel-Mappings Pfad: /samples/ *.xml . . . . . . . . . . Im XML-Format abgespeicherte Mappings B.9 Sonstige Dateien Pfad: / torem/mapping.xsd . . 95 XML-Schema für die XML-Dateien zum Speichern erstellter Mappings Abkürzungsverzeichnis API . . . . . . . . . . . . . BSD . . . . . . . . . . . . . CF . . . . . . . . . . . . . . CM . . . . . . . . . . . . . . CORBA . . . . . . . . . DIP . . . . . . . . . . . . . DK . . . . . . . . . . . . . . DLL . . . . . . . . . . . . . DOP . . . . . . . . . . . . DVI . . . . . . . . . . . . . EIB . . . . . . . . . . . . . EPS . . . . . . . . . . . . . GNU . . . . . . . . . . . . GPL . . . . . . . . . . . . . GUI . . . . . . . . . . . . . HTML . . . . . . . . . . . HTTP . . . . . . . . . . . IDL . . . . . . . . . . . . . IIOP . . . . . . . . . . . . IP . . . . . . . . . . . . . . . J2SE . . . . . . . . . . . . JAR . . . . . . . . . . . . . JDK . . . . . . . . . . . . . JMF . . . . . . . . . . . . . JRMP . . . . . . . . . . . JVM . . . . . . . . . . . . l10n . . . . . . . . . . . . . LAN . . . . . . . . . . . . . LGPL . . . . . . . . . . . LM . . . . . . . . . . . . . . MICO . . . . . . . . . . . MIDI . . . . . . . . . . . . MVC . . . . . . . . . . . . OMG . . . . . . . . . . . . Application Programming Interface Berkeley Software Distribution Component Factory Component Manager Common Object Request Broker Architecture Daten-Input-Port Daten-Kopplung Dynamic Link Library Daten-Output-Port Device Independent European Installation Bus Encapsulated Postscript GNU is not Unix General Public License Graphical User Interface Hypertext Markup Language Hypertext Transfer Protocol Interface Definition Language Internet InterORB Protocol Internet Protocol Java 2 Standard Edition Java Archive Java Development Kit Java Media Framework Java Remote Method Protocol Java Virtual Machine Localization: ‘l’ + 10 Buchstaben + ‘n’ Local Area Network Library (oder Lesser) General Public License Link Manager Mico is Corba Musical Instrument Digital Interface Model View Control Object Management Group 96 ABKÜRZUNGSVERZEICHNIS OSGi . . . . . . . . . . . . OSI . . . . . . . . . . . . . . PDE . . . . . . . . . . . . . PDF . . . . . . . . . . . . . PIP . . . . . . . . . . . . . . PK . . . . . . . . . . . . . . POP . . . . . . . . . . . . . RMI . . . . . . . . . . . . . RPC . . . . . . . . . . . . . SAM . . . . . . . . . . . . SMS . . . . . . . . . . . . . SOAP . . . . . . . . . . . Socam . . . . . . . . . . . TCP . . . . . . . . . . . . . UPnP . . . . . . . . . . . VSL . . . . . . . . . . . . . W3C . . . . . . . . . . . . WSDL . . . . . . . . . . . XML . . . . . . . . . . . . Open Services Gateway Initiative Open Systems Interconnection Plugin Development Environment Portable Document Format Programmfluss-Input-Port Programmfluss-Kopplung Programmfluss-Output-Port Remote Method Invocation Remote Procedure Call Sensor Aktuator Module Short Message Service Simple Object Access Protocol Service Oriented Context Aware Middleware Transport Control Protocol Universal Plug and Play Virtools Scripting Language World Wide Web Consortium Web Services Description Language Extensible Markup Language 97 Literaturverzeichnis [1] Ahamer, I. R.: Unified Generic Control Point – Entwicklung einer Steuerinstanz für Universal-Plug-and-Play- und Jini-fähige Geräte. Diplomarbeit, Fachhochschule Hagenberg, Software Engineering, Hagenberg, Austria, Juni 2003. [2] Cetus Team: Cetus Links: 16604 Links on Objects and Components / Distributed Objects & Components: CORBA ORBs. URL, http://www. cetus-links.org/oo%5Fobject%5Frequest%5Fbrokers.html, Oktober 2005. Kopie auf CD-ROM. [3] Comer, D. E.: Computernetzwerke und Internets. Pearson Studium, Munich, 2 Aufl., 2000. [4] Deitsch, A. und D. Czarnecki: Java Internationalization. O’Reilly & Associates, Inc., Sebastopol, CA, USA, März 2001. [5] Doppler, J. S.: Erstellung von Interfaces zur Steuerung verteilter Anwendungen am Beispiel UPnP-fähiger Eingabegeräte und automatisierter GUI-Generatoren. Diplomarbeit, Fachhochschule Hagenberg, Digitale Medien, Hagenberg, Austria, Juni 2006. [6] Eclipse Foundation Inc.: Equinox OSGi Transition Proposal . URL, http://www.eclipse.org/equinox/documents/transition.html, September 2005. Kopie auf CD-ROM. [7] Emberger, E. N.: Janus—A Service-Oriented Middleware for Distributed Applications with a Multimodal User Interface. Diplomarbeit, Fachhochschule Hagenberg, Software Engineering, Hagenberg, Austria, August 2004. [8] Gamma, E., R. Helm, R. Johnson und J. Vlissides: Entwurfsmuster . Addison-Wesley, Munich u. a., 2004. [9] Gu, T., H. K. Pung und D. Q. Zhang: Towards an OSGi-Based Infrastructure for Context-Aware Applications. IEEE Pervasive Computing, 3(4):66–73, Oktober–Dezember 2004. 98 LITERATURVERZEICHNIS 99 [10] Halsall, F.: Data Communications, Computer Networks and Open Systems. Addison-Wesley, 4 Aufl., 1995. [11] Hitthaler, T.: Generative Erzeugung von Design mit vvvv . Diplomarbeit, Fachhochschule Salzburg, MultiMediaArt, Vienna, Austria, Mai 2005. [12] Ircam – Centre Pompidou: A brief history of MAX . URL, http: //freesoftware.ircam.fr/article.php3?id article=5, 1998. Kopie auf CDROM. [13] Kowalk, W. P. und M. Burke: Rechnernetze. B. G. Teubner Stuttgart, Stuttgart, 1994. [14] Kurose, J. F. und K. W. Ross: Computer Networking: A Top-Down Approach Featuring the Internet. Addison-Wesley, 2 Aufl., 2003. [15] Meso: vvvv – a multipurpose toolkit. URL, http://vvvv.meso.net/, Mai 2006. Kopie auf CD-ROM. [16] mico/E: The mico/E project - an OSS CORBA implementation in Eiffel . URL, http://www.math.uni-goettingen.de/micoe/, Februar 1999. Kopie auf CD-ROM. [17] Microsoft: .NET Remoting. URL, http://msdn.microsoft.com/ library/default.asp?url=/library/en-us/dndotnet/html/hawkremoting.asp, Juli 2001. Kopie auf CD-ROM. [18] Object Management Group: Naming Service Specification. URL, http://www.omg.org/docs/ptc/00-08-07.pdf, August 2000. Kopie auf CD-ROM. [19] Object Management Group: CORBA FAQ. URL, http://www. omg.org/gettingstarted/corbafaq.htm, Januar 2006. Kopie auf CD-ROM. [20] OSGi Alliance: OSGi Service Gateway Specification. URL, http: //osgi.org/, Mai 2000. Kopie auf CD-ROM. [21] OSGi Alliance: About the OSGi Platform. URL, http://osgi.org/ documents/osgi technology/osgi-sp-overview.pdf, Juli 2004. Kopie auf CD-ROM. [22] OSGi Alliance: OSGi Service Platform Core Specification. URL, http://osgi.org/, August 2005. Kopie auf CD-ROM. [23] OSGi Alliance: OSGi Service Platform Service Compendium. URL, http://osgi.org/, August 2005. Kopie auf CD-ROM. LITERATURVERZEICHNIS 100 [24] OSGi Alliance: OSGi Members. URL, http://osgi.org/about/ member list.asp?section=1, März 2006. Kopie auf CD-ROM. [25] OSGi Alliance: OSGi Products. URL, http://osgi.org/products/ products.asp?section=3, März 2006. Kopie auf CD-ROM. [26] Puckette, M. S.: Combining Event and Signal Processing in the MAX Graphical Programming Environment. Computer Music Journal, 15:68– 77, 1991. [27] Puckette, M. S.: Max at seventeen. Computer Music Journal, 26:31– 43, 2002. [28] Puder, A. und K. Römer: Middleware für verteilte Systeme: Konzepte und Implementierungen anhand der CORBA-Plattform MICO. dpunkt-Verlag, Heidelberg, 1 Aufl., 2001. [29] Richens, P. und M. Nitsche: Mindstage: Towards a Functional Virtual Architecture. In: Proceedings of the 11th International CAAD Futures Conference, S. 331–340, Dordrecht, 2005. Springer. [30] Schmidt, D., M. Stal, H. Rohnert und F. Buschmann: PatternOriented Software Architecture – Volume 2: Patterns for Concurrent and Networked Objects. John Wiley & Sons, Ltd, Chichester, UK, 2001. [31] Schramm, P., E. Naroska, P. Resch, J. Platte, H. Linde, G. Stromberg und T. Sturm: A Service Gateway for Networked Sensor Systems. IEEE Pervasive Computing, 3(1):66–74, Januar–März 2004. [32] Struck, F.: Shaderentwicklung für menschliche Darsteller in 3D Echtzeitanwendungen. Diplomarbeit, Fachhochschule Wedel, Medieninformatik, Wedel, Germany, Februar 2004. [33] Sun: Java Remote Method Invocation: 3 - RMI System Overview . URL, http://java.sun.com/j2se/1.5.0/docs/guide/rmi/spec/ rmi-arch4.html, September 2004. Kopie auf CD-ROM. [34] Sun: Locale (Java 2 Platform SE 5.0). URL, http://java.sun.com/j2se/ 1.5.0/docs/api/java/util/Locale.html, September 2004. Kopie auf CDROM. [35] Sun: Official Specifications for CORBA support in J2SE 5.0 . URL, http://java.sun.com/j2se/1.5.0/docs/guide/idl/compliance.html, September 2004. Kopie auf CD-ROM. [36] Sun: RMI-IIOP Programmer’s Guide. URL, http://java.sun.com/j2se/ 1.5.0/docs/guide/rmi-iiop/rmi%5Fiiop%5Fpg.html, September 2004. Kopie auf CD-ROM. LITERATURVERZEICHNIS 101 [37] Tanenbaum, A. S.: Computernetzwerke. Pearson Studium, Munich, 3 Aufl., 2000. [38] The Open Group: The Open Group offers $1 million sponsorship. URL, http://www.opengroup.org/press/7jun99%5Fa.htm, Juni 1999. Kopie auf CD-ROM. [39] Thurner, M.: Middleware für das Haus der Zukunft. Diplomarbeit, Fachhochschule Hagenberg, Medientechnik und -design, Hagenberg, Austria, Juli 2004. [40] Ullenboom, C.: Java ist auch eine Insel . Galileo Press, Bonn, 4 Aufl., 2004. [41] W3C: Web Services. URL, http://www.w3.org/2002/ws/, März 2006. Kopie auf CD-ROM. [42] Waldo, J.: The Jini Specifications. Addison-Wesley, Boston, 2000. Messbox zur Druckkontrolle — Druckgröße kontrollieren! — Breite = 100 mm Höhe = 50 mm — Diese Seite nach dem Druck entfernen! — 102