Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel (8. Auflage) von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel (8. Auflage)

Java ist auch eine Insel (8. Aufl.)
8., aktual. Auflage, geb., mit DVD
1.475 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1371-4
Pfeil 19 Verteilte Programmierung mit RMI und Web–Services
Pfeil 19.1 Entfernte Objekte und Methoden
Pfeil 19.1.1 Stellvertreter helfen bei entfernten Methodenaufrufen
Pfeil 19.1.2 Standards für entfernte Objekte
Pfeil 19.2 Java Remote Method Invocation
Pfeil 19.2.1 Zusammenspiel von Server, Registry und Client
Pfeil 19.2.2 Wie die Stellvertreter die Daten übertragen
Pfeil 19.2.3 Probleme mit entfernten Methoden
Pfeil 19.2.4 Nutzen von RMI bei Middleware-Lösungen
Pfeil 19.2.5 Zentrale Klassen und Schnittstellen
Pfeil 19.2.6 Entfernte und lokale Objekte im Vergleich
Pfeil 19.3 Auf der Serverseite
Pfeil 19.3.1 Entfernte Schnittstelle deklarieren
Pfeil 19.3.2 Remote-Objekt-Implementierung
Pfeil 19.3.3 Stellvertreterobjekte
Pfeil 19.3.4 Der Namensdienst (RegistryRegistry)
Pfeil 19.3.5 Remote-Objekt-Implementierung exportieren und beim Namensdienst anmelden
Pfeil 19.3.6 Einfaches Logging
Pfeil 19.3.7 Aufräumen mit dem DGC
Pfeil 19.4 Auf der Clientseite
Pfeil 19.5 Entfernte Objekte übergeben und laden
Pfeil 19.5.1 Klassen vom RMI-Klassenlader nachladen
Pfeil 19.6 Weitere Eigenschaften von RMI
Pfeil 19.6.1 RMI und CORBA
Pfeil 19.6.2 RMI über HTTP getunnelt
Pfeil 19.6.3 Automatische Remote-Objekt-Aktivierung
Pfeil 19.7 Daily Soap
Pfeil 19.7.1 SOAP-Protokoll
Pfeil 19.7.2 Die technische Realisierung
Pfeil 19.7.3 SOAP-Implementierungen
Pfeil 19.7.4 @WebService in Java 6
Pfeil 19.7.5 Einen Web-Service definieren
Pfeil 19.7.6 Web-Services veröffentlichen
Pfeil 19.7.7 Einen JAX-WS-Client implementieren
Pfeil 19.8 Java Message Service (JMSJMS)
Pfeil 19.9 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

19.3 Auf der Serverseite Zur nächsten ÜberschriftZur vorigen Überschrift

Um entfernte Objekte mit ihren Methoden in Java-Programmen zu nutzen, sind einige Arbeitsschritte nötig, die wir im Folgenden kurz skizzieren. An den Schritten spiegelt sich der Programmieraufwand wider:

1. Eine entfernte Schnittstelle deklariert Methoden.
2. Eine Klasse implementiert die Schnittstelle und füllt die Methode mit Leben. Dies bildet die Remote-Objekt-Implementierung.
3. Mit dieser Implementierung benötigen wir ein exportiertes Exemplar. Wir melden es beim Namensdienst an, damit der Client es finden kann. Dies schließt die Serverseite ab.

Galileo Computing - Zum Seitenanfang

19.3.1 Entfernte Schnittstelle deklarieren Zur nächsten ÜberschriftZur vorigen Überschrift

Damit der Client eine entfernte Methode nutzen kann, muss er ein Stellvertreterobjekt befragen. Dieses packt die Daten ein und übermittelt sie. Die Stellvertreterobjekte erzeugt Java selbstständig. Damit der Generator korrekten Quellcode für die Übertragung erstellen kann, ist eine Beschreibung nötig. Die Deklaration muss die Signatur eindeutig spezifizieren, und damit weiß der Client, wie die Methode aussieht, die er aufrufen kann, und der Server kann die Methode dann beschreiben. Normalerweise gibt es für die Spezifikation der entfernten Methoden spezielle Beschreibungssprachen (wie IDL bei CORBA), doch bei RMI reicht es, ein Java-Interface mit den Methoden zu deklarieren.

Listing 19.1 com/tutego/insel/rmi/Adder.java

package com.tutego.insel.rmi; 
 
import java.rmi.Remote; 
import java.rmi.RemoteException; 
 
public interface Adder extends Remote 
{ 
  int add( int x, int y ) throws RemoteException; 
}

An diesem Beispiel können wir mehrere wichtige Eigenschaften der Schnittstelle ablesen:

  • Die entfernte Schnittstelle ist öffentlich. Wenn sie nur paketsichtbar oder eingeschränkter ist, kann der Client die entfernte Methode nicht finden, wenn er danach verlangt.
  • Die eigene Schnittstelle erweitert die Schnittstelle Remote. Nur die Klassen, die Remote implementieren, können entfernte Methoden anbieten. Remote ist allerdings leer und damit eine Markierungsschnittstelle.
  • Die angebotenen Methoden können unbeabsichtigte Fehler auslösen, zum Beispiel, wenn das Transportsystem zusammenbricht. Für diesen Fall muss jede Methode RemoteException in einer throws-Anweisung aufführen.
  • Eine entfernte Methode darf Parameter besitzen. Sind die Argumente primitive Werte, werden diese einfach übertragen. Handelt es sich um Objekte, so müssen diese serialisierbar sein.

Galileo Computing - Zum Seitenanfang

19.3.2 Remote-Objekt-Implementierung Zur nächsten ÜberschriftZur vorigen Überschrift

Der Client nutzt letztendlich das vom Server bereitgestellte entfernte Objekt. Der Server steht in der Pflicht, eine Implementierung der Remote-Schnittstelle anzugeben, sodass diese im nächsten Schritt exportiert und damit angemeldet werden kann.

Die Implementierung der Geschäftslogik ist einfach:

Listing 19.2 com/tutego/insel/rmi/AdderImpl.java

package com.tutego.insel.rmi; 
 
public class AdderImpl implements Adder 
{ 
  @Override public int add( int x, int y ) 
  { 
    return x + y; 
  } 
}

Da die Klasse eine Implementierung der Schnittstelle ist, geben wir ihr die Endung Impl. (Das ist eine übliche Namensgebung, aber keine Pflicht.)

Es steht frei, andere Methoden anzugeben, die nicht in der Schnittstelle vorgegeben sind, doch sind diese natürlich nicht nach außen hin sichtbar. Die Argumente und Rückgabewerte können von jedem beliebigen Datentyp sein. Bei primitiven Datentypen werden spezielle read()- und write()-Folgen generiert. Objekte müssen die Schnittstelle Serializable implementieren (oder Remote sein). Dann werden die lokalen Objekte als Kopie übertragen. Über die Serialisierung werden alle nicht-statischen und nicht-transienten Attribute übermittelt. Ist das Argument wiederum instanceof Remote, wird dieser Verweis als einfache Referenz übergeben. In Wirklichkeit ist die Referenz ein Verweis auf den Stellvertreter.


Galileo Computing - Zum Seitenanfang

19.3.3 Stellvertreterobjekte Zur nächsten ÜberschriftZur vorigen Überschrift

Die Stellvertreter sind Objekte auf der Client- und Serverseite, die die tatsächliche Kommunikation betreiben. Unsere Clients programmieren ausschließlich gegen eine Remote-Schnittstelle, aber der Stellvertreter (Proxy) auf der Clientseite ist die Implementierung der Remote-Schnittstelle, die tatsächlich die Clientanfrage annimmt und auch an das Netzwerk weitergibt. Von Hand müssen diese Stellvertreter nicht programmiert werden; sie generiert Java zur Laufzeit.


Hinweis Vor Java 5 musste der RMI-Compiler rmic verwendet werden. Der Generator erzeugte den Stub (Stellvertreter auf der Clientseite) und den Skeleton (Proxy auf der Serverseite) aus der Methodenbeschreibung der Remote-Klasse. Steht auf einer Seite der Client-Server-Kommunikation nicht mindestens eine Java 5-Implementierung, so muss doch wieder rmic zum Einsatz kommen.



Galileo Computing - Zum Seitenanfang

19.3.4 Der Namensdienst (Registry) Zur nächsten ÜberschriftZur vorigen Überschrift

Der RMI-Dienst muss im nächsten Schritt beim Namensdienst das entfernte Objekt unter einem öffentlichen Namen anmelden, sodass der Client es finden kann. Java bietet zwei Namensdienste, die in Frage kommen: die RMI-Registry und den JNDI-Dienst. [Weiteres dazu unter http://java.sun.com/javase/6/docs/technotes/guides/jndi/jndi-rmi.html.] Beides lässt sich in Java nutzen, aber die RMI-Registry ist etwas leichter zu verwenden.

Die Registry ist ein eigenständiger Dienst, der auf zwei Arten gestartet werden kann:

  • beim Server selbst, der über LocateRegistry.createRegistry() den Namensdienst vor dem Exportieren aufbaut;
  • über ein externes in Java programmiertes Dienstprogramm rmiregistry, das im bin-Verzeichnis eines JDKs mitgeliefert wird.

Die rmiregistry von Hand zu starten, hat den Vorteil, dass Client, RMI-Dienst und Registry auf drei unterschiedlichen Servern laufen können, die abweichende Lebenszyklen haben dürfen. Wenn der RMI-Server die Registry automatisch startet, ist das praktisch und hat den Vorteil, dass Anpassungen am Klassenpfad nicht nötig sind und der Anwender es nicht vergessen kann.

Der Server startet die Registry selbst

Um die RMI-Registry zu starten, wird einfach LocateRegistry.createRegistry() aufgerufen.

try 
{ 
  LocateRegistry.createRegistry( Registry.REGISTRY_PORT ); 
} 
catch ( RemoteException e )  { /* ... */ }

Anschließend ist der Namensdienst gestartet, und der Server kann seine Dienste dort anmelden.


final class java.rmi.registry.LocateRegistry

  • static Registry createRegistry()
    Startet den Namensdienst auf dem Port 1099.
  • static Registry createRegistry( int port )
    Startet den Namensdienst auf dem angegebenen Port. Registry.REGISTRY_PORT ist der Standard-Port 1099.

Das Dienstprogramm rmiregistry

Unter Windows starten wir den Dienst in einer neuen DOS-Box (sozusagen im Hintergrund) mit folgender Zeile:

$ start rmiregistry

Unter Unix-Systemen so:

$ rmiregistry &

Die Registry können wir uns somit als einen einfachen Assoziativspeicher vorstellen, der Namen und Stub-Objekte verbindet. Der Zustand des Stubs wird bei der Registry hinterlegt.


Hinweis Die RMI-Registry ist ein Java-Programm, welches zwingend die Remote-Schnittstellen unserer RMI-Klassen im Klassenpfad benötigt. Entweder ist dazu rmiregistry auf der Konsole in dem Pfad zu starten, in dem die Klassendateien des RMI-Servers stehen, oder mit dem Schalter -Djava.rmi.server.codebase=file:/pfad der Ort der Typen zu bestimmen [Mehr zur Codebase unter http://java.sun.com/javase/6/docs/technotes/guides/rmi/codebase.html.]. Der Start der RMI-Registry über LocateRegistry.createRegistry() spart das natürlich, denn so stehen die Klassen automatisch im Suchpfad.


Der Port

Der Namensdienst läuft standardmäßig auf dem Port 1099. Für Dienste hinter einer Firewall ist es bedeutend, dass dieser Port auch anders lauten kann. Eine andere Port-Nummer lässt sich einfach als Argument angeben:

$ start rmiregistry 2001

Der angegebene Port dient nur der Vermittlung vom Client zum Namensdienst. Die Kommunikation von Client und Server läuft über einen anderen Port.

An dieser Stelle haben wir schon fast alles beisammen. Der Namensdienst läuft und wartet auf den Server und den Client. Beginnen wir mit dem Server. Er ist ein normales Java-Programm ohne Einschränkungen und muss weder etwas mit Remote noch mit Serializable zu schaffen haben.


Galileo Computing - Zum Seitenanfang

19.3.5 Remote-Objekt-Implementierung exportieren und beim Namensdienst anmelden Zur nächsten ÜberschriftZur vorigen Überschrift

Bevor ein Client sich mit dem Server verbinden und die entfernten Methoden aufrufen kann, muss unser Server für eingehende Netzwerkverbindungen bereitet sein. Aus diesem Grund muss unsere Remote-Objekt-Implementierung exportiert werden. Dann erst kann unser Server eingehende RMI-Verbindungen annehmen. [Java setzt für die Netzwerkkommunikation vorübersetzte Stubs oder automatische generierte Proxy-Objekte ein. Mehr dazu unter in der API-Dokumentation unter http://java.sun.com/javase/6/docs/api/java/rmi/server/UnicastRemoteObject.html.] Nach dem Exportieren erfolgt die Anmeldung beim Namensdienst, und der Server-Code ist abgeschlossen. Wir starten vorher die RMI-Registry aus dem Programm, und dann ist der Server bereit.

Listing 19.3 com/tutego/insel/rmi/Server.java

package com.tutego.insel.rmi; 
 
import java.rmi.registry.LocateRegistry; 
import java.rmi.registry.Registry; 
import java.rmi.server.RemoteServer; 
import java.rmi.server.UnicastRemoteObject; 
 
public class Server 
{ 
  public static void main( String[] args ) throws RemoteException 
  { 
    LocateRegistry.createRegistry( Registry.REGISTRY_PORT ); 
 
    AdderImpl adder = new AdderImpl(); 
    Adder stub = (Adder) UnicastRemoteObject.exportObject( adder, 0 ); 
    RemoteServer.setLog( System.out ); 
 
    Registry registry = LocateRegistry.getRegistry(); 
    registry.rebind( "Adder", stub ); 
 
    System.out.println( "Adder angemeldet" ); 
  } 
}

Remote-Objekt-Implementierung exportieren

Zum Exportieren von Remote-Objekten wird die Klasse UnicastRemoteObject verwendet. Sie lässt sich auf zwei Arten nutzen:

  • Das Remote-Objekt wird mit der statischen Funktion UnicastRemoteObject.exportObject(Remote) exportiert. Die Rückgabe ist ein Remote-Objekt-Proxy, der beim Anmelden beim Namensdienst verwendet wird.
  • Die Remote-Objekt-Klasse erweitert UnicastRemoteObject. Im Konstruktor von UnicastRemoteObject, den unsere Unterklasse ja automatisch aufrufen wird, steht ein Aufruf von exportObject(), sodass sich das Objekt selbst exportiert. [Eine Unterklasse von UnicastRemoteObject zu erzeugen hat gegenüber exportObject() den Vorteil, dass die Methoden hashCode(), equals(), toString() über die UnicastRemoteObject-Oberklasse RemoteObject implementiert sind. Das ist interessant, wenn unser exportiertes Objekt als Parameter oder Rückgabe eines Methodenaufrufs über die Leitung geht und die drei Methoden etwa beim Hashing wichtig sind.]

class java.rmi.server.UnicastRemoteObject 
extends RemoteServer

  • static RemoteStub exportObject( Remote obj ) throws RemoteException
    Exportiert das Remote-Objekt und liefert einen Proxy zurück.

Anmelden/Abmelden am Namensdienst

Ist das Remote-Proxy-Objekt exportiert, wird es dem Namensdienst mit rebind() oder bind() bekannt gemacht. Die RMI-Registry ist als assoziative Datenstruktur zu verstehen, die einen Objektnamen mit einem entfernten Objekt-Proxy assoziiert. (Es ist der Proxy und nicht das Remote-Objekt AdderImpl, obwohl beide die Remote-Schnittstelle implementieren – Adder in unserem Fall.)

Die Notation für den Objekt-Namen beim Anmelden ist wie bei einer URL:

rmi://Host:Port/Objektname

Ist ein alternativer Port für den Namensdienst gewählt, stellen wir diesen mit einem Doppelpunkt wie üblich hintenan – sonst läuft der Namensdienst standardmäßig unter 1099. Der vorangestellte Protokollname rmi ist optional, sodass er auch weggelassen werden kann. Ist kein Rechnername angegeben, wird localhost angenommen. Daher steht im oberen Beispiel einfach nur rebind("Adder", stub).

Zum Binden der Informationen bietet der Namensdienst zwei unterschiedliche Methoden an:

  • bind() trägt den Service im Namensdienst ein, aber wenn schon ein anderer Dienst unter dem gleichen Namen läuft, wird eine AlreadyBoundException ausgelöst.
  • rebind() dagegen fügt einen neuen Eintrag mit dem gleichen Namen hinzu oder überschreibt den alten.

Ist der Dienst nicht mehr erwünscht, meldet unbind() ihn wieder ab. Der Namensdienst muss wie beim Anmelden laufen. Aus Sicherheitsgründen erlaubt der Namensdienst nur dem Server, der das Objekt seinerzeit angemeldet hat, es wieder abzumelden. Einen zusätzlichen Namen müssen wir nicht angeben.


final class java.rmi.registry.LocateRegistry

  • static void static Registry getRegistry()
    Liefert einen Verweis auf die Registry oder löst eine RemoteException aus, wenn die Registry nicht lokalisiert werden konnte.

interface java.rmi.registry.Registry 
extends Remote

  • void bind( String name, Remote obj ) throws AlreadyBoundException, MalformedURLException, RemoteException Bindet das Objekt obj, das in der Regel der Stub ist, an den Namen name und trägt es so in der Registrierung ein. Eine AlreadyBoundException zeigt an, dass der Name schon vergeben ist. Die MalformedURLException informiert, wenn der Name ungültig gebunden ist. Eine RemoteException wird ausgelöst, wenn der Namensdienst nicht erreicht werden konnte. Fehlende Rechte führen zu einer AccessException.
  • void rebind( String name, Remote obj )
    Verhält sich wie bind(), mit dem Unterschied, dass Objekte ersetzt werden, sofern sie schon angemeldet sind.
  • void unbind( String name )
    Entfernt das Objekt aus der Registrierung. Ist das Objekt nicht gebunden, folgt eine NotBoundException. Die anderen Fehler sind wie bei bind().

Galileo Computing - Zum Seitenanfang

19.3.6 Einfaches Logging Zur nächsten ÜberschriftZur vorigen Überschrift

Um die Aktivität von RMI verfolgen zu können, haben die Entwickler einen einfachen Login-Mechanismus eingebaut. Er gibt Auskunft über die Objekte und entfernte Referenzen. Hier erfahren wir auch, ob alle gewünschten Objekte korrekt gefunden wurden. Das Logging lässt sich mit der Eigenschaft java.rmi.server.logClass einschalten, wenn der Wert auf true gesetzt ist. Dann erscheinen Ausgaben auf dem System.err-Fehlerkanal. Außerdem setzt die statische Funktion RemoteServer.setLog(OutputStream) einen Fehlerausgabestrom. Die Funktion getLog() liefert diesen Fehlerkanal allerdings als aufgewerteten PrintStream.


abstract class java.rmi.server.RemoteServer 
extends RemoteObject

  • static void setLog( OutputStream out )
    Loggt RMI-Aufrufe, indem sie in den Ausgabestrom out geschrieben werden. Ist out=null, wird das Logging beendet.
  • static PrintStream getLog()
    Liefert den Ausgabestrom für das RMI-Logging.

Tipp Das Paket java.rmi.server hält noch eine andere Klasse bereit, die recht nützlich sein kann: UID. Mit dieser Klasse lässt sich eine einfache ID berechnen.

String s = new java.rmi.server.UID().toString();


Galileo Computing - Zum Seitenanfang

19.3.7 Aufräumen mit dem DGC topZur vorigen Überschrift

Im Fall von verteilten Anwendungen reicht der normale GC nicht, und das Konzept muss um einen verteilten GC (engl. distributed GC, kurz DGC) erweitert werden. Im lokalen Fall weiß die lokale Maschine immer, ob ein Objekt referenziert wird, bei verteilten Anwendungen kann aber auf dem Server ein Objekt existieren, für das sich kein Mensch mehr interessiert. Damit bei verteilten Anwendungen auch der GC nicht mehr benutzte Objekte auf der Serverseite wegräumen kann, verschickt die Maschine beim Nutzen und Lösen von Verbindungen referenced- beziehungsweise dereferenced-Meldungen. Ist die Verbindung dann gelöst, bleibt die Klasse jedoch noch einige Zeit auf dem Server und wird nicht sofort gelöst. Aussagen über die Verweildauer gibt die Lease an, die sich über eine Eigenschaft verändern lässt.


Beispiel Setze die Verweildauer für Objekte auf eine halbe Stunde hoch:

$ java -Djava.rmi.dgc.leaseValue=1800000 MyClass

Die Standarddauer ist auf 10 Minuten gesetzt.




Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Tipp
Zum Katalog: Coding for Fun





 Coding for Fun


 Buchempfehlungen
Zum Katalog: Objektorientierte Programmierung





 Objektorientierte
 Programmierung


Zum Katalog: Einstieg in Eclipse 3.4






 Einstieg in
 Eclipse 3.4


Zum Katalog: Java 6 lernen mit Eclipse






 Java 6 lernen
 mit Eclipse


Zum Katalog: NetBeans Platform 6






 NetBeans
 Platform 6


Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Visual C# 2008






 Visual C# 2008


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de