Java: нет менеджера безопасности: загрузчик класса RMI отключен
Привет у меня есть приложение RMI, и теперь я пытаюсь вызвать некоторые методы на сервере из моего клиента. У меня есть следующий код:
public static void main(final String[] args) {
try {
//Setting the security manager
System.setSecurityManager(new RMISecurityManager());
IndicatorsService server = (IndicatorsService) Naming
.lookup("rmi://localhost/" + IndicatorsService.SERVICE_NAME);
DataProvider provider = new OHLCProvider(server);
server.registerOHLCProvider(provider);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
}
}
Сервер правильно загружен, но когда я пытаюсь позвонить server.registerOHLCProvider(provider);
Я получаю эти ошибки:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:336)
at sun.rmi.transport.Transport$1.run(Transport.java:159)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
at sk.fri.statistics.service.impl.IndicatorsServiceImpl_Stub.registerOHLCProvider(Unknown Source)
at sk.fri.statistics.service.Client.main(Client.java:61)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:296)
at sun.rmi.transport.Transport$1.run(Transport.java:159)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:375)
at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:165)
at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620)
at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247)
at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:197)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1574)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:290)
... 9 more
Я добавил свой файл политики в качестве аргумента VM, вот как это выглядит:
grant {
permission java.security.AllPermission;
}
Он продолжает что-то говорить об отключенной загрузке классов, так что я думаю, что проблема где-то там... Спасибо!
6 ответов
Удаленная загрузка классов может быть сложной.
Оригинальный пост не содержит никакой информации о базе кода. Может случиться так, что конфигурация безопасности клиента верна, но он не имеет доступа к удаленному коду. Классы загружаются непосредственно из "базы кода" клиентом. Они не предоставляются клиенту службой через соединение RMI. Служба просто ссылается на внешний источник для классов.
Сервер должен указать системное свойство java.rmi.server.codebase
, Значение должно быть URL-адресом , доступным для клиента, с которого могут быть загружены необходимые классы. Если это file:
URL, файловая система должна быть доступна клиенту.
И наоборот: если сервер должен загружать классы с клиента (как здесь), клиент должен установить для свойства базы кода URL-адрес, который доступен для сервера.
Каждый раз, когда вы вызываете метод на динамическом прокси RMI, MarshalInputStream
(который расширяет ObjectInputStream
переопределить resolveClass
а также resolveProxyClass
) делегаты LoaderHandler
искать в 3 местах для ClassLoader
использовать:
- ClassLoader прокси, который вызывается (технически он использует хак, называемый
latestUserDefinedLoader()
: он идет вверх по стеку, ища первый метод в стеке, который не является частью JRE). - Thread-местный
contextClassLoader
звонящего - Codebase ClassLoader, если включен SecurityManager
- Если системное свойство
java.rmi.server.useCodebaseOnly=false
то кодовая база ClassLoader использует URL в удаленномjava.rmi.server.codebase
, Обратите внимание, что значение по умолчанию для useCodebaseOnly изменилось в JDK 7u21, поэтому удаленная кодовая база больше не используется, пока вы ее не измените! - В противном случае база кода ClassLoader использует URL-адреса в локальном
java.rmi.server.codebase
,
- Если системное свойство
Так что есть несколько возможных причин, по которым вы бы получили ClassNotFoundException
при вызове метода Remote:
- Если в стеке "нет диспетчера безопасности: загрузчик классов RMI отключен", убедитесь, что установили SecurityManager, как описано другими, если вам нужна удаленная загрузка классов для обеих сторон, чтобы получить все удаленные интерфейсы и сериализуемые классы.
- Если вы используете удаленную загрузку классов, и она перестала работать при обновлении до JRE 7u21, то либо установите
-Djava.rmi.server.useCodebaseOnly=true
соответствовать предыдущему поведению или установить-Djava.rmi.server.codebase
в разделенный пробелами список URL-адресов как на локальной, так и на удаленной сторонах. И убедитесь, что компьютер может получить доступ к этим URL. - Если вы используете пользовательский ClassLoader локально, родительский загрузчик которого определяет некоторые удаленные интерфейсы, то обязательно вызовите
Thread.setContextClassLoader(ClassLoader)
так что RMI будет использовать этот ClassLoader. (Это была моя проблема: у меня былSwingWorker
это было запланировано на рабочий поток, который был создан до того, как contextClassLoader был установлен на EventDispatchThread). Например, A и C принадлежат вашему пользовательскому ClassLoader, но B принадлежит родительскому ClassLoader, тогда когда вы вызываете a.getB(). GetC(), вызов getB () будет использовать пользовательский загрузчик классов, но вызов getC() не сможет найти C в latestUserDefinedClassLoader и должен будет вернуться к contextClassLoader.
Все это предостерегающая история о плохом дизайне API ObjectInputStream. ObjectInputStream должен был требовать, чтобы вы передавали параметр ClassLoader, а не пытались найти его случайно, используя latestUserDefinedLoader, contextClassLoader и codebase.
Я хочу добавить кое-что, что может быть полезно для некоторых людей, особенно для начинающих. Я пришел сюда и искал решение указанной выше ошибки, но, будучи новичком, я не знал, как использовать политики безопасности и указывать атрибут "java.rmi.server.codebase". Самый простой способ исправить эту ошибку - убедиться, что классы объектов, которые должны быть отправлены через RMI, находятся в одном и том же пути к пакетам. Таким образом, оба приложения имеют класс в одном месте относительно их основной папки и ошибка решит.
Пример: если вы хотите отправить объект типа MedicationDTO
(который является сериализуемым) от сервера к клиенту, убедитесь, что он находится в том же пути к пакету. В моем случае в серверном приложении объект находился вcom.example.springdemo.dto
а в клиентском приложении он был в com.example.springdemo.service.dto
.. Проблема заключалась в том, что при использовании IntelliJ, поскольку service
в пакете ничего не было, кроме другого пакета, их имя было объединено (service.dto
) и я не мог видеть, что путь был не тот. Итак, убедитесь, что ваши классы имеют одинаковый путь к пакету. (Решение для моего случая:MedicationDTO
класс должен быть в обоих приложениях в пакете: com.example.springdemo.dto
.
Я знаю, что это не лучшее решение, это просто "небольшая уловка", но я был бы очень счастлив найти это решение тогда, потому что оно избавило меня от кучи потраченного впустую времени на решение проблемы.
Я надеюсь, что это будет полезно для тех, кто хочет быстро исправить эту ошибку, потому что я думаю, что обучение использованию менеджеров безопасности и включение кодовой базы может быть немного сложным и потребует времени.
Вам нужен менеджер безопасности на стороне сервера, а не только на стороне клиента.
Без этого механизм RMI сервера отказывается загружать классы с клиента, поскольку он не может гарантировать, что они не будут делать зла на сервере.
Вам нужна загрузка класса RMI вообще? Может ли сервер уже иметь классы, которые клиент пытается отправить?
Я знаю, что мой случай очень особенный, но, возможно, он помогает другим. У меня был случай, когда на одном сервере было несколько приложений, использующих один и тот же реестр с разными путями, конечно. Где бы этот реестр ни создавался (обычно из первого приложения), в этом первом приложении должен быть указан полный путь к классам всех последующих приложений. Подвести итог:
- Убедитесь, что политика предоставлена
- Убедитесь, что путь к классам полностью указан для всех приложений, клиентов и сервера.
Я знаю, почему это происходит. например, вы запускаете сервер в проекте A, но вы используете клиент в проекте B для запроса этого сервера, это неправильно. Таким образом, вы должны поместить сервер и клиент в одном проекте.