我有一个应用FooApplication
(foo.jar
中的代码),插件BarPlugin
(foo-bar-plugin.jar
中的代码)。应用程序动态实例化插件的实例。它很棒。
FooApplication具有一些可通过RMI接口FooRemote
访问的功能。除了一件事之外,这也很有效。 FooRemote
有一个访问插件导出的Remote
对象的方法,当我尝试将其中一个插件分发给RMI客户端时,我得到一个java.rmi.UnmarshalException。
public interface FooRemote extends Remote
{
/* other methods */
public RemoteControl getPluginRemoteControl(int i) throws RemoteException;
}
/** just a named Remote object for debugging purposes */
public interface RemoteControl extends Remote
{
public String getName() throws RemoteException;
}
我在FooRemoteImpl中所做的工作如下:
/* just a test object */
private static class RC0 extends UnicastRemoteObject implements RemoteControl
{
public RC0() throws RemoteException { super(); }
@Override public String getName() throws RemoteException { return "RC0"; }
}
@Override public RemoteControl getPluginRemoteControl(int i)
throws RemoteException
{
int j = i;
if (j <= 0)
return new RC0();
Collection<RemoteControl> rclist = this.model.getApplicationPluginRemotes();
for (RemoteControl rc : rclist)
{
if (--j == 0)
return rc;
}
return null;
}
当我调用FooRemote.getPluginRemoteControl(0)
时,它会发出我的虚拟类RC0
的实例,并且可以从客户端正常工作。当我调用FooRemote.getPluginRemoteControl(1)
时,它试图分发一个真正的插件遥控器,但它失败了:
??? Java exception occurred:
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: com.example.plugin.BarPluginRemoteControl (no security manager: RMI class loader disabled)
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at $Proxy5.getPluginRemoteControl(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.example.plugin.BarPluginRemoteControl (no security manager: RMI class loader disabled)
[ more stuff deleted ]
是什么给出了?
答案 0 :(得分:2)
您可能需要设置SecurityManager
。您的客户端无法找到类com.example.plugin.BarPluginRemoteControl
,因为没有安全管理器可以访问它。
尝试以下方法:
向客户端代码添加:
System.setSecurityManager(new java.rmi.RMISecurityManager());
创建一个名为client.policy
的策略文件,其中包含:
grant{
permission java.security.AllPermission;
};
将策略文件传递到客户端的启动命令:
java -Djava.security.policy=client.policy ... <main-class>...
答案 1 :(得分:2)
请不要将这些类放在RMI Registry类路径中。这解决了问题,但它不是正确的解决方案。这样做意味着每次更新服务器代码时,都需要将类与RMI Registry 和所有客户端同步。正确的解决方案是使用http或ftp URI在服务器的代码库上提供所需的类(请不要使用文件URI)。然后,RMI Registry和您的客户端将能够通过http或ftp动态访问所需的类。
您应该在代码库中包含的唯一类是服务器的远程接口,以及作为参数出现或从这些接口中的方法返回值的任何类,以及这些接口抛出的任何异常类。我相信(但我不确定)最好的做法是创建一个带有'-dl'后缀的单独jar,其中只包含这些类文件。
答案 2 :(得分:1)
如果您在单独的VM中运行它,RMI注册表还需要它自己的类路径上的类。我记得浪费太多时间学习这一点。
很高兴我能帮忙!
编辑:显然这不是正确的方法,请参阅评论者的回答。