ОС: Windows 7
JDK: 1.8.0_05
Я работаю над некоторыми простыми учебниками RMI, включая образец "Compute" Oracle (вычислить). Запуск моего сервера не требует кодовой базы, и ответы на вопросы, подобные этому, говорят, что "кодовая база не является обязательной". Тем не менее мой сервер не может зарегистрировать удаленный объект, если его интерфейс не находится в некоторой базе кода.
Я уверен, что мой интерфейс Compute доступен веб-серверу, запущенному на localhost, запустите сервер реестра следующим образом:
set CLASSPATH=
rmiregistry -J-Djava.rmi.server.codebase="http://localhost:80/"
И все работает нормально:
Exporting stub
Locating registry
Binding stub
ComputeEngine bound
Но если я удалю Compute.class с пути веб-сервера, я получаю ClassNotFoundException:
Exporting stub
Locating registry
Binding stub
java.rmi.ServerException: RemoteException occurred ...:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: edu.uweo.java2.rmi.compute.server.Compute
Из журнала веб-сервера я вижу, что была сделана попытка загрузить Compute.class:
GET '/edu/uweo/java2/rmi/compute/server/Compute.class'
Я также попытался запустить сервер реестра без указания кодовой базы:
set CLASSPATH=
rmiregistry
Когда я делаю это так, никто не пытается связаться с моим веб-сервером (что меня не удивляет), но я все еще получаю ClassNotFoundException.
Мой код выходит из учебника Oracle с помощью нескольких дополнительных печатных диагностических программ:
try
{
String name = "Compute";
ComputeEngine engine = new ComputeEngine();
System.out.println( "Exporting stub" );
Compute stub =
(Compute)UnicastRemoteObject.exportObject( engine, 0 );
System.out.println( "Locating registry " );
Registry registry = LocateRegistry.getRegistry();
System.out.println( "Binding stub" );
registry.rebind( name, stub );
System.out.println( "ComputeEngine bound" );
}
Может ли кто-нибудь сказать мне, что мне не хватает?
Благодарю.
Вы правы, вам не нужно использовать функцию codebase. Однако это означает, что ваш удаленный интерфейс, классы, от которого он зависит, и заглушка, если вы используете его, должны быть доступны как клиенту, так и реестру в их классах. Исключение, которое вы получаете, указывает на то, что в реестре эти классы отсутствуют на CLASSPATH.
Причина, по которой он работает, заключается в том, что Registry
RMI реализован сам с помощью RMI. Поэтому, когда вы вызываете bind
в одном приложении, фактическая реализация Registry
в процессе rmiregistry
получит объект-заглушку, реализующий все ваши Remote
интерфейсы, так же как и все вызовы RMI, передающие ссылки на удаленные объекты.
Поэтому для процесса rmiregistry
необходим доступ ко всем удаленным интерфейсам, реализованным объектом, который вы bind
но, как вы уже обнаружили, его фактическое содержимое не имеет значения, поскольку реестр никогда не будет ссылаться на какие-либо методы на нем. Все, что он делает, - это передача удаленной заглушки вызывающим абонентам lookup
и в этом случае заглушка сериализуется и передается в JVM удаленных абонентов, где создается эквивалентный заглушка, снова реализуя все удаленные интерфейсы, заглушка (и, следовательно, исходный объект) имеет реализованы.
Таким образом, все, что происходит с интерфейсами в процессе rmiregistry
(если вы запустили его как отдельный процесс), заключается в том, что информация о удаленных интерфейсах, реализованных удаленным объектом, записывается, позволяя заглушке реализовать их соответствующим образом и передаваться всем другие удаленные приложения, выполняющие поиск.
В принципе, можно было бы реализовать весь реестр по-другому, не используя RMI и записывая реализованный интерфейс без необходимости в их файлах классов, однако это было бы огромным усилием, так как вам пришлось бы реализовать все, что вы получаете бесплатно когда просто используется RMI.
Но учтите, что если у вас есть только один сервер, вы можете упростить все, разрешив самому серверу создать реестр в своей собственной JVM. Тогда вам не нужно начинать другой процесс и не думать о его classpath/codebase.