wtorek, 5 października 2010

Mechanizm RMI (Remote Method Invocation) w praktyce cz. 3

Dotychczas w poprzednich artykułach (część 1 i część 2) przedstawiłem jak zbudować prostą aplikację przy użyciu RMI, gdzie klient wywołuje metody serwera i serwer wywoluje metody klienta. Dotychczas wszystko uruchamialiśmy na jednej maszynie (localhost). Spróbujmy czy zadziała wcześniej napisana przez nas aplikacja przy próbie uruchomienia na dwóch komputerach.

Moje komputery to laptop o adresie 192.168.0.2 i PC 192.168.0.1. Na PCie odpalę serwer, a na laptopie klienta. Kopiujemy projekty na odpowiednie maszyny i pierwsze co to należy zmienić ścieżki dostępowe w skryptach uruchomieniowych. Bez tego otrzymamy zapewne jeden z błędów opisywanych w poprzednich artykułach.

No dobra ruszamy. Odpalamy serwer na PCie - wszystko działa (bo na nim przygotowywalem poprzednie artykuły). Odpalamy klienta na laptopie i:

javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: 192.168.0.2; nested exception is:
        java.net.ConnectException: Connection refused: connect]


Czyli aplikacja kliencka próbuje się połączyć z serwerem działającym na laptopie. Trzeba więc zmodyfikować adres pod który aplikacja próbuje się łączyć. Otwieramy więc MyClientMain.java w Eclipse i modyfikujemy linię definiującą adres URL:

String url = "rmi://192.168.0.1";

Uruchamiamy ponownie klienta i znów ten sam błąd. Jednak to nie tu szukaliśmy. Przyglądajac się bardziej stosowi wywołań, można zauważyć że wyjątek został rzucony przez MyClientMain.java:19. Zobaczmy co się tam dzieje:

context.bind("rmi:MyClientObject", myClientImpl);

Aaaa, czyli aplikacja kliencka nie może zarejestrować obiektu, gdyż rmiregistry zostało uruchomione na innej maszynie. Poświęciłem sporo czasu aby rozwikłać ten problem. Z tego co wyczytałem nie można zarejestrować obiektu, który ma być zdalny na maszynie innej niż localhost. (Moge się mylić w tym momencie, wiec jak ktoś wie lepiej niech pisze!) Po co więc ten argument w metodzie bind() do podania adresu? A no po to, że na jednej maszynie możemy mieć uruchomionych kilka rmiregistry działających na innych portach.

Wracając do aplikacji to jeśli chcemy klientem wywoływać metody serwera i serwer chce wywoływać metody klienta i ma to działać na osobnych maszynach, to musimy troche przerobić aplikację. Przede wszystkim klient musi posiadać uruchomienione własne rmiregistry, gdzie będzie wystawiał swoje zdalne obiekty. Dodatkowo serwer musi jakoś poznać IP z jakim ma się połaczyć, więc warto go mu przekazać (np. jako wywołanie zdalnej metody).

Ok najpierw zmiany u klienta. W skrypcie uruchomieniowym programu klienta należy dopisac linie uruchamiającą rmiregistry zanim uruchomimy właściwą aplikację:

start rmiregistry

Następnie w klasie MyClientMain należy zmodyfikować wywołanie zdalnej metody:

String clientAdres = InetAddress.getLocalHost().getHostAddress();
String str = myRemoteObject.getDescription(clientAdres);

Teraz argument przekazywany do metody będziemy traktować jak adres IP klienta, aby serwer wiedział z kim się połaczyć.

Teraz czas na serwer. Zmieniamy wywołanie metody szukającej zdalny obiekt klienta na następujący kod:

MyClientInt myClientInt = (MyClientInt) 
        context2.lookup("rmi://" + text + "/MyClientObject");

gdzie text jest argumentem przekazywanym do metody.

Uruchamiamy i wszystko działa poprawnie :) Możemy cieszyc się aplikacją RMI działającą na osobnych maszynach. Tym samym kończę cykl artykułów o RMI (chyba że jeszcze coś ciekawego przyjdzie mi do głowy, lub pojawi się zapotrzebowanie na rozwiązanie jakiegoś problemu). Wszelkie uwagi mile widziane.

Więcej informacji:
Mechanizm RMI (Remote Method Invocation) w praktyce cz. 1
Mechanizm RMI (Remote Method Invocation) w praktyce cz. 2

Brak komentarzy:

Prześlij komentarz