30

嗨,我有 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;
}

它一直在说有关禁用类加载的内容,所以我想问题出在某个地方……谢谢!

4

6 回答 6

29

远程类加载可能很棘手。

原始帖子不包含有关代码库的任何信息。可能是客户端的安全配置是正确的,但是它无法访问远程代码。这些类由客户端直接从“代码库”加载。服务不会通过 RMI 连接将它们呈现给客户端。该服务仅引用类的外部源。

服务器应指定系统属性java.rmi.server.codebase。该值必须是客户端可访问的 URL,可以从中加载必要的类。如果这是一个file:URL,则客户端必须可以访问文件系统。

反之亦然:如果服务器应该能够从客户端加载类(如这里),则客户端必须将代码库属性设置为服务器可访问的 URL。

于 2011-06-14T18:27:12.067 回答
7

每次调用 RMI 动态代理上的方法时,MarshalInputStream(扩展ObjectInputStream为覆盖resolveClassresolveProxyClass)委托以LoaderHandler查看 3 个位置以供ClassLoader使用:

  1. 正在调用的代理的 ClassLoader(从技术上讲,它使用了一个名为 的 hack latestUserDefinedLoader():它沿着堆栈向上走,在堆栈上寻找不属于 JRE 的第一个方法)。
  2. contextClassLoader调用者的线程本地
  3. 代码库类加载器(如果启用了 SecurityManager)
    1. 如果 System 属性java.rmi.server.useCodebaseOnly=false,则代码库 ClassLoader 使用远程 java.rmi.server.codebaseURL 。请注意,JDK 7u21中 useCodebaseOnly 的默认值已更改,因此除非您更改它,否则不再使用远程代码库
    2. 否则,代码库 ClassLoader 使用本地 java.rmi.server.codebase.

因此,ClassNotFoundException在调用 Remote 方法时,您可能会遇到以下几个可能的原因:

  • 如果堆栈包含“无安全管理器:RMI 类加载器已禁用”,那么如果您需要为双方进行远程类加载以获取所有远程接口和可序列化类,请确保按照其他人的描述设置一个 SecurityManager。
  • 如果您正在使用远程类加载,并且在升级到 JRE 7u21 时它停止工作,那么要么设置-Djava.rmi.server.useCodebaseOnly=true为匹配以前的行为,要么设置-Djava.rmi.server.codebase为本地和远程端以空格分隔的 URL 列表。并确保计算机可以访问这些 URL。
  • 如果您在本地使用自定义 ClassLoader,其父类加载器定义了一些Remote 接口,请确保调用Thread.setContextClassLoader(ClassLoader)以便 RMI 将使用该 ClassLoader。(这是我的问题:我有一个SwingWorker恰好被安排到一个工作线程上,该线程是在 EventDispatchThread 上设置 contextClassLoader 之前创建的)。比如A和C属于你自定义的ClassLoader,而B属于父ClassLoader,那么当你调用a.getB().getC()时,getB()调用会使用自定义的classloader,但是getC()调用将无法在 latestUserDefinedClassLoader 中找到 C,将不得不退回到 contextClassLoader。

所有这些都是对 ObjectInputStream 糟糕的 API 设计的警示。ObjectInputStream 应该要求您传递一个 ClassLoader 参数,而不是尝试使用 latestUserDefinedLoader、contextClassLoader 和代码库随意查找。

于 2013-07-29T18:29:12.350 回答
7

我想添加一些可能对某些人有帮助的东西,尤其是初学者。我来到这里,寻找解决上述错误的方法,但是作为初学者,我不知道如何使用安全策略并指定“java.rmi.server.codebase”属性。修复该错误的最简单方法是确保要通过 RMI 发送的对象的类位于包的相同路径中。这样,两个应用程序的类相对于它们的主文件夹位于相同的位置,并且错误将解决。

示例:如果MedicationDTO要从服务器向客户端发送类型(可序列化)的对象,请确保它位于相同的包路径中。在我的情况下,在服务器应用程序中,对象在com.example.springdemo.dto客户端应用程序中,它在com.example.springdemo.service.dto..问题是,使用 IntelliJ,因为service包中没有任何内容,而是另一个包,它们的名称被连接(service.dto) 我看不出路径不一样。因此,请确保您的类具有相同的包路径。(我的情况的解决方案:MedicationDTO类必须在包中的两个应用程序中:com.example.springdemo.dto.

我知道这不是最好的解决方案,它只是一个“小技巧”,但我会非常高兴找到这个解决方案,因为它让我免于浪费大量时间来解决问题。

我希望这对那些想要快速修复该错误的人有所帮助,因为我认为学习使用安全管理器和包含代码库可能有点棘手并且需要时间。

于 2020-01-17T10:32:16.300 回答
4

您需要服务器端的安全管理器,而不仅仅是客户端。

没有这个,服务器的 RMI 引擎拒绝从客户端加载类,因为它不能保证这些不会在服务器上做坏事。

您是否需要加载 RMI 类?服务器不能已经拥有客户端尝试发送的类吗?

于 2011-06-14T18:18:53.080 回答
0

我知道我的情况非常特别,但也许它可以帮助其他人。我的情况是,一台服务器上的多个应用程序当然以不同的路径共享同一个注册表。无论在何处创建此注册表(通常从第一个应用程序开始),所有后续应用程序的完整类路径都必须在此第一个应用程序中指定。把它们加起来:

  1. 确保提供政策
  2. 确保为所有应用程序、客户端和服务器指定完整的类路径
于 2021-02-10T09:37:18.260 回答
-4

我知道为什么会这样。例如您在项目A中启动服务器,但您使用项目B中的客户端请求此服务器,这是错误的。所以你应该把服务器和客户端放在同一个项目中。

于 2014-04-21T10:01:00.690 回答