Krótki spis częstych wyjątków i ich typowych przyczyn

java.security.AccessControlException: access denied (java.net.SocketPermission komp0404.dydaktyka.wszib.edu.pl resolve)
  at java.security.AccessControlContext.checkPermission(AccessControlContext.java:269)
...

JVM nie jest w stanie uzgodnić odpowiednich praw aby otworzyć zdalne połączenie. Możliwe przyczyny to brak ustawienia pliku z prawami (java.policy) w czasie uruchomienia lub w ogóle brak tego pliku w katalogu bieżącym gdzie jest program uruchamiany.


Echo exception: Connection refused to host: 145.100.42.21; nested exception is:
  java.net.ConnectException: Connection refused
java.rmi.ConnectException: Connection refused to host: 145.100.42.21; nested exception is:
  java.net.ConnectException: Connection refused
java.net.ConnectException: Connection refused
...

Przyczyna może być dwojaka. Pierwsza możliwość: na wskazanym komputerze (tu: 145.100.42.21) nie jest uruchomiony rejestr rmiregistry. Inny możliwy powód: obiekt, którego poszukujemy w rejestrze, jest w nim zarejestrowany pod podaną nazwą ale fizycznie już nie istnieje. Może się tak stać jeśli zarejestrujemy serwer (wywołaniem Naming.(re)bind) w rejestrze a następnie ów serwer wyłączymy.


java.rmi.NotBoundException: aplikacja
  at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:245)
  at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:220)
  at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:354)
...

Ten wyjątek oznacza, że rejestr z którym chcieliśmy się połączyć działa pod podanym adresem, jednak nie ma w nim zarejestrowanego obiektu o podanej nazwie (tu: aplikacja).


Echo exception: error unmarshalling return; nested exception is:
  java.lang.ClassNotFoundException: EchoServer_Stub
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
  java.lang.ClassNotFoundException: EchoServer_Stub
  at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
...

Rejestr zgłasza problem z dostępem do klasy EchoServer_Stub. Proste (niedobre) rozwiązanie: uruchomić rejestr w ten sposób aby posiadał plik EchoServer_Stub.class w ścieżce CLASSPATH (automatycznie tak się dzieje jeżeli uruchomimy rejestr w tym samym katalogu). Rozwiązaniem lepszym jest przekazanie lokalizacji tego pliku w chwili uruchamiania serwera opcją -Djava.rmi.server.codebase=URL.


Echo exception: error unmarshalling return; nested exception is:
  java.lang.ClassNotFoundException: access to class loader denied
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
  java.lang.ClassNotFoundException: access to class loader denied
  at sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
...

Wyjątek ten najczęściej pojawia się kiedy klient próbuje wyszukać serwer w rejestrze i zdobyć jego stub w celu wywołania metody. Można temu zaradzić ustawiając parametr -cp wywołania klienta (ścieżka classpath) tak aby zawierał m.in. ścieżkę dostępu do klasy stub serwera (np. EchoServer_Stub.class). W sytuacji ogólnej, kiedy klient nie ma bezpośredniego dostępu do tej klasy należy ją przekazać dynamicznie (poprzez protokół HTTP) przy pomocy ustawienia parametru -Djava.rmi.server.codebase=URL przy uruchamianiu klienta - wymaga to jednak udostępnienia pliku EchoServer_Stub.class poprzez serwer Web.


java.rmi.server.ExportException: Port already in use: 1099; nested exception is:
  java.net.BindException: Address already in use
java.net.BindException: Address already in use
...

Wyjątek ten może się pojawić podczas uruchamiania rejestru rmiregistry. Oznacza, że port 1099 jest już używany przez inny program na tej maszynie. Zazwyczaj chodzi tu o inny, wcześniej uruchomiony i nie zakończony rejestr rmiregistry.


java.rmi.AccessException: Registry.Registry.rebind disallowed; origin e21.blue.sara.nl/145.100.42.21 is non-local host
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
  java.rmi.AccessException: Registry.Registry.rebind disallowed; origin e21.blue.sara.nl/145.100.42.21 is non-local host
...

Nie można zarejestrować w rmiregistry serwera znajdującego się na innej maszynie niż lokalna. Jest to umyślne ograniczenie narzucone przez twórców RMI.


java.rmi.AlreadyBoundException: echo123
  at sun.rmi.registry.RegistryImpl.bind(RegistryImpl.java:123)
  at sun.rmi.registry.RegistryImpl_Skel.dispatch(Unknown Source)
...

Obiekt próbuje zarejestrować się metodą bind() pod nazwą używaną już obecnie przez inny obiekt (serwer) - tu: echo123. Należy albo zmienić proponowaną nazwę albo użyć metody rebind() - ta druga opcja spowoduje jednoczesne wyrejestrowanie poprzedniego serwera z rejestru. Uwaga! Samo zakończenie działania przez poprzedni serwer nie zwalnia użytej przezeń nazwy w rejestrze. Dopiero bezpośrednie wywołanie metody unbind() rejestru zwolni używaną nazwę.


Tomasz Gubała