19.2.2 | Beispiel: Kaufhaus-Anwendung |
Eine Kaufhaus-Anwendung ist eine Sitzungs-orientierte Anwendung, wobei in einer Sitzung üblicherweise der Benutzer und die aktuell gewählten Artikel gespeichert werden. Das Beispiel wird mit Hilfe von zwei unterschiedlichen Typen von Server-Objekten implementiert:Da es sich hier um zwei verschiedene Typen von Server-Objekten handelt, müssen zwei Server-Schnittstellen definiert werden. Die Schnittstelle des zentralen Server-Objekts hat dabei folgenden Aufbau:
- Ein zentrales Server-Objekt, das eingehende Verbindungen entgegennimmt und dafür zuständig ist, neue Sitzungen zu erzeugen. Dieses Objekt ist somit für die Ausgabe eines Warenkorbs zuständig.
- Mehrere dezentrale Objekte, die den jeweiligen Warenkorb eines Clients repräsentieren. Die Daten im Warenkorb werden von Client-Seite hinzugefügt und auf dem Server im Warenkorb zwischengespeichert.
import java.rmi.Remote; import java.rmi.RemoteException; public interface ShopServer extends Remote { public Cart createCart() throws RemoteException; }Wenn ein neuer Client einen Warenkorb benötigt, ruft er die Methode createCart() und bekommt ein Cart-Exemplar zurück. Da der Inhalt des Warenkorbs auf dem Server gespeichert werden soll, wird er in einer eigenen Server-Schnittstelle modelliert:import java.rmi.Remote; import java.rmi.RemoteException; public interface Cart extends Remote { public void addProduct(String name) throws RemoteException; public void removeProduct(String name) throws RemoteException; public String[] listContents() throws RemoteException; public void buy(String custID) throws RemoteException; }Man kann dem Warenkorb neue Produkte hinzufügen, Produkte entfernen, die vorhandenen Produkte auflisten und alle darin enthaltenen Produkte kaufen. Zur Vereinfachung werden die Produkte hier nur durch ihren Namen identifiziert.
Das zentrale Server-Objekt wird von der Klasse ShopServerImpl implementiert. Es stellt die Hauptanwendung dar, die als eigener Server gestartet wird, und verfügt deshalb über eine main()-Methode, in der das Objekt beim Namensdienst registriert wird.import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.rmi.Naming; public class ShopServerImpl extends UnicastRemoteObject implements ShopServer { public ShopServerImpl() throws RemoteException { } public Cart createCart() throws RemoteException { System.out.println("create cart"); return new CartImpl(); } public static void main(String args[]) { try { Naming.rebind("shop-server", new ShopServerImpl()); } catch(Exception ex) { ex.printStackTrace(); } } }Beim Aufruf von createCart() wird ein neues Cart-Exemplar zurückgegeben. Die Implementierung wird hier durch die Klasse CartImpl bereitgestellt. Dass es sich hierbei auch um ein Objekt handelt, dessen Methode auf dem Server und nicht auf dem Client ausgeführt werden, wird dadurch sichtbar, dass die Klasse auch von UnicastRemoteObject abgeleitet ist:import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; import java.util.List; import java.util.ArrayList; public class CartImpl extends UnicastRemoteObject implements Cart { List products; boolean pending = false; public CartImpl() throws RemoteException { products = new ArrayList(); } public void addProduct(String name) { System.out.println("add product: "+name); products.add(name); } public void removeProduct(String name) { System.out.println("remove product: "+name); products.remove(name); } public String[] listContents() { System.out.println("list contents"); String[] contents = new String[products.size()]; products.toArray(contents); return contents; } public void buy(String custID) { System.out.println("buy products: "+custID); pending = false; // Hier Bestellung verschicken ... } }Wenn eine Server-Methode ein neues Exemplar einer Klasse zurückgibt, das von UnicastRemoteObject erbt, wird nicht das neue Exemplar per Objekt-Serialisierung auf den Client übertragen, sondern es wird auf Serverseite ein neues Server-Objekt erzeugt und lediglich der Client-Side Proxy auf den Client übertragen. Das heißt, wenn der Client nach dem Abrufen eines Cart-Exemplars dessen Methoden aufruft, werden diese Methoden auf Serverseite ausgeführt. Mit folgender Client-Anwendung kann das getestet werden:import java.rmi.Naming; public class ShopClient { public static void main(String args[]) { try { ShopServer server = (ShopServer)Naming.lookup("shop-server"); Cart cart = server.createCart(); cart.addProduct("Coffee"); cart.addProduct("Tea"); cart.addProduct("Java"); System.out.println("listing contents ..."); String[] contents = cart.listContents(); for(int i=0; i < contents.length; i++) System.out.println(" - "+contents[i]); cart.buy("rsinger"); } catch(Exception ex) { ex.printStackTrace(); } } }Nacheinander werden in diesem Beispiel die verschiedenen Methoden auf dem Server aufgerufen. Zunächst wird über den Namens-Dienst das zentrale ShopServer-Exemplar ermittelt. Von diesem Exemplar wird anschließend ein neuer Warenkorb abgerufen, dem drei Produkte hinzugefügt werden. Danach wird der Inhalt des Warenkorbs aufgelistet und schließlich durch Aufruf von buy() gekauft. Wenn man die Anwendung ausführt, kann man feststellen, dass die Ausgaben in CartImpl alle im Server-Prozess ausgegeben werden und der Client somit lediglich einen Verweis auf den im Server gespeicherten Warenkorb bekommt.
Material zum Beispiel