客户端调用时java RMI服务器ClassNotFoundException
我正在尝试在本地机器上运行一个简单的RMI执行引擎,以便其他java程序可以轻松地在第二个处理器上运行代码。(在未来,这将扩展以允许更多处理器)
服务器正常启动,客户端似乎找到了注册表ok,但当它尝试调用执行引擎(并向其传递参数)时,会导致ClassNotFoundException
我认识到这与关于堆栈溢出的许多其他问题类似,但据我所知,所有其他问题都与客户端无法下载服务器的类有关,而不是与服务器无法下载客户端的类有关
这是我的代码(几乎完全是从this sample code复制的):
RMIServerEclipse项目
计算机引擎接口:
package interfaces;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface ComputeEngine_IF extends Remote {
/**
* @param task The task object specifying the computation to be performed
* @return
* @throws RemoteException
*/
<T extends Serializable> T compute(TaskWithoutInput<T> task) throws RemoteException;
}
TaskWithOutput接口:
package interfaces;
import java.io.Serializable;
public interface TaskWithoutInput<T> extends Serializable {
public T compute();
}
计算机引擎类:
package server;
import interfaces.ComputeEngine_IF;
import interfaces.TaskWithoutInput;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class ComputeEngine implements ComputeEngine_IF{
public ComputeEngine() {
super();
}
@Override
public <T extends Serializable> T compute(TaskWithoutInput<T> task) throws RemoteException {
return task.compute();
}
public static void main(String[] args) {
if(System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";
ComputeEngine_IF engine = new ComputeEngine();
ComputeEngine_IF stub = (ComputeEngine_IF) UnicastRemoteObject.exportObject(engine, 0);
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind(name, stub);
System.out.println("Registry bound!");
}
catch (Exception e) {
System.err.println("ComputeEngine exception:");
e.printStackTrace();
}
}
}
RMIClientEclipse项目
GetAnswerTask类
package client;
import interfaces.TaskWithoutInput;
public class GetAnswerTask implements TaskWithoutInput<Integer> {
private static final long serialVersionUID = 1L;
@Override
public Integer compute() {
return Integer.valueOf(42);
}
}
客户端类
package client;
import interfaces.ComputeEngine_IF;
import interfaces.TaskWithoutInput;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RmiClientExample {
public static void main(String args[]) {
if(System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
String name = "Compute";
Registry registry = LocateRegistry.getRegistry("localhost");
ComputeEngine_IF comp = (ComputeEngine_IF) registry.lookup(name);
TaskWithoutInput<Integer> getAnswer = new GetAnswerTask();
Integer answer = comp.compute(getAnswer);
System.out.println("Answer: " + answer);
}
catch (Exception e) {
System.err.println("RmiClientExample exception:");
e.printStackTrace();
}
}
}
两个项目都包含相同的安全策略文件(在RMIServer中称为server.policy,在RMIClient中称为client.policy)
grant {
permission java.security.AllPermission;
};
当然,我会在这项工作完成后再次限制权限
建筑/运行
由于代码与我从示例中复制的内容非常接近,我猜测代码是正确的,或者至少是接近的,但我的错误在于编译/运行代码。这个例子不是为eclipse编写的,所以我没有确切的说明
我做的第一件事是使用eclipse的jar导出向导将接口包jar到接口中。我刚刚放在RMIServer文件夹中的jar
然后,我使用以下定义运行ComputeEngine:
-Djava.rmi.server.codebase=file:/c:/Users/nate/workspace/RMIServer/Interfaces.jar
-Djava.rmi.server.hostname=localhost
-Djava.security.policy=c:/Users/nate/workspace/RMIServer/src/server.policy
计算引擎似乎只运行find并在代码中输出print语句
然后,我使用以下定义运行客户端:
-Djava.rmi.server.codebase=file:/c:/Users/nate/workspace/RMIClient/bin/
-Djava.security.policy=c:/Users/nate/workspace/RMIClient/src/client.policy
我得到以下错误消息:
RmiClientExample exception:
java.rmi.ServerException: RemoteException occurred in server thread; nested exception is:
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: client.GetAnswerTask
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Unknown Source)
at sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
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 com.sun.proxy.$Proxy0.compute(Unknown Source)
at client.RmiClientExample.main(RmiClientExample.java:24)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
java.lang.ClassNotFoundException: client.GetAnswerTask
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: client.GetAnswerTask
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.rmi.server.LoaderHandler$Loader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at sun.rmi.server.LoaderHandler.loadClassForName(Unknown Source)
at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
at sun.rmi.server.LoaderHandler.loadClass(Unknown Source)
at java.rmi.server.RMIClassLoader$2.loadClass(Unknown Source)
at java.rmi.server.RMIClassLoader.loadClass(Unknown Source)
at sun.rmi.server.MarshalInputStream.resolveClass(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
at sun.rmi.server.UnicastServerRef.unmarshalParametersUnchecked(Unknown Source)
at sun.rmi.server.UnicastServerRef.unmarshalParameters(Unknown Source)
... 13 more
有没有想过我可能做错了什么
# 1 楼答案
GetAnswerTask
实现了Serializable,因此当您调用ComputeEngine_IF.compute()
时,它将被序列化到服务器。所以GetAnswerTask
需要在服务器上、在其类路径上可用,而不是# 2 楼答案
对于那些仍在努力学习oracle RMI教程的人,他们的服务器不从给定客户机的代码库下载可序列化类(并且顽固地在自己的代码库中寻找类),请尝试添加
到服务器的参数