Galileo Computing < openbook >
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


Java ist auch eine Insel (2. Aufl.) von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 1.4
Java ist auch eine Insel (2. Auflage)
gp Kapitel 18 Verteilte Programmierung mit RMI und SOAP
  gp 18.1 Entfernte Methoden
  gp 18.2 Nutzen von RMI bei Middleware-Lösungen
  gp 18.3 Die Lösung für Java ist RMI
    gp 18.3.1 Entfernte Objekte programmieren
    gp 18.3.2 Entfernte und lokale Objekte im Vergleich
    gp 18.3.3 RMI und CORBA
  gp 18.4 Definition einer entfernten Schnittstelle
  gp 18.5 Das entfernte Objekt
    gp 18.5.1 Der Bauplan für entfernte Objekte
    gp 18.5.2 Der Konstruktor
    gp 18.5.3 Implementierung der entfernten Methoden
    gp 18.5.4 UnicastRemoteObjekt, RemoteServer und RemoteObject
  gp 18.6 Stellvertreterobjekte erzeugen
    gp 18.6.1 Das Dienstprogramm rmic
  gp 18.7 Der Namensdienst (Registry)
    gp 18.7.1 Der Port
  gp 18.8 Der Server: entfernte Objekte beim Namensdienst anmelden
    gp 18.8.1 Automatisches Anmelden bei Bedarf
  gp 18.9 Einen Client programmieren
    gp 18.9.1 Einfaches Logging
  gp 18.10 Aufräumen mit dem DGC
  gp 18.11 Entfernte Objekte übergeben und laden
    gp 18.11.1 Klassen vom RMI-Klassenlader nachladen
    gp 18.11.2 Sicherheitsmanager
  gp 18.12 Registry wird vom Server gestartet
  gp 18.13 RMI über die Firewall
    gp 18.13.1 RMI über HTTP getunnelt
  gp 18.14 Daily Soap
    gp 18.14.1 SOAP-Implementierung der Apache-Gruppe
    gp 18.14.2 Einen Client mit der Apache-Bibliothek implementieren
    gp 18.14.3 Der Seifen-Server
  gp 18.15 Java-API für XML Messaging (JAXM)
  gp 18.16 Java Message Service (JMS)
    gp 18.16.1 OpenJMS
    gp 18.16.2 Beispiel mit Konsument und Produzent im Publish–Subscribe-Modell


Galileo Computing

18.5 Das entfernte Objekt  downtop

Die Methoden des Servers werden letztendlich vom Client über die Stellvertreter genutzt. Der Server muss unterschiedliche Vorgaben erfüllen: Er muss eine spezielle Klasse erweitern, einen Konstruktor anbieten und die entfernte Schnittstelle implementieren. Dann kann ein Server-Objekt angemeldet werden. Das Server-Objekt implementiert daher die Geschäftslogik und sieht wie folgt aus:

Listing 18.2   AdderImpl.java

import java.rmi.*;
import java.rmi.server.*;

public class AdderImpl extends UnicastRemoteObject implements Adder
{
  public AdderImpl() throws RemoteException
  {
  }

  public int add( int x, int y ) throws RemoteException
  {
    return x + y;
  }
}

Da die Klasse eine Implementierung der Schnittstelle ist, geben wir ihr die Endung Impl.


Galileo Computing

18.5.1 Der Bauplan für entfernte Objekte  downtop

Zuerst fällt in der Implementierung auf, dass wir die Klasse UnicastRemoteObject erweitern. Sie liegt im Paket java.rmi.server und zeigt so die grobe Richtung für die Verwendung an.

public class AdderImpl extends UnicastRemoteObject implements Adder
{
   ...
}

Diese Klasse bietet Hilfe bei der Übertagung der Daten mittels Standard-TCP-Sockets an. Der Server kann so auf eingehende Anfragen reagieren und diese bearbeiten. Weiterhin zeigt UnicastRemoteObject an, dass ein Exemplar (nicht repliziert) unserer Klasse existieren soll.


Galileo Computing

18.5.2 Der Konstruktor  downtop

Für den Konstruktor eines entfernten Objekts gelten zwei Eigenschaften: Wir müssen einen Standard-Konstruktor anbieten und dieser muss ebenso wie die Methoden RemoteException anzeigen.

public AdderImpl() throws RemoteException
{
}

Der Standard-Konstruktor ist notwendig, da eine Unterklasse genau diesen aufrufen möchte. Unser Konstruktor muss nichts machen. Er ruft aber automatisch den Konstruktor der Oberklasse auf, also den von UnicastRemoteObject. Wir haben schon beschrieben, dass er bei der Übertragung hilft. Wenn ein entferntes Objekt konstruiert wird, dann bindet er diesen Dienst an einen anonymen Port und horcht auf einkommende Aufrufe. Wollten wir einen speziellen Port nutzen, müssten wir im Konstruktor unserer Unterklasse einen parametrisierten Konstruktor von UnicastRemoteObject aufrufen, der einen Port annimmt.

class java.rmi.server.UnicastRemoteObject
extends RemoteServer

gp  protected UnicastRemoteObject() throws RemoteException
Erzeugt und exportiert ein neues UnicastRemoteObject und bindet es an einen unbekannten Port. Konnte es nicht exportiert werden, löst der Konstruktor eine RemoteException aus.
gp  protected UnicastRemoteObject( int port ) throws RemoteException
Erzeugt ein UnicastRemoteObject und bindet es an den angegeben Port. Ist dieser null, so wird er vom System zugewiesen.

Vererbung nicht möglich – was dann?

Es kann passieren, dass eine entfernte Klasse schon von einer anderen Klasse erbt, und wir wegen der fehlenden Mehrfachvererbung ein Problem bekommen. Wenn wir uns dafür entscheiden, keine Objekte über UnicastRemoteObject anzubieten, aber eine existierende Klasse trotzdem Anbieter sein möchte, so muss das Objekt im Konstruktor ein entferntes Objekt, welches Remote implementiert, mit UnicastRemoteObject.exportObject(Remote) anmelden.

class java.rmi.server.UnicastRemoteObject
extends RemoteServer

gp  static RemoteStub exportObject( Remote obj )
Exportiert das entfernte Objekt und macht es empfänglich für hereinkommende Aufrufe. Es wird ein willkürlicher Port verwendet.
gp  static Remote exportObject( Remote obj, int port )
Verhält sich wie exportObject(Remote), nur wird der angegebene Port und nicht der Standard-Port 1099 verwendet.
Beispiel   Der Server implementiert die Schnittstelle Adder und registriert sich selbst über exportObject() beim Namensdienst. (Was das ist, erfahren wir gleich etwas genauer.)

Listing 18.3   AdderImpl2.java

import java.rmi.*;
import java.rmi.server.*;

class AdderImpl2 implements Adder
{
  public AdderImpl2() throws RemoteException
  {
  }
  
  public int add( int x, int y ) throws RemoteException
  {
    return x + y;
  }
  
  public static void main( String args[] ) throws Exception
  {
    AdderImpl2 server = new AdderImpl2();
    UnicastRemoteObject.exportObject( server );
    Naming.rebind( "adder", server );
  }
}

Hinweis   Die Erweiterung von UnicastRemoteObject ist nur eine Abkürzung für den Weg über exportObject(). Die Methode wird aber ohnehin genommen, wie der Ausschnitt aus dem Quellcode für den Konstruktor zeigt.
protected UnicastRemoteObject() throws RemoteException
{
  this( 0 );
}

protected UnicastRemoteObject(int port) throws RemoteException
{
  this.port = port;
  exportObject( (Remote)this, port );
}


Galileo Computing

18.5.3 Implementierung der entfernten Methoden  downtop

Im nächsten Schritt müssen die Methoden der Schnittstelle implementiert werden. Es steht frei, andere Methoden anzugeben, die nicht in der Schnittstelle vorgegeben sind, doch diese sind dann natürlich nicht nach außen sichtbar.

public int add( int x, int y ) throws RemoteException
{
  return x + y;
}

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. Dann werden die lokalen Objekte als Kopie übertragen. Über die Serialisierung werden alle nicht statischen und nicht transienten Attribute übermittelt. Ist der Parameter wiederum instanceof Remote, dann wird dieser Verweis als einfache Referenz übergeben. In Wirklichkeit ist sie ein Verweis auf den Stellvertreter.


Galileo Computing

18.5.4 UnicastRemoteObjekt, RemoteServer und RemoteObject  toptop

Entfernte Objekte erweitern oft die Klasse UnicastRemoteObjekt. Sie selbst ist jedoch eine Unterklasse von java.rmi.server.RemoteServer, eine abstrakte Klasse, die wiederum die abstrakte Klasse java.rmi.server.RemoteObject beerbt.

RemoteObject ist ein verteiltes Objekt

RemoteObject ist nichts anderes als ein verteiltes Objekt, welches die Schnittstellen Remote und Serializable implementiert. Da beide aber nur Markierungsschnittstellen sind, taucht keine ausprogrammierte Methode auf. RemoteObject ersetzt die Klasse Object für verteilte Objekte. Die Unterscheidung tritt bei den Methoden hashCode(), equals() und toString() auf. Ebenso implementiert RemoteObject die Methoden writeObject() und readObject(), damit die verteilten Objekte serialisiert werden können.

abstract class java.rmi.server RemoteObject
implements Remote, Serializable

gp  protected RemoteObject()
Erzeugt ein remote-Objekt.
gp  protected RemoteObject( RemoteRef newref )
Erzeugt ein remote-Objekt, welches mit angegebener remote-Referenz initialisiert ist.
gp  RemoteRef getRef()
Liefert eine remote-Referenz für das Objekt.
gp  public static Remote toStub( Remote obj ) throws NoSuchObjectException
Liefert den Stub für das remote-Objekt obj. Die Operation kann nur durchgeführt werden, wenn das Objekt schon exportiert wurde. Sonst wird eine NoSuchObjectException ausgelöst.
gp  int hashCode()
Liefert den Hashcode. Zwei entfernte Stubs, die auf dasselbe Objekt verweisen, sollten auch den gleichen Hashwert liefern. Die Methode ruft auf entfernten RemoteRef-Objekten die Methode remoteHashCode() auf und auf lokale Objekte einfach hashCode().
gp  boolean equals( Object obj )
Vergleicht, ob zwei entfernte Objekte gleich sind. Ist obj kein entferntes Objekt, so wird ein normaler Vergleich mit equals() gemacht. Der Vergleich mit dem entfernten Objekt wird mit remoteEquals() auf dem RemoteRef-Objekt vorgenommen.
gp  String toString()
Liefert eine String-Repräsentation.

RemoteServer

RemoteServer erweitert RemoteObject und fügt die interessante Methode getClientHost() hinzu. Damit kann der Server den Hostnamen vom Client als String erfahren. Damit der Server jedoch Informationen an den Client zurückschicken kann, muss der Client selbst als Server auftreten. Die Kommunikation ist bei RMI in der Regel immer einseitig.

abstract class java.rmi.server.RemoteServer
extends RemoteObject

gp  protected RemoteServer()
Erzeugt für die Unterklassen ein RemoteServer()-Objekt.
gp  protected RemoteServer( RemoteRef ref )
Erzeugt für die Unterklassen ein RemoteServer()-Objekt mit der Referenz ref.
gp  static String getClientHost() throws ServerNotActiveException
Liefert den Hostnamen des aktuellen Clients. Die Ausnahme ServerNotActiveException wird ausgelöst, wenn der Aufruf außerhalb eines Servers stattfindet, der RMI anbietet.




Copyright © Galileo Press GmbH 2003
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 GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de