Höhere Programmierkonzepte Praktikum IV Verteilte Programmierung Prof. Dr. Nikolaus Wulff 11. – 20. Dez 2012 1 Verteilte Dienste mit RMI Ziel diesen Praktikums ist es, die Bibliothek aus Praktikum III als verteilten Dienst anzubieten. Die Schnittstelle java.rmi.Remote kennzeichnet beliebige, verteilbare Java RMI Objekte, die per TCP/IP im Intra- und oder Internet erreichbar sind. Der Quelltext (1) definiert eine generische Schnittstelle, die sich der RMI Architektur bedient. package de.lab4inf.lab4; /∗∗ ∗ Common interface for an arbitrary, generic remote service. ∗ ∗ @param <T> task to accomplish ∗ @param <A> arguments for the task ∗ @param <R> return object from the task ∗/ public interface Service<T,A,R> extends java.rmi.Remote { /∗∗ ∗ Every service has it ’s unique name. ∗ @return name of service ∗ @throws RemoteException in case of an error ∗/ String getName() throws java.rmi.RemoteException; /∗∗ ∗ Execute the given task within the service . ∗ @param task T to execute ∗ @param args arguments A of the task ∗ @return the result R calculated with T and A. ∗ @throws RemoteException in case of an error ∗/ R execute(T task,A ... args) throws java.rmi.RemoteException; } Listing 1: Generische Java Schnittstelle für einen beliebigen RMI Service. 1 package de.lab4inf.lab4; import java.rmi.∗; /∗∗ ∗ Simple client of the EchoService. ∗/ public class EchoClient { public static void main(String[] args) throws Exception { Service<String,String,String> service; String t ,a,r , url = ”//localhost/EchoService”; service = (Service<String,String,String>) Naming.lookup(url); t = ”hello world”; a = ”top secret”; System.out.printf(”service .execute(%s,%s) \n”,t,a); r = service.execute(t,a); System.out.printf(”returned: %s \n”,r); } } Listing 2: Ein einfacher Client für den EchoService. Java Klassen, welche die Serviceschnittstelle (1) implementieren, können mittels Java RMI im TCP/IP Netzwerk erreich- und aufrufbar sein. Im Quelltext (3) des Anhangs finden Sie eine einfache Implementierung eines EchoService, der sich beim Start seiner main-Funktion bei der RMI Registry mit dem Aufruf Naming.rebind registriert und auf einkommende Anfragen wartet. Der EchoService erwartet als Task T und Argument A eine Zeichenkette, die er leicht modifiziert an den Aufrufenden als Ausgabe R zurückliefert. Obiger Quelltext (2) zeigt einen Klienten des EchoService, der per RMI versucht auf die Serviceschnittstelle zuzugreifen. Per Naming.lookup erhält der Klient von der RMI Registry eine Referenz auf einen Service und kann dessen execute Methode aufrufen. Beachten Sie, wie der EchoService zugleich die Service-Schnittstelle implementiert und die java.rmi.server.UnicastRemoteObject Basisklasse erweitert. Letzteres erlaubt es den Service innerhalb der main-Funktion bei der RMI Registry unter Port 1099 anzumelden und im Netz per TCP/IP zur Verfügung zu stellen. Basisfunktionalität eines RemoteServers wird ohne größeren Programmieraufwand durch Vererbung bereitgestellt. Aufgabe Aufgabe ist es nun den Differenzierer und den Integrierer des letzten Praktikums als zwei verteilte Services erreichbar zu machen, so dass diese in unterschiedlichen Prozessräumen aufrufbar sind. Nach Vorlage des EchoService werden der DifferentiatorService und der IntegratorService entwickelt. Diese ”umwickeln” als Wrapper die schon existierenden Differentiator und Integrator Klassen und ma2 chen sie remote-fähig, ohne das Differentiator und Integrator oder die C/C++ Bibliothek verändert werden müssen. 1. Erstellen Sie den EchoService und den EchoClient und führen Sie diese in der Reihenfolge EchoService, EchoClient in zwei getrennten Konsolen auf Ihrem Rechner aus. 2. Sobald Server und Client zufriedenstellend laufen schreiben Sie nach der Vorlage des EchoClient einen JUnitTest, um den Service automatisiert zu testen. 3. Nachdem der JUnit Test läuft nehmen Sie ein Refactoring vor. Zerlegen Sie den Test in eine abstrakte Basisklasse und einen konkreten Anteil für den EchoServiceTest. D.h. faktorisieren Sie die Anteile, die sich nicht direkt auf den EchoService beziehen in einen abstract class BasicServiceTest<T,R,A> JUnit Test. Diese generische Basisklasse – mit den selben Generics T,R,A wie der Service – dient als Elternklasse für die weiteren zu erstellenden Testklassen. 4. Erst wenn das Echo-Beispiel per RMI läuft, lohnt es sich an die Bereitstellung und Entwicklung weiterer Services zu denken. Erstellen Sie den DifferentiatorService, der den Differentiator des letzten Praktikums kapselt und auf Verlangen ausführt und testen Sie ihn durch Erweiterung des BasicServiceTest<T,R,A> mit einer entsprechend typisierten DifferentiatorServiceTest Klasse. 5. Erstellen Sie einen Service IntegratorService, der den Integrator des letzten Praktikums kapselt und auf Verlangen ausführt und schreiben Sie auch hierzu einen passenden IntegratorServiceTest. Tip Denken Sie daran, bei den Tests wieder den Systempfad für die JNI Bibliothek mit anzugeben. Die Bindung des EchoService ist <T,R,A>=<String,String,String> entsprechend gelten für den Differentiator- und den IntegratorService <T,R,A>=<Function,Double,Double>, so dass sowohl die Function als auch die Argumente vom Type double übergeben werden können. Voraussetzung für die Übergabe von Argumenten per RMI ist, dass diese Java Primitive sind oder aber die java.io.Serializable Schnittstelle implementieren, d.h. die Funktionen müßen serialisierbar deklariert sein. 3 package de.lab4inf.lab4; import java.rmi.registry.∗; /∗∗ ∗ Echo RMI Service. ∗/ public class EchoService extends java.rmi.server.UnicastRemoteObject implements Service<String, String, String> { /∗∗ ∗ Sole RMI constructor. ∗/ public EchoService() throws java.rmi.RemoteException {} /∗ (non−Javadoc) ∗ @see de.lab4inf . lab4 . Service#getName() ∗/ @Override public String getName() throws java.rmi.RemoteException { return ”EchoService”; } /∗ (non−Javadoc) ∗ @see de.lab4inf . lab4 . Service#execute(java.lang.Object, A[]) ∗/ @Override public String execute(String task, String ... args) throws java.rmi.RemoteException { return String.format(”Echo => %s [%s]”,task,args[0]); } } /∗∗ ∗ Start and register the service . ∗/ public static void main(String[] args) throws Exception { String url = ”//localhost/EchoService”; Registry registry = LocateRegistry.createRegistry(1099); EchoService service = new EchoService(); Naming.rebind(url, service) ; System.out.printf(”Service %s registered\n”,url) ; } Listing 3: Ein einfacher EchoService verteilt per RMI. 4