|
ix 04/1999 (Artikel auf heise.de )
Titel: Jini
Autor: ?
Beschreibung: Überblick über die Jini-Grundlagen.
top
Prinzip:
-
"typed network"
-
objektorientierter Ansatz von
Java
-
RMI
- Remote Method
Invocation
-
Klassen werden übers Netz von einem
HTTP-Server geladen
-
Kommunikation
-
Jini=
- Java +
- Konzepte zur Entwicklung verläßlicher verteiler
Systeme
- "write-once, run anywhere"
-
djinns
-
Gruppen von Diensten
- mit beliebigen
Funktionen
-
Soft- oder Hardware-Dienste
-
Bsp.
- Service, der E-Mails
abschickt
- Druckdienst
top
"spontaneous networking"
- Hardware sofort "benutzbar"
top
Lookup-Service (LUS)
- zentrale Anlaufstelle
-
liefert
-
Proxy-Object
- Übernimmt als Stellvertreter die komplette
Kommunikation mit dem Dienst
-
Dienste
- nachfragen
» Siehe auch: liefert
- anbieten
-
Bootstrap-Phase
Dienste suchen einen Lookup-Service; sie bekommen dessen Proxy (P) und
hinterlegen ihren eigenen (SP).
-
Auffinden und Benutzen
Interessenten suchen nach einem Lookup-Server und erhalten den Proxy
eines oder mehrerer Dienste(s); nun lassen sich dessen Methoden aufrufen.
- Discovery
Interessenten suchen zuerst via Discovery-Protokoll einen LUS und
erhalten dessen Proxy.
- Lookup
Der Interessent gibt eine Beschreibung des gesuchten Dienstes an den
Proxy des LUS. Der wiederum durchsucht seine Dienst-Einträge nach passenden Kandidaten und übermittelt
die Proxies der gefundenen Dienste.
- Use
Der Dienstnehmer kann nun in den Proxies wie in lokalen Java-Objekten
Methoden aufrufen und den Dienst benutzen. Jegliche Kommunikation zwischen Proxy und Dienst bleibt dabei
für ihn transparent.
top
Infrastruktur
-
Anmeldung der Dienste über
» Siehe auch: Dienste
- Discovery&Join-
Protokoll
-
zentrale Aufgabe:
-
Zusammenbringen von Service-
top
bootstraping
» Siehe auch: Bootstrap-Phase
-
Dienste können einander (und auch den LUS) ohne
Kenntnis des Netzes finden
-
Vor.
- Adresse
- Java Virtual Machine
(JVM)
- Speicher
top
Proxy
- jeder Dienst besitzt einen
Proxy
-
besteht aus mehreren Java-Objekten
- werden (incl. Programmcode) an den Dienstnehmer
übertragen
-
Schnittstelle=
- Typ +
- Methoden (der
Objekte)
-
"dynamischer Treiber"
- Laden bei Bedarf
- Dienst nicht mehr benötigt -> Verwerfen des
Proxies
-
z.B. Drucker lädt seinen Proxy zum
Client
- Java-RMI-Stub
- wohldefinierte Schnittstelle
- Client ruft Methoden des Proxy
auf
top
Komponenten
- Beans
- Swing
- Leases
- Transaktionen
- Distributed Events
- (Interaktion zw. den Diensten wird festgelegt,
nicht deren Implementierung)
top
Buchtipps
» Siehe Dokument: http://www.amazon.de/exec/obidos/external-search?tag=personalknowl-21&keyword=jini&index=books-de
top
Anwendungen
-
Beispiele
-
Kommunikation zw. Dienst und
Proxy
Als Beispiel dient ein Additions-Dienst, der die Berechnung auf
einem anderen Rechner ausführen läßt. Da Jini auf Remote Methode Invocation aufbaut, ist es naheliegend
(aber nicht zwingend), die Kommunikation zwischen Proxy und dem eigentlichen Dienst über dieses
Protokoll zu realisieren. Jini ermöglicht es, den von RMI erzeugten Server-Stub direkt als Proxy zu
benutzen. Das Interface ( Listing
3 ) zwischen Proxy und Dienstnehmer ist ein
normales RMI-Interface, das wie üblich von Remote abgeleitet ist und dessen Methoden
RemoteExceptions auslösen können.
Im zweiten Schritt ist das Interface zu implementieren
( Listing 4 ). Dazu ist ein RMI-Server erforderlich, der die Methode add zur
Verfügung stellt. Nach dem Kompilieren müssen mit rmic die Stub- und Skeleton-Klassen erzeugt
werden.
Damit man den Dienst verwenden kann, muß er im LUS registriert
werden. Im Beispiel erledigt das ein eigenständiges Java-Programm ( Listing 5 ). Als
erstes muß ein RMISecurityManager installiert werden, da der Code des LUS-Proxy über das Netz
geladen und dann ausgeführt werden soll. Dies ist mit dem Standard-Security-Manager nicht zulässig.
Neben den eigentlichen Jini-Klassen (net.jini-Packages) hat Sun eine Reihe weiterer
Wrapper-Klassen mitgeliefert, die Standardaufgaben wie Eintragen im LUS und Verlängern von Leases stark
vereinfachen. Diese finden sich in den com.sun.jini-Packages.
Im folgenden wird von der JoinManager- und
LeaseRenewalManager-Klasse Gebrauch gemacht. Der JoinManager verbirgt die Arbeit mit den
Discovery/Join-Protokollen und stellt eine einfache Möglichkeit zum Eintragen von Diensten in den LUS
dar. Er erwartet in seinem Konstruktor eine Instanz des Dienst-Proxy, dessen Attribute (zusätzliche
Informationen), einen ServiceIDListener, der die erzeugte Service-ID entgegennimmt, und ein
LeaseRenewalManager-Objekt. Letzterer übernimmt dann selbständig die Verlängerung des Lease für
den Eintrag im LUS. Im Beispiel wird eine Instanz der AdderInterface_impl-Klasse erzeugt und an
den JoinManager übergeben. Dieser ermittelt automatisch die zugehörige Stub-Klasse und
hinterlegt sie als Proxy im LUS. Jeder Jini-Dienst wird in den LUS durch einen Universally Unique
Identifier (UUID) eindeutig identifiziert. Die Discovery- und Join-Spezifikation fordert von Diensten,
daß sie sich in allen LUS mit der gleichen Service-ID registrieren und diese persistent speichern, um
sie auch bei einem möglichen Neustart des Dienstes beizubehalten. Im Beispiel läßt der
JoinManager die Service-ID von einem LUS generieren und übermittelt sie dann an die
ServiceIDNotify-Methode, wo sie ignoriert wird. Das Beispiel ist also nicht ganz
Jini-konform.
java
-Djava.rmi.server.codebase=http://<host>:<port>/<path>/
-Djava.security.policy=<path>/<policy-file> JiniAddService
startet das Programm. Wichtig ist dabei der abschließende Slash in der
Codebase, da sie als Präfix vor Package und Klassennamen steht, um eine URL zu erhalten. Der gibt an,
von wo die jeweilige Klassendatei aus Sicht des Client zu finden ist. Der Hostname sollte deshalb immer
voll-qualifiziert sein. Die Klasse foo.bar.puh muß sich auf dem HTTP-Server unter
http://<host>:<port>/<path>/foo/bar/puh.class befinden, da der RMI-Classloader der
Client-JVM über diesen URL ihm unbekannte Klassen nachzuladen versucht.
- Def. Java-Interface als Schnittstelle zw.
Service und Dienstnehmer
- Implementierung der Schnittstelle u.
Erzeugen eines Proxy-Objekts
- Anmelden des
Dienstes
-
Dienstbeschreibung mit "Service
Template"
Um den Dienst zu benutzen, ist - wie in Listing 6 erkennbar -
ein wenig mehr Aufwand zu treiben. Die wesentlichen Klassen für diese Aufgabe sind
LookupLocator, ServiceRegistrar und ServiceTemplate. Die erstgenannte verbirgt die
Discovery-Phase. Verwendet wird dabei das Unicast Discovery Protocol, bei dem die Adresse eines LUS
direkt angegeben werden muß. Das von ihm erhaltene ServiceRegistrar-Objekt stellt den LUS-Proxy
dar. Die Beschreibung des gesuchten Dienstes erfolgt mittels der ServiceTemplate-Klasse. Sie
läßt als Suchkriterien eine Service-ID, Java-Typen und Attribute zu. Service-IDs und Attribute müssen
exakt übereinstimmen, bei Java-Typen werden sowohl Klassen gleichen Typs als auch davon abgeleitete
gefunden. Der Wert 'null' wird als Wildcard interpretiert.
Im allgemeinen ist es sinnvoll, immer mindestens einen Typ anzugeben,
damit es beim Benutzen der gefundenen Objekte nicht zu ClassCastExceptions kommen kann.
Attribute können die Suche verfeinern, um die Treffermenge zu reduzieren, und Service-IDs sind
nützlich, um einen bestimmten Dienst zu finden. Im Beispiel wird nur ein Java-Typ, nämlich das
AddInterface, angegeben. Die lookup-Methode des ServiceRegistrar-Objekts führt die
eigentliche Suche aus. Das gefundene Objekt läßt sich nach einem Typecast wie ein lokales
ansprechen.
java
-Djava.security.policy=/<path>/policy.all JiniAddClient
startet das Programm. Jini sieht vor, daß Dienste in Gruppen
organisiert werden können. Jeder kann zu beliebig vielen Gruppen gehören. Beispielsweise könnte ein
Drucker sich in den Gruppen 'Hardware', 'Drucker' und 'Konferenzraum 2' anmelden, oder verschiedene
Dienste werden zusammengefaßt, weil sie gemeinsam eine Aufgabe lösen. Gruppen werden eingerichtet,
indem man LUS die entsprechenden Gruppennamen zuweist. Bei Suns Referenzimplementierung müssen sie als
Kommandozeilenparameter beim Start des LUS angegeben werden. Der Name public ist als Standard
vergeben. Ein Dienstanbieter ist selbst für eine passende Registrierung in den LUS verantwortlich. Er
muß seinen Dienst also in jedem ihm bekannten LUS genau dann registrieren, wenn der mindestens eine
Gruppe verwaltet, zu der der Dienst gehören soll. Daneben hat Sun auch ein Admin-Interface vorgesehen.
Implementiert ein Dienst dies, ist dessen Gruppenzugehörigkeit während der Laufzeit zum Beispiel über
den mitgelieferten Browser änderbar. Bei den LUS kann man zusätzlich noch die Gruppen ändern, die sie
verwalten sollen. Im Beispiel-Dienst ( Listing
5 ) ist das Gruppenkonzept durch den
JoinManager verborgen. Bei dem angebenen Aufruf registriert er den Dienst in allen gefundenen
LUS und damit auch in allen Gruppen. Durch Verwendung eines anderen Konstruktors läßt sich die
Registrierung des Dienstes auf bestimmte Gruppen einschränken:
new JoinManager(proxy, null,
new String[] {'Gruppe1', 'Gruppe2'}, null,
new SL(), new LeaseRenewalManager());
-
Unterschiedliche
Discovery-Protokolle
Wie bereits angedeutet, unterstützt der Dienstnehmer
in Listing 6 nur das Unicast Discovery Protocol. Daneben bietet Jini noch das Multicast
Request und das Multicast Announcement Protocol, um LUS in einem Netz zu finden. Jini bietet eine
Infrastruktur auf Dienstebene an, das heißt, sie kann nicht dazu dienen, IP-Adressen und Subnetz-Masken
von Rechnern zu konfigurieren, sondern setzt die Konfiguration auf niedriger Ebene
voraus.
Voraussetzung beim Unicast Discovery Protocol ist, daß die Adresse des
LUS bereits bekannt ist. Er wird über eine TCP-Verbindung zum Port 4160 (ein 'Scherz' der
Jini-Entwickler: Ergebnis der hexadezimalen Subtraktion CAFE - BABE) angesprochen. Als Antwort
überträgt der LUS sein Proxy-Objekt und seine Gruppenzugehörigkeiten. Der Einsatz von Multicast-IP
ermöglicht es, LUS ohne Kenntnis ihrer konkreten IP-Adresse zu finden. Dazu werden UDP-Datagramme an
die Multicast-Gruppe 224.0.1.85 und Port 4160 gesendet. Sie enthalten die Gruppennamen, an denen man
interessiert ist, und Service-IDs der bereits bekannten LUS. Ein LUS antwortet nur dann, wenn er noch
nicht bekannt ist und zu einer der genannten Gruppen gehört. Er baut dazu eine TCP-Verbindung auf und
übermittelt die Antwort wie im Unicast Discovery Protocol.
Über das Multicast Announcement Protocol machen sich LUS im Netz
bekannt. Dazu schicken sie UDP-Datagramme an die Multicast-Gruppe 224.0.1.84:4160. Sie enthalten die
Service-ID, die Netzadresse des LUS und die Gruppenzugehörigkeiten. Falls der LUS noch nicht bekannt
ist und eine interessante Gruppe verwaltet, fordern interessierte Teilnehmer den LUS-Proxy über das
Unicast Discovery Protocol an.
Normalerweise werden die beiden Multicast-Protokolle verwendet, um LUS
im lokalen Netzsegment zu finden. Der Vorteil liegt vor allem im geringeren Konfigurationsaufwand: Der
Benutzer muß in seinen Diensten und Anwendungen keine LUS-Adressen eintragen. Die Multicast-Protokolle
haben jedoch nur eine begrenzte Reichweite, da die meisten Router so konfiguriert sind, daß sie keine
Multicast-Pakete weitergeben. Für Verbindungen über solche Netzsegmente hinaus muß das Unicast
Discovery Protocol verwendet werden.
Nach diesem theoretischen Exkurs soll nun auch der Dienstnehmer
'spontan' über die Multicast-Protokolle seinen Additionsdienst finden ( Listing 7 ).
Wesentlich für die Implementierung sind die Klasse LookupDiscovery und das
DiscoveryListener-Interface. Die Klasse LookupDiscovery benutzt die Multicast-Protokolle,
um umliegende LUS zu finden. Welche interessant sind, bestimmen die im Konstruktor angegebenen Gruppen.
Die Proxies dieser LUS werden per Event an die registrierten DiscoveryListener übergeben. Das
Programm nimmt die Proxies in der discovered-Methode des DiscoveryListener-Interface
entgegen. Dessen Methoden sollen keine komplexen Operationen beinhalten, um die Bearbeitung schnell
aufeinanderfolgender Events zu ermöglichen. Daher speichert das Programm die Proxies zwischen und weckt
den main-Thread zur Bearbeitung. Dort werden die Lookup-Services wie in
Listing 6 nach einem AddInterface durchsucht. Sobald dieses gefunden wurde, wird die Berechnung
durchgeführt und das Programm beendet. Zur Vervollständigung des DiscoveryListener-Interface muß
die Methode discarded implementiert sein.
-
Attribute zur
Diensterläuterung
Attribute ermöglichen es, einerseits die Anzahl der Treffer
einer Suchanfrage möglichst gering zu halten, andererseits einem menschlichen Benutzer Informationen
für die Auswahl des 'richtigen' Dienstes anzubieten. Realisiert werden Attribute durch Java-Objekte.
Jedes public-Feld einer Klasse, die von Entry oder AbstractEntry abgeleitet ist,
wird dabei als Attribut interpretiert (siehe Listing 8 ).
Ein Entry-Objekt stellt eine Menge von Attributen dar. Da einem
Dienst mehrere solche Objekte zugeordnet sein können, speichert der LUS zu jedem Dienst demnach eine
Menge von Attributmengen. Dadurch, daß Attribute in für Menschen verständlicher Form angezeigt werden
sollen, entstehen Probleme wie Lokalisierung. Zu deren Lösung soll unter anderem das JavaBeans-Konzept
beitragen.
Listing 9 zeigt das Registrieren eines Dienstes mit Attributen. Der attributes-Array wird dann dem
JoinManager im Konstruktor übergeben. Das Code-Fragment in Listing 10 setzt
zusätzlich zum Typ ein Attribut als Suchkriterium ein. Es wird nach einem Dienst gesucht, dem
mindestens ein ServiceInfo-Objekt zugeordnet ist, das genau den Eintrag '1.0beta' enthält. Der
Wert 'null' fungiert wieder als Wildcard.
Attribute sind auch nach der Registrierung noch modifizierbar, um
Zustandsänderungen des Dienstes wiederzugeben. Beispielsweise können Statusinformationen in Attributen
in den LUS hinterlegt werden (etwa Fehlerzustände von Geräten). Es wird jedoch davon ausgegangen, daß
Attribute eher statischen Charakter haben. Aus Effizienzgründen gilt als Richtschnur, sie nicht öfter
als einmal pro Minute zu ändern.
Da die Attribute in normalen Java-Klassen gespeichert werden, lassen
sie sich auch mit Programmcode versehen. Damit ergibt sich eine weitere interessante
Anwendungsmöglichkeit der Attribute, nämlich das Hinterlegen eines GUI zur Benutzung des Dienstes.
Dieser Ansatz erlaubt es, daß andere Dienste und Programme ihn über das Java-Interface ansprechen
können. Zusätzlich bringt er für den Fall, daß ein menschlicher Benutzer ihn direkt verwenden will,
sein eigenes GUI gleich mit.
Die Beispiele zeigen, wie wenig Aufwand notwendig ist, einen Dienst in
ein Jini-System zu integrieren. Der Vorteil liegt in den dynamischen Konfigurationsmöglichkeiten.
Weitergehende interessante Aspekte (Transaktionen, verteilte Events, GUIs, JavaSpaces) sollen Artikel
in künftigen Heften zeigen.
- Listings dazu
» Siehe Dokument: http://www.heise.de/ix/artikel/1999/04/144/01.shtml
top
|