Segédanyag: Java alkalmazások gyakorlat Készítette: Szabó Attila 2009/2010-2 félév, 12. gyakorlat 1 Távoli metódushívás Java-ban (RMI) Java-ban a távoli metódushívás ( Remote Method Invocation, RMI) egy magas szintű eszköz a különböző JVM-ben (esetleg más-más gépen) futó programok kommunikációjára. Az RMI felületet nyújt ahhoz, hogy egy adott virtuális gépen futó program meghívja egy másik virtuális gépben futó programban létező objektum ( remote object ) egy metódusát úgy, mintha ez az objektum jelen lenne a hívó programban. Egy RMI alkalmazás legalább két programból áll. A két programból legalább egy (de akár mindkettő) létrehoz egy objektumot, és elérhetővé teszi az objektumra mutató referenciát. Ezt az objektumot más programok elérhetik (azaz meghívhatják a metódusait), ha szereznek egy megfelelő referenciát. Ehhez a működéshez három feltételt kell biztosítani: nyilvántartást kell vezetni a távolról elérhető objektumokról, meg kell hívni az objektum megfelelő metódusát, illetve a távolról hívható objektumok osztályait elérhetővé kell tenni. A fenti feltételek biztosításához a következő komponensekre van szükség: rmiregistry: a távolról elérhető objektumokat egy RMI regiszter tartja nyilván. Az objektumot létrehozó program ide regisztrálja be az objektumra mutató referenciát, illetve a kliens program innen kérheti le azt. RMI szerver: az objektumot megosztó program, ami az RMI regiszterbe bejegyzi a kliens által ismert néven az objektumra mutató referenciát. RMI kliens: a távoli objektum metódusát hívó program, ami az RMI regiszterből lekéri a távoli objektum eléréséhez szükséges referenciát, és meghívja valamelyik metódusát. web szerver: a kliens programnak le kell töltenie a távoli objektum osztályát (a kliens program csak egy interfészt ismer, a szerveren lévő megvalósítást futás közben tölti be a JVM). Nem szükséges web szerver akkor, ha a megfelelő osztály implementációja rendelkezésre áll a fájlrendszerben Az RMI futásához szükséges komponensek az alábbi ábrán láthatóak (a nyilak feliratai a kommunikációs módot adják meg). 1. Ábra: Az RMI rendszer részei(forrás: http://java.sun.com/docs/books/tutorial/rmi/overview.html) 1
1.1 RMI programok felépítése Az RMI programok felépítésükben csak annyiban különböznek a megszokottaktól, hogy a távolról hívható objektumok osztályainak implementálniuk kell a java.rmi.remote interfészt egy leszármazottját. Szükséges továbbá, hogy a leszármazott interfész által előírt összes függvény deklarálja, hogy dobhat java.rmi RemoteException-t. (Ezt az interfészt kell a kliensnek ismernie a metódusok meghívásához.) Az rmidemo.time interfész: import java.rmi.remote; /** * Pontos ido szolgaltatas interfesze. */ public interface Time extends Remote { /** * Visszater a pontos idovel. */ String gettimestamp() throws RemoteException; Az rmidemo.clockengine osztály: import java.rmi.registry.locateregistry; import java.rmi.registry.registry; import java.rmi.server.unicastremoteobject; public class ClockEngine implements Time{ = //implemented interfaces public synchronized String gettimestamp() throws RemoteException{ //ide jon a pontos ido megvalositasa return new String( "2010.05.04. 08:35:00" ); = //main public static void main(string[] args) { if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); try { String name = "Time"; //egy egyszeru objektum, amit meg fogunk osztani RMI-n keresztul Time engine = new ClockEngine(); //hozzaferhetove teszi az objektumot Time stub = (Time) UnicastRemoteObject.exportObject(engine, 0); //lekeri az RMI registry-t Registry registry = LocateRegistry.getRegistry(); //regisztralja az objektumot a megadott nevvel registry.rebind(name, stub); System.out.println("ClockEngine bound"); 2
catch (Exception e) { System.err.println("ClockEngine exception:"); e.printstacktrace(); Az rmidemo.clockdisplay osztály: import java.rmi.registry.locateregistry; import java.rmi.registry.registry; public class ClockDisplay { //main public static void main(string args[]) { if( args.length!= 2 ){ System.out.println( "Usage: ClockDisplay <hostname>" ); if (System.getSecurityManager() == null) { System.setSecurityManager(new SecurityManager()); try { String name = "Time"; //lekeri a registry-t az elso parameteben megadott geprol Registry registry = LocateRegistry.getRegistry(args[0]); //nev alapjan lekeri a tavoli objektumra mutato referenciat Time time = (Time) registry.lookup(name); //meghivja a tavoli objektum metodusat String currenttime = time.gettimestamp(); System.out.println("Current time: " + currenttime); catch (Exception e) { System.err.println("ClockDisplay exception:"); e.printstacktrace(); Az inkonzisztens állapot elkerüléséhez a megosztott objektumok implementációjában használjunk synchronized metódust! (Egy objektum szinkronizált metódusának meghívásától annak lefutásáig egyetlen másik szál sem hívhatja az objektum szinkronizált metódusait.) 1.2 RMI programok futtatása A futtatáshoz szükség van néhány JVM paraméterre, és egy policy fájlra. A távolról meghívandó objektumok osztályait el kell helyezni a kliens által hozzáférhető helyen, mondjuk egy jar fájlban. Ezen kívül el kell indítani az rmiregistry programot, ami megtalálható a jre bin könyvtárában. A policy fájl szabályozza a beregisztrált objektumhoz történő hozzáférést: grant { ; permission java.security.allpermission; A szerver JVM paraméterei: -Djava.security.policy: az egyenlőségjel után meg kell adni a policy fájl helyét, illetve nevét (a lentebbi példában a fájl az aktuális könyvtárban van, és nincs kiterjesztése!). 3
Példa: -Djava.rmi.server.codebase: az egyenlőségjel után meg kell adni a távoli objektumok implementációinak elérhetőségét (lásd a lenti példát). Ha a jar a neten érhető el, akkor az URL-t kell megadni, ha elérhető lokálisan, akkor a file:/ előtag után meg kell adni a hivatkozást. -Djava.rmi.server.hostname: az egyenlőségjel után meg kell adni a szerver nevét (lásd a lenti példát). E:\java -Djava.security.policy=policy -Djava.rmi.server.codebase=file:/E:\workspace\RMIDemo\rmidemo.jar -Djava.rmi.server.hostname=localhost rmidemo.clockengine A kliens JVM paraméterei: Példa: -Djava.security.policy: az egyenlőségjel után meg kell adni a policy fájl helyét, illetve nevét (a lentebbi példában a fájl az aktuális könyvtárban van, és nincs kiterjesztése!). -Djava.rmi.server.codebase: az egyenlőségjel után meg kell adni a távoli objektumok implementációinak elérhetőségét (lásd a lenti példát). Ha a jar a neten érhető el, akkor az URL-t kell megadni, ha elérhető lokálisan, akkor a file:/ előtag után meg kell adni a hivatkozást. E:\java -Djava.security.policy=policy -Djava.rmi.server.codebase=file:/E:\ELTE\PhD_IV.felev\Java_alkalmazasok_gyak\pel daprogramok\workspace\rmidemo\rmidemo.jar rmidemo.clockdisplay localhost 2 Feladatok 1. Írd meg a 8. gyakorlaton bemutatott EchoServer osztályt úgy, hogy nem socket-en keresztül lehet elérni a szolgáltatását, hanem egy megosztott objektum segítségével! Készítsd el a kliens programot is! Használj szinkronizált metódust. 2. Írj egy távolról hívható kalkulátort (csinálj hozzá tesztklienst is)! Az interfész: import java.rmi.remote; public interface Calculator extends java.rmi.remote { public long add(long a, long b) throws java.rmi.remoteexception; public long sub(long a, long b) throws java.rmi.remoteexception; public long mul(long a, long b) throws java.rmi.remoteexception; public long div(long a, long b) throws java.rmi.remoteexception; 3. Írj egy távoli naplózó programot! A naplózó tudja rögzíteni az új bejegyzéseket, illetve lekérhető tőle a teljes napló. 3 Az előadáson leadott anyagok A második zárthelyi elméleti részére való felkészüléshez összefoglalom ide, hogy mikről volt szó az előadáson (zárójelben megadtam az előadások dátumát: ebből látszik, hogy a lista nem teljes, tessék a hiányzó előadások anyagát kideríteni...). A felkészüléshez használjátok a tárgy honlapján lévő diákat, a Java honlapját, na meg a Google-t, ha kell. 4
(02.19.) String/StringBuilder, és általában az immutable/final kérdéskör, a vezérlési szerkezetek (kivéve a try-catch-finally-t), a kifejezéskiértékelés, hatóköri szabályok, static tagok, megjegyzések (02.26.) csomagok, a forráskód és a class fájlok elhelyezése, classpath, paraméterátadás, túlterhelés, inline-osítás, vararg, ellenőrzött kivételek (03.19.) beágyazott osztályok (03.26.) generic-ek (04.09.) kovariancia, felüldefiniálás, @Override, clone(), equals() (04.16.) generic-ek (04.30.) generic-ek (05.07.) Thread, Runnable, életciklus, ütemezés, yield(), sleep(), blokkoló művelet, közös változók problémái, synchronized 5