Skip to main content.

Jini

ix 04/1999 (Artikel auf heise.de )
Titel: Jini
Autor: ?
Beschreibung: Überblick über die Jini-Grundlagen.



top


Prinzip:

  • "typed network"
    • objektorientierter Ansatz von Java
      • Hardware
      • Software
    • RMI
      • Remote Method Invocation
      • Klassen werden übers Netz von einem HTTP-Server geladen
        • Port 8080
          • .jar
          • .class
  • 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).



    • Discovery
      Anbieter sucht mittels Discovery-Protokoll nach erreichbaren LUS.

      LUSes bieten ihre Dienste ebenfalls über Proxies an.


      • UDP (Unicast Discovery Protocol)
    • Join
      Anbieter trägt seinen Dienst in den LUS ein. Dabei wird der zugehörige Proxy dort als serialisiertes Objekt hinterlegt.


    • Leasing
      Der Eintrag ist nur für eine bestimmte Zeit vereinbart. Nach Ablauf dieser Zeit wird er vom LUS automatisch entfernt.
      Soll der Eintrag länger bestehen bleiben, so muss der Dienstanbieter den "Lease" für den Eintrag rechtzeitig verlängern. Dieses Konzept verhindert, dass der LUS nach einiger Zeit viele ungültige Einträge von Diensten, die nicht mehr verfügbar sind, enthält.



  • 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-
      • Anbietern
      • Nutzern

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
    • Dies beachten! 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