Remote Method Invocation RMI



Podobné dokumenty
Soketové připojení Klient InetAddress getbyname() UnknowHostException SecurityException Socket getinputstream() getoutputstream() IOException Server

RMI Remote Method Invocation

Jini (pronounced GEE-nee) Cvičení 8 - DS 2006

JAVA V RMI Java, letní semestr

RMI - Distribuované objekty v Javě

java remote method invocation Kateřina Fricková, Matouš Jandek

JAVA RMI Java, letní semestr 2018

Katedra měřicí a řídicí techniky, VŠB - Technická univerzita v Ostravě, tř. 17. listopadu, Ostrava-Poruba, Česká republika

RMI - Remote Method Invocation

Obsah přednášky 7. Základy programování (IZAPR) Přednáška 7. Parametry metod. Parametry, argumenty. Parametry metod.

Výčtový typ strana 67

Soubor jako posloupnost bytů

Bezpečnost v Javě. Architektura zabezpečení jazyka Java.

Class loader. každá třída (java.lang.class) obsahuje referenci na svůj class loader. Implementace class loaderu

Statické proměnné a metody. Tomáš Pitner, upravil Marek Šabo

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Generické programování

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Abstraktní třída a rozhraní

8. přednáška: Soubory a proudy

Seminář Java II p.1/43

3. Třídy. Základní pojmy objektového programování. Třídy

7. přednáška - třídy, objekty třídy objekty atributy tříd metody tříd

Úvod do programovacích jazyků (Java)

17. Projekt Trojúhelníky

Definice třídy. úplná definice. public veřejná třída abstract nesmí být vytvářeny instance final nelze vytvářet potomky

20. Projekt Domácí mediotéka

Programování v Javě I. Leden 2008

Z. Kotala, P. Toman: Java ( Obsah )

Programování v Javě I. Únor 2009

SOUBORY, VSTUPY A VÝSTUPY POKRAČOVÁNÍ

TŘÍDY POKRAČOVÁNÍ. Události pokračování. Příklad. public delegate void ZmenaSouradnicEventHandler (object sender, EventArgs e);

1. Programování proti rozhraní

Vytváření a použití knihoven tříd

8 Třídy, objekty, metody, předávání argumentů metod

Java - výjimky. private void vstup() throws IOException {... }

Java aplety. Předávání parametrů z HTML

Obsah přednášky 9. Skrývání informací. Skrývání informací. Zapouzdření. Skrývání informací. Základy programování (IZAPR, IZKPR) Přednáška 9

Programovací jazyk Java

Tento studijní blok má za cíl pokračovat v základních prvcích jazyka Java. Konkrétně bude věnována pozornost rozhraním a výjimkám.

IRAE 07/08 Přednáška č. 7. Začátek (head)

Počítačové laboratoře bez tajemství aneb naučme se učit algoritmizaci a programování s využitím robotů CZ.1.07/1.3.12/

10 Balíčky, grafické znázornění tříd, základy zapozdření

Osnova. GIOP a IIOP IDL IOR POA. IDL Klient Server. 2 Historie. 3 Princip a základní pojmy. 4 Implementace. 5 Aplikace CORBA

Algoritmizace a programování

typová konverze typová inference

Dynamicky vázané metody. Pozdní vazba, virtuální metody

Při studiu tohoto bloku se předpokládá, že student je zvládá základy programování v jazyce Java s využitím vývojového prostředí NetBeans.

InputStream. FilterInputStream

PŘETĚŽOVÁNÍ OPERÁTORŮ

KTE / ZPE Informační technologie

Úvod Třídy Rozhraní Pole Konec. Programování v C# Hodnotové datové typy, řídící struktury. Petr Vaněček 1 / 39

Algoritmizace a programování

11 Diagram tříd, asociace, dědičnost, abstraktní třídy

IRAE 07/08 Přednáška č. 2. atr1 atr2. atr1 atr2 -33

Úvod do programovacích jazyků (Java)

Testování. Zadání příkladu. Vytvoření kostry třídy. Obsah:

Textové soubory. alg9 1

Abstraktní datové typy: zásobník

Platforma.NET 11.NET Framework 11 Visual Basic.NET Základní principy a syntaxe 13

Úvod do programování - Java. Cvičení č.4

Třídy, polymorfismus. A0B36PR2-Programování 2 Fakulta elektrotechnická České vysoké učení technické

Principy objektově orientovaného programování

1. Dědičnost a polymorfismus

7. Datové typy v Javě

Základy objektové orientace I. Únor 2010

Využití OOP v praxi -- Knihovna PHP -- Interval.cz

Dědění, polymorfismus

7 Formátovaný výstup, třídy, objekty, pole, chyby v programech

Více o konstruktorech a destruktorech

Tvorba informačních systémů

PB161 Programování v jazyce C++ Přednáška 7

PB161 Programování v jazyce C++ Přednáška 7

Výjimky. v C# a Javě

Programování v C++ 3, 3. cvičení

7. Statické prvky třídy

NetBeans platforma. Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti

Java a XML. 10/26/09 1/7 Java a XML

UJO Framework. revoluční architektura beans. verze

Webové služby a XML. Obsah přednášky. Co jsou to webové služby. Co jsou to webové služby. Webové služby a XML

1 Správce licencí Správce licencí Správce licencí Start > Všechny programy > IDEA StatiCa > Správce licencí Soubor > Správce licencí Licence

PREPROCESOR POKRAČOVÁNÍ

Pokročilé programování v jazyce C pro chemiky (C3220) Operátory new a delete, virtuální metody

Vlákna. První jednoduchý program s vlákny:

29. Výjimky, jejich vznik, vyhození, odchyt a zpracování. (A7B36PVJ)

Iterator & for cyklus

Semin aˇr Java V yjimky Radek Ko ˇc ı Fakulta informaˇcn ıch technologi ı VUT Unor 2008 Radek Koˇc ı Semin aˇr Java V yjimky 1/ 25

Dědičnost (inheritance)

Vaše jistota na trhu IT. Balíčky. Rudolf Pecinovský

IRAE 07/08 Přednáška č. 1

Java GUI události. Událostmi řízené programování. Zpracování = obsluha událostí

Java Výjimky Java, zimní semestr

Teoretické minimum z PJV

Seminář Java IV p.1/38

Úvod do programovacích jazyků (Java)

Předmluva k aktuálnímu vydání Úvod k prvnímu vydání z roku Typografické a syntaktické konvence... 20

Chování konstruktorů a destruktorů při dědění

Transkript:

Remote Method Invocation RMI Java TM Remote Method Invocation (RMI) umožňuje objektu na jedné Java Virtual Mashine(JVM) jednoduše spustit metodu jiného objektu na vzdálené JVM. Při volání vzdálené metody získává klient referenci na vzdálený objekt prostřednictvím jmenné služby poskytované RMI. RMI používá objektovou serializaci pro posílání parametrů metod a návratových hodnot, tj. posílají se objekty, u kterých je možno využít všech jejich vlastností včetně polymorfismu. Vzdálené volání metod má tu samou syntaxi jako volání metod lokálních objektů. RMI poskytuje podporu pro dynamické stahování kódu. Součásti RMI Funkční RMI systém má několik částí Rozhraní definující vzdálené služby Implementaci vzdálené služby a server poskytující tuto implementaci Soubor se stubem, který umožňuje vzdálenou komunikaci RMI jmennou službu, která umožňuje klientovi službu najít Klientský program, které požaduje vzdálené služby Poskytovatele, který umožní dynamické stahování kódu (HTTP nebo FTP server) Veškeré třídy, rozhraní a nástroje pro vytvoření a spuštění RMI aplikace jsou součástí J2SE. Třídy a rozhraní pro RMI jsou obsaženy ve více balíčcích. Nejpoužívanější jsou balíčky java.rmi a java.rmi.server. Rozhraní definující vzdálené služby vzdálené rozhraní. Vzdáleně mohou být volány pouze metody deklarované v tzv. vzdáleném rozhraní. Vzdálené rozhraní je potomkem rozhraní java.rmi.remote. V tomto rozhraní deklarujeme vzdáleně volané metody. Každá metoda musí vyhazovat výjimku java.rmi.remoteexception. Parametry a návratové hodnoty mohou být jakéhokoli typu, který je možno serializovat. Primitivní datové typy je možno používat bez problémů. Implementace vzdálené služby vzdálený objekt Je třeba vytvořit implementaci vzdáleného rozhraní, tj.třídu, která bude součástí serveru. Tato třída bude implementovat rozhraní definující vzdálené služby a musí mít konstruktor vyhazující výjimku java.rmi.remoteexception. Třída může obsahovat jakékoli další metody, ty však nebude možno volat přímo přes RMI. Pokud nepotřebujeme tuto třídu napsat jako potomka jiné třídy, použijeme dědičnost od třídy java.rmi.server.unicastremoteobject. V případě, že chceme aby třída dědila od jiného předka než je UnicastRemoteObject, musí konstruktor vaší třídy obsahovat tento kód: UnicastRemoteObject.exportObject(this); Volání této metody (či volání konstruktoru předka, v případě dědění od třídy UnicasteRemoteObject) zahájí čekání tohoto objektu na volání metody klientem. V okamžiku, kdy klient požádá server o službu, je pro zpracování příchozího požadavku vytvořeno samostatné vlákno. Službu je tedy možno poskytovat více klientům najednou. 75

V metodě main třídy serveru (může to být třída implementující vzdálené rozhraní nebo samostatná třída) je nutné: Vytvořit instanci rozhraní, které definuje vzdálené služby. A to tak, že vytvoříme instanci třídy implementující toto rozhraní a přetypujeme ho na typ tohoto rozhraní. Zaregistrovat se u jmenné služby RMI rmiregistry Registrace proběhne pomocí třídy java.rmi.naming. Tato třída má několik metod třídy, které si nyní objasníme. static void bind(string name, Remote obj) zaregistruje službu se zadaným jménem a převezme odkaz na objekt, který ji reprezentuje. Parametr obj je tedy instance vzdáleného rozhraní. Pokud již služba tohoto jména existuje, vyhodí výjimku. java.rmi.alreadyboundexception. static void rebind(string name, Remote obj) od metody bind se liší tím, že pokud již služby tohoto jména existuje, je její registrace přepsána touto novou. static String[] list(string name) vrací pole se jmény zaregistrovaných služeb static Remote lookup(string name) vrací referenci na stub objektu, který je asociován s daným jménem služby. I třída stubu implementuje vzdálené rozhraní, je ji tedy možno přetypovat na java.rmi.remote. V případě, že služba tohoto jména není na daném počítači registrována je vyhozena výjimka java.rmi.notboundexception static void unbind(string name) zruší registraci služby daného jména. Jméno služby je stringová hodnota s následující strukturou: //host:port/jmeno host:port udávají počítač a port, na kterém je spuštěno RMIregistry pokud není uveden host, bere se localhost pokud není určen jiný port, použije se implicitní hodnota 1099 jmeno je jednoznačné jméno služby Stuby Při vzdáleném volání metod používá RMI pro komunikaci instance stubů 5. Instance stubu odpovídající volanému vzdálenému objektu je poslána na klienta, klient volá metodu na stubu. Na straně serveru je vytvořena instance stubu a tyto dvě instance mezi sebou komunikují pomocí socketů. Stub na straně klienta převezme požadavek na volání metody a převede jej do streamů, pro převod všech parametrů je použita serializace. Tato činnost se v distribuovaných systémech nazývá marshalování. Požadavek je přes TCP/IP komunikaci přenesen na server, kde je instance stubu (případně skeletonu) převede za pomoci serializace zpět do podoby volání metody. Metoda je pak spuštěna a vrátí návratovou hodnotu stubu, který ji marshaluje na klienta. Stub na klientovi provede demarshalování a předá výsledek metody klientskému objektu. Kód stubu je generován pomocí nástroje rmic. Tento nástroj na základě implementace rozhraní vytvoří vlastní zdrojový kód stubu, přeloží jej a, pokud neuvedete jinak, tento zdrojový kód odstraní. Pokud používáte Javu verze 1.4, není třeba vytvářet skeletony. Nová verze protokolu 1.2 je již nevyžaduje. Použijte tedy při vytváření přepínač v1.2. Pokud si chcete prohlédnout zdrojový kód stubu, použijte přepínač keep. 5 V starších verzích Javy (do verze 1.3.1) byly pro komunikaci používány i instance skeletonů. Vytváří se pomocí rmic bez parametru v1.4. Distribuují se stejným způsobem jako stuby. 76

Jmenná služba RMIregistry Pro registraci RMI má Java jednoduchou registrační službu RMIregistry. Tuto službu je možné spustit dvěma způsoby. Z příkazového řádku pomocí příkazu rmiregistry. Tuto službu musíte mít spuštěnou před samotným spouštěním serveru a klienta. Pokud nechceme používat standardní číslo portu pro RMI tj. 1099, uvedeme číslo portu jako parametr při spuštění. Tuto službu je třeba spouštět v jiném adresáři, než je adresář serveru. Na serveru pomocí statické metody java.rmi.registry.locateregistry.createregistry(int port), která má jako parametr číslo portu. V této variantě je třeba uvádět i implicitní číslo portu 1099. Tuto metodu voláme v metodě main serveru před registrací vzdálené služby. Klient Při tvorbě klienta narozdíl od lokálního spouštění metody musíme nejdříve získat referenci na stub vzdáleného objektu. Tato činnost se provádí pomocí již zmíněné metody lookup() třídy Naming. Pokud používáme dynamické stahování kódu, je také vyžadováno vytvoření instance třídy java.rmi.rmisecuritymanager a nastavení odpovídajících bezpečnostních politik pro přístup k jednotlivým souborům či portům. Poskytovatel pro dynamické stahování kódu Je možné vytvořit funkční RMI aplikaci bez dynamického stahování kódu. Pak ovšem při každé změně implementace vzdáleného rozhraní budeme muset na všechny klienty nakopírovat novou verzi stubu. Dynamicky si klient obvykle stahuje class soubor stubu. Je možné, aby si klient i server stahovali soubory.class tříd, instance kterých jsou návratovými hodnotami nebo parametry vzdáleně volaných metod. RMI umožňuje i dynamické stahování kódu v rámci jednoho počítače, kdy server a klient běží ve dvou nezávislých instancích JVM a příslušný kód si klient nebo i server stahují s určených adresářů. To umožňuje ladění RMI aplikací s dynamickým stahováním kódu na jednom stroji. Při praktickém běhu aplikace se pak používá stahování pomocí FTP nebo HTTP. Informace o umístění stubu a případně i dalších souborů class poskytuje server prostřednictvím své systémové proměnné java.rmi.server.codebase. Stejnou proměnnou používá i klient pro informování serveru o umístění pro něj potřebných souborů. RMI využívá pro zajištění bezpečnosti instanci třídy RMISecurityManager, je tedy nutné nastavovat bezpečnostní politiky pro některé porty a případně i adresáře (pokud se soubory class čtou z lokálního disku). Komunikace mezi jednotlivými částmi RMI aplikace s dynamickým stahováním kódu Na následujícím obrázku je uveden postup komunikace při spouštění RMI aplikace. Je zde uvedena varianta, kdy je dynamicky stahován kód stubu na klienta. Server má k dispozici všechny potřebné class soubory. 77

Server java.rmi.server.codebase=url location 1 9 8 RMIregistry 2 3 URL location (ftp, http nebo file) 4 5 6 7 Klient Obrázek č. 11 Znázornění postupu komunikace při vzdáleném volání metody. 1. Server si vytvoří svoji instanci stubu a registruje službu u RMIregistry, tj. nastaví její jméno, pošle instanci vzdáleného rozhraní a obsah proměnné java.rmi.server.codebase. (V případě, kdy se kód nestahuje dynamicky, je tato proměnná nastavena na aktuální adresář serveru.) 2. RMIregistry si na základě udané codebase požaduje soubory class stubu a vzdáleného rozhraní, aby mohl vytvořit instanci stubu. 3. RMIregistry získá požadované soubory. 4. Klient pomocí metody lookup() hledá službu daného jména u služby RMIregistry. 5. RMIregistry poskytuje klientovi instanci stubu vzdáleného objektu, informace o serveru a příslušnou codebase. 6. Klient potřebuje class soubor stubu, pokud ho nenajde na své classpath, požaduje tento soubor z URL uvedené v codebase serveru. 7. Klient získá odpovídající soubor class stubu. 8. Klient volá vzdálenou metodu na instanci stubu, ta provede odpovídající serializaci parametrů a pošle požadavek na server, kde je deserializován a spuštěna metoda. 9. Výsledek metody je opět serializován a pomocí streamů poslán na klienta, kde stub provede deserializaci a předá výsledek klientovi. Při dynamickém stahování kódu a pro komunikaci mezi klientem a serverem je možné použít i zabezpečenou komunikaci přes SSL. Pak je třeba napsat si vlastní implementaci rozhraní java.rmi.server.seversocketfactory a java.rmi.server.clientsocketfactory, které budou používat šifrování. Bližší informace naleznete na stránkách firmy Sun. Příklad jednoduché aplikace používající RMI Napíšeme velmi jednoduchou aplikaci, která bude umožňovat na serveru sčítat dvě celá čísla. V celém příkladě jsou záměrně všechny výjimky posílány až na JVM, aby ukázkový kód nebyl příliš rozsáhlý. V praxi je samozřejmě nutné výjimky ošetřit. Pro jednoduchost vytvoříme v prvním kroku RMI aplikaci, která bude celá v jednom adresáři. Po jejím zprovoznění si ukážeme jak postupovat při další distribuci 78

Vytvoření rozhraní definujícího vzdálené služby. Vytvoříme tedy rozhraní, definující metodu pro sčítání dvou čísel. Rozhraní musí být potomkem java.rmi.remote a metoda musí vyhazovat výjimku java.rmi.remoteexception. import java.rmi.*; public interface ScitaniCisel extends Remote { int secti (int a, int b) throws RemoteException; Vytvoření implementace vzdálené služby. Vytvoříme třídu, která bude implementovat ScitaniCisel. import java.rmi.*; import java.rmi.server.*; import java.net.*; public class ScitaniNaServeru extends UnicastRemoteObject implements ScitaniCisel { public int secti (int a, int b) throws RemoteException { return a+b; public ScitaniNaServeru() throws RemoteException { super(); Vytvoření serveru Server vytvoříme jako samostatnou třídu, která vytvoří instanci vzdáleného rozhraní a zaregistruje službu se jménem Scitani na localhostu na portu 1099. import java.rmi.*; import java.rmi.server.*; import java.net.*; public class Server { public static void main(string[] args) throws RemoteException, MalformedURLException { String name = "///Scitani"; ScitaniCisel scitani = new ScitaniNaServeru(); Naming.rebind(name, scitani); System.out.println("Probehla registrace"); 79

Vytvoření třídy stubu Nyní vytvoříme stub pomocí rmic. Jako parametr tohoto příkazu uvedeme jméno třídy implementující vzdálené rozhraní, tj. v našem příkladě VzdálenyObjekt. Příkazová řádek může vypadat takto: rmic v1.4 keep ScitaniNaServeru Po provedení tohoto příkazu budou v aktuálním adresáři vytvořeny dva soubory ScitaniNaServeru _Stub.java a ScitaniNaServeru _Stub.class. Vytvoření klienta Nyní vytvoříme jednoduchého klienta, který bude využívat metodu secti() na serveru. V cyklu vygenerujeme 10x náhodná čísla, která budeme vzdáleně sčítat. Stejně jako v případě serveru i u klienta je vynecháno ošetření výjimek. V případě klienta musí být ošetřeny tyto výjimky: RemoteException, NotBoundException, java.net.malformedurlexception. Jméno služby je Scitani a hledáme ji na localhostu na portu 1099. Kód klienta je uveden v následujícím výpisu. import java.rmi.*; public class Klient { public static void main (String [] args) throws Exception { String name = "///Scitani"; ScitaniCisel scitani = (ScitaniCisel) Naming.lookup(name); for( int i = 0; i<10; i++) { int a = (int)(math.random()*10); int b = (int)(math.random()*10); System.out.println("Vysledek je :" + scitani.secti(a,b)); Spuštění registrační služby; RMIregistry spustíme z příkazového řádku příkazem rmiregistry (spouštíme ho z adresáře serveru). Budeme používat port 1099, neuvedeme tedy žádný parametr. Spuštění celého příkladu bez dynamického stahování kódu. Pokud tedy máme spuštěnou jmennou službu RMIregistry a vytvořen stub třídy serveru, můžeme spustit server, a poté klienta v samostatných instancích JVM. Pokud nyní zkusíte rozdělit aplikaci do dvou adresářů nebo na dva různé stroje a nebudete požadovat dynamické stahování kódu, bude aplikace fungovat. Znamená to pouze,že na počítači (případně v adresáři) klienta musíte mít tyto soubory class (pro verzi Javy 1.4 a vyšší): ScitaniCisel.class Klient.class ScitaniNaServeru_Stub.class Při síťovém spouštění musíte upravit jméno služby v metodě lookup(), aby se služba hledala na počítači se spuštěným RMIregistry a serverem. 80

Pro server platí, že musí mít spuštěné rmiregistry a dostupné soubory class: Server.class ScitaniCisel.class ScitaniNaServeru.class ScitaniNaServeru_Stub.class Spuštění celého příkladu s dynamickým stahováním kódu. Dalším logickým krokem je vyzkoušet fungování RMI, pokud je požadováno dynamické natažení kódu stubu na klienta. Tuto funkčnost je možno vyzkoušet i na jednom počítači, pokud rozdělíme kód klienta a kód serveru do dvou adresářů tak, že v adresáři klienta nebude soubor se stubem a při spouštění nebude dostupný ani nikde na CLASSPATH. Tento kód bude pro potřeby klienta umístěn v dalším adresáři spolu s kódem vzdáleného rozhraní. Důvodem k tomu je fakt, že RMIregistry hledá potřebný kód podle codebase a potřebuje stáhnout i kód vzdáleného rozhraní. Adresářová struktura našeho příkladu bude následující: H:\server\ H:\klient\ H:\zdroj\ Server.class ScitaniCisel.class ScitaniNaServeru.class ScitaniNaServeru_Stub.class ScitaniCisel.class.class Klient.class ScitaniCisel.class.class ScitaniNaServeru_Stub.class Před spuštěním jmenné služby se nastavíme do adresáře, který není přístupný přes classpath serveru! Při spouštění serveru se v této variantě nic nemění. Při spouštění klienta je však vyhozena výjimka java.rmi.unmarshalexception, která je důsledkem toho, že nebyl nalezen stub. Naší snahou tedy bude sdělit klientovi, odkud si má stáhnout class soubor stubu. K tomu se používá systémová proměnná java.rmi.server.codebase. Spustíme tedy znovu server, tentokrát však s nastavením parametru java.rmi.server.codebase java Djava.rmi.server.codebase= file:/h:\zdroj/ Server Ani to však nevede ke správnému fungování aplikace, chybové hlášení je velmi podobné. Při podrobnějším prostudování výpisu chyby zjistíte, že na straně klienta chybí spuštění security managera. Do kódu klienta tedy doplníme jeho spuštění, použijeme instanci třídy RMISecurityManager. if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); Když třídu klienta upravíme, přeložíme a spustíme, dojde znovu k chybě, tentokrát je to java.security.accesscontrolexception. Je třeba povolit čtení z adresáře, kde je uložen stub a socketovou komunikaci se serverem (bližší podrobnosti viz kapitola o bezpečnosti v Javě). Soubor s bezpečnostní politikou javarmi.policy by mohl vypadat následovně: grant { permission java.net.socketpermission "*:1025-65535", "connect,accept"; permission java.io.filepermission "H:\\zdroj\\-","read"; ; 81

Klienta pak spustíme takto: java Djava.security.policy=javaRMI.policy Klient Pak již celá aplikace funguje bez problémů. Při umístění souborů pro dynamické stahování kódu na webserveru je třeba v bezpečnostní politice povolit přístup přes port 80. 82