18.5 Das entfernte Objekt
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. (Das ist eine bekannten Namensgebung aber keine Pflicht.)
18.5.1 Der Bauplan für entfernte Objekte
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 UnicastRemoteObject bietet Hilfe bei der Übertragung 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.
18.5.2 Der Konstruktor
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 UnicastRemoteObject 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.
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.
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 );
}
}
|
class java.rmi.server.UnicastRemoteObject
extends RemoteServer
|
|
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. |
|
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. |
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 );
}
|
18.5.3 Implementierung der entfernten Methoden
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 (oder Remote sein). 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 die Referenz ein Verweis auf den Stellvertreter.
|