Lekce 3 - Java RMI - Volání ze serveru na portu 2020
V minulé lekci kurzu o RMI v Javě,Java RMI - Volání void metod, jsme si zavolali vzdálenou metodu na serveru.
V dnešní lekci si vyzkoušíme použití nedefaultního portu. V našem případě použijeme RMI registry na portu 2020. Prakticky se jedná o RMI registry, které jsme si vytvořili v dílu nastavení prostředí. Ovšem pozor, je nutné nezapomenout, že RMI registry nastavujeme pro daný projekt (Working directory). Jinak bude aplikace padat. V tomto příkladu bude mít každá třída (server i klient) používající RMI jak svůj skeleton tak i stub toho druhého.

Tento příklad tedy bude podobný příkladu v předchozí lekci, ovšem využijeme jiný port pro RMI registry, vyzkoušíme si volání ze serveru na klienta/z klienta na server. Opět se bude jednat o konzolovou aplikaci konceptu server/klient. Vše budeme simulovat na localhostu. Celý postup se prakticky opět skládá ze 4 kroků, které si zde uvedeme.
- Naprogramujeme si rozhraní pro sdílené objekty, které budeme přes RMI sdílet
- Naprogramujeme serverovou část, spustíme RMI registry a spustíme server
- Naprogramujeme klientskou část a spustíme klienta
- Sledujeme a kontrolujeme výstupy
Programování rozhraní pro sdílené objekty
Opětovně naprogramujeme rozhraní, které bude zděděno od rozhraní java.rmi.Remote. Klient musí implementovat dvě metody a server pouze jednu.

Rozhraní pro klienta:
package rozhrani.klient; public interface RozhraniKlient extends java.rmi.Remote { void volameKlientMetoda1() throws java.rmi.RemoteException; void volameKlientMetoda2() throws java.rmi.RemoteException; }
Rozhraní pro server:
package rozhrani.server; public interface RozhraniServer extends java.rmi.Remote { void volameServerMetoda1() throws java.rmi.RemoteException; }
Programování serverové části, spuštění RMI registry a spuštění serveru
Opětovně si naprogramujeme server (např. class ServerRMI), který bude implementovat sdílené rozhraní, které jsme napsali výše. Z implementovaného rozhraní nám vznikla povinnost doprogramovat vnitřek jediné metody "volameServerMetoda1()". Zdrojový kód serveru jsem napsal bez importů, můžete si je dodat a kód tak zjednodušit.
/* Program potřebuje registry na defaultním portu 2020 */ package server; public class ServerRMI implements rozhrani.server.RozhraniServer { private static rozhrani.server.RozhraniServer rozhrServer = null; private static java.rmi.registry.Registry registry = null; public void volameServerMetoda1() throws java.rmi.RemoteException { System.out.println("Metoda zavolana na serveru"); try { Thread.sleep(5000); // uspíme vlákno na 5 sekund } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Zavolame metody na klientovi"); rozhrani.klient.RozhraniKlient rozhObjektKlient= null; try { rozhObjektKlient = (rozhrani.klient.RozhraniKlient) registry.lookup("KlientRMI"); } catch (java.rmi.RemoteException | java.rmi.NotBoundException e) { System.out.println("Nepodarilo se ziskat objekt stubu (rozhrani)"); e.printStackTrace(); } System.out.println("Zavolame si metody na klientech"); try { rozhObjektKlient.volameKlientMetoda1(); rozhObjektKlient.volameKlientMetoda2(); } catch (java.rmi.RemoteException e) { System.out.println("Nepodarilo se zavolat metodu na klientovi"); e.printStackTrace(); } } public static void main(String[] args) { System.out.println("Spustime RMI server"); // vytvoříme objekt rozhraní (tzv. skeleton) try { rozhrServer = (rozhrani.server.RozhraniServer) java.rmi.server.UnicastRemoteObject.exportObject(new ServerRMI(),0); } catch (java.rmi.RemoteException e) { System.out.println("Nepodarilo se nam vytvorit skeleton"); e.printStackTrace(); } // získáme objekt RMI registru pro daný port try { registry = java.rmi.registry.LocateRegistry.getRegistry(2020); } catch (java.rmi.RemoteException e) { System.out.println("Nepodarilo se ziskat objekt RMI registru"); e.printStackTrace(); } // nahrajeme skeleton do registru try { registry.bind("ServerRMI",rozhrServer); } catch (java.rmi.RemoteException | java.rmi.AlreadyBoundException e) { System.out.println("Nepodarilo se nahrat skeleton do registru"); e.printStackTrace(); } System.out.println("Cekame na volani Klienta"); } }
Zde si rozebereme co vlastně zdrojový kód dělá. V první části (public static void main() metodě) si vytvoříme objekt (instanci) třídy implementující sdílené rozhraní. Poté získáme objekt RMI registru a již jen nahrajeme sdílený objekt do RMI registrů. Tímto způsobíme vznik tzv. skeleton. Skeleton pojmenujeme, přesněji mu přiřadíme identifikátor, "ServerRMI". Samozřejmě jméno identifikátoru je volitelné a takových skeletonů můžeme nahrát do RMI registrů kolik chceme. Nejsem si vědom omezení co se týká počtu nahraných skeletonů. Zajisté jste si všimli, že metoda získání RMI registrů má tentokrát parametr 2020. Ano, tento parametr specifikuje port (více viz Java dokumentace). Teoreticky bychom mohli i vyzkoušet spustit více RMI registrů na různých portech a do nich nahrát jiné skeletony, ale co z toho? Fungovat by to nejspíše mělo. Metoda volameServerMetoda1() je metodou, která je k dispozici stubu na klientovi. Pokud ji zavolá, pak v metodě vytvoříme stub klienta a tím získáme metody, které má sdílený objekt na klientovi k dispozici.

Zde si celý kód ukážeme v IDE. V druhé části (volameServerMetoda1() metodě) se provede nejdříve uspání na 5 sekund, poté si vytvoříme zrcadlový objekt stubu klienta a zavoláme obě jeho metody, které jsou (budou) na klientovi k dispozici. Zpoždění 5 sekund je tam jen abyste přepnuli výpis konzole v IDE a mohli se přesvědčit o výpisu na klientovi na vlastní oči.
Zajisté jste si všimli, že kód je navržen tak, že se spustí server, poté klient, klient zavolá metodu na serveru a v ní se zas zavolají metody na klientovi. Samozřejmě, mohl jsem to udělat na přímo bez té sdílené metody na serveru, kdy server přímo vytvoří stub klienta, ale chtěl jsem, abychom si to vyzkoušeli.

Programování klientské části a uspání klienta
Jako další vytvoříme třídu klienta (např. class KlientRMI). Jak jste si všimli, jedná se o standardní public static void main() metodu a je třeba ji tak i spustit.

Kód je skutečně velmi jednoduchý a je velmi analogický s tím ze serveru. Nejdříve vytvoříme skeleton klienta, získáme RMI registry z portu 2020 a pak skeleton nahrajeme s identifikátorem "KlientRMI" do RMI registru. Po tomto je skeleton klienta k dispozici všem, kdo disponují rozhraním.
Dále vytvoříme sdílený objekt rozhraní serveru (tzv. stub) přes RMI registry. K tomu potřebujeme identifikátor, který jsme použili na serveru v RMI registrech. No a poté konečně máme objekt, který disponuje naší metodou, kterou jsme si definovali v rozhraní.
package klient; import java.rmi.*; import java.rmi.registry.*; import rozhrani.klient.*; import rozhrani.server.*; public class KlientRMI implements RozhraniKlient { private static RozhraniKlient rozhrKlient; public void volameKlientMetoda1() throws RemoteException { System.out.println("Klient RMI metoda1"); } public void volameKlientMetoda2() throws RemoteException { System.out.println("Klient RMI metoda2"); } public static void main(String[] args) { System.out.println("Spustime RMI klienta"); // vytvoříme objekt implementující rozhraní (tzv. skeleton) try { rozhrKlient = (RozhraniKlient) java.rmi.server.UnicastRemoteObject.exportObject(new KlientRMI(),0); } catch (java.rmi.RemoteException e) { System.out.println("Nepodarilo se nam vytvorit skeleton"); e.printStackTrace(); } // získáme objekt RMI registru pro daný port Registry registry = null; try { registry = LocateRegistry.getRegistry(2020); } catch (RemoteException e) { System.out.println("Nepodarilo se ziskat objekt registru"); e.printStackTrace(); } // nahrajeme stub do registru try { registry.bind("KlientRMI",rozhrKlient); } catch (java.rmi.RemoteException | java.rmi.AlreadyBoundException e) { System.out.println("Nepodarilo se nahrat stub do registru"); e.printStackTrace(); } // zavoláme metodu na serveru RozhraniServer rozhObjektServer = null; try { rozhObjektServer = (RozhraniServer) registry.lookup("ServerRMI"); } catch (RemoteException | NotBoundException e) { System.out.println("Nepodarilo se ziskat objekt stubu (rozhrani)"); e.printStackTrace(); } System.out.println("Zavolame si metodu na serveru"); try { rozhObjektServer.volameServerMetoda1(); } catch (RemoteException e) { System.out.println("Nepodarilo se zavolat metodu na serveru"); e.printStackTrace(); } System.out.println("Cekame na volani Serveru"); } }
Nyní si spustíme opětovně RMI registry na portu 2020.

Spustíme i server a prohlédneme si výpis na konzole.

Nyní si spustíme klienta a prohlédneme si výpis na konzole. Přepneme na server a podíváme se na výpis konzole.

No a nyní se zas přepneme se na výpis konzole klienta. Krásně jde vidět zavolané metody na klientovi.

V příštím díle, Java RMI - Vysvětlení pojmů a popis tříd, si probereme trochu další teorie ohledně RMI.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 9x (6.65 kB)
Aplikace je včetně zdrojových kódů v jazyce Java