我正在使用RMI做我的第一步,我有一个简单的问题。
我有一个.jar文件,它可以从库中实现几种方法。 我想使用RMI在.jar文件中调用此方法。
我正在尝试创建一种包装器来实现它。
所以,我正在做这样的事情:
接口类:此接口具有远程对象要实现的方法。
实现类:这个类,具有接口方法的实现,每个实现调用.jar文件中的相应方法。例如,jar文件有一个名为getDetails()的方法,它返回一个“ResponseDetail”对象。 ResponseDetail是我在.jar中的响应类。
服务器类:它将方法绑定到rmiregistry
客户端类:它将使用实现中实现的方法。
到目前为止一切顺利? :)
现在,我有一个lib文件夹,其中包含.jar文件。
在服务器机器中,我部署了Interface,Implementation和Server类。我已经生成了存根,我成功地运行了rmiregistry,但是,有了这些细节:
要启动rmiregistry,我必须在命令行中设置classpath以引用.jar文件,否则我得到java.lang.NoClassDefFoundError。我用这个.sh文件做到了:
THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
rmiregistry -J-classpath -J".:${THE_CLASSPATH}"
要启动服务器,我必须设置类路径以引用.jar文件,否则,我得到java.lang.NoClassDefFoundError。我用过这样的东西:
THE_CLASSPATH=
for i in `ls ./lib/*.jar` do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
java -classpath ".:${THE_CLASSPATH}" Server
客户端计算机 要从客户端计算机运行Client.class文件,我必须将.jar文件复制到它,并在类路径中引用它们,否则,它不会运行,我得到java.lang.NoClassDefFoundError。我不得不在客户端机器上使用它:
THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
java -classpath ".:${THE_CLASSPATH}" HelloClient
这可以吗?我的意思是,我是否必须将.jar文件复制到客户端机器以通过RMI执行方法?
答案 0 :(得分:7)
在JDK v5之前,必须使用rmic
( RMI编译器)生成RMI存根。这是从JDK v5自动完成的。此外,您还可以从Java代码中启动RMI注册表。要从简单的RMI应用程序开始,您可能需要执行以下步骤:
import java.rmi.*;
public interface SomeInterface extends Remote {
public String someMethod1() throws RemoteException;
public int someMethod2(float someParameter) throws RemoteException;
public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException;
}
import java.rmi.*;
import java.rmi.server.*;
public class SomeImpl extends UnicastRemoteObject implements SomeInterface {
public SomeImpl() throws RemoteException {
super();
}
public String someMethod1() throws RemoteException {
return "Hello World!";
}
public int someMethod2( float f ) throws RemoteException {
return (int)f + 1;
}
public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException {
int i = someStruct.getInt();
float f = someStruct.getFloat();
someStruct.setInt(i + 1);
someStruct.setFloat(f + 1.0F);
return someStruct;
}
}
import java.io.*;
public class SomeStruct implements Serializable {
private int i = 0;
private float f = 0.0F;
public SomeStruct(int i, float f) {
this.i = i;
this.f = f;
}
public int getInt() {
return i;
}
public float getFloat() {
return f;
}
public void setInt(int i) {
this.i = i;
}
public void setFloat(float f) {
this.f = f;
}
}
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.net.*;
import java.io.*;
public class SomeServer {
public static void main(String args[]) {
String portNum = "1234", registryURL;
try{
SomeImpl exportedObj = new SomeImpl();
startRegistry( Integer.parseInt(portNum) );
// register the object under the name "some"
registryURL = "rmi://localhost:" + portNum + "/some";
Naming.rebind(registryURL, exportedObj);
System.out.println("Some Server ready.");
} catch (Exception re) {
System.out.println("Exception in SomeServer.main: " + re);
}
}
// This method starts a RMI registry on the local host, if it
// does not already exist at the specified port number.
private static void startRegistry(int rmiPortNum) throws RemoteException{
try {
Registry registry = LocateRegistry.getRegistry(rmiPortNum);
registry.list( );
// The above call will throw an exception
// if the registry does not already exist
} catch (RemoteException ex) {
// No valid registry at that port.
System.out.println("RMI registry is not located at port " + rmiPortNum);
Registry registry = LocateRegistry.createRegistry(rmiPortNum);
System.out.println("RMI registry created at port " + rmiPortNum);
}
}
}
import java.io.*;
import java.rmi.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
public class SomeClient {
public static void main(String args[]) {
try {
String hostName;
String portNum = "1234";
String registryURL = "rmi://localhost:" + portNum + "/some";
SomeInterface h = (SomeInterface)Naming.lookup(registryURL);
// invoke the remote method(s)
String message = h.someMethod1();
System.out.println(message);
int i = h.someMethod2(12344);
System.out.println(i);
SomeStruct someStructOut = new SomeStruct(10, 100.0F);
SomeStruct someStructIn = new SomeStruct(0, 0.0F);
someStructIn = h.someStructTest(someStructOut);
System.out.println( someStructIn.getInt() );
System.out.println( someStructIn.getFloat() );
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
更大的客户端 - 服务器应用程序应分为三个模块:client
,server
和common
(对于服务器和客户端代码之间共享的类,即远程接口和本例中的非原始对象)。然后,将从类路径上的client
+ common
模块和类路径上的server
+ common
模块中的服务器创建客户端应用程序。
多年前我用这个例子来学习RMI的基础知识,它仍然有效。然而,它远非完美(使用默认的Java包,不正确的异常处理,主机名和端口参数是硬编码的,不可配置等)。
然而,对初学者来说这是好事。所有文件都可以放在一个目录中,并使用简单的javac *.java
命令进行编译。然后,可以通过启动java SomeServer
命令使用java SomeClient
和客户端启动服务器应用程序。
我希望这有助于理解Java RMI,事实上,它远比这复杂得多。
答案 1 :(得分:3)
您不应该生成存根(如果您正在关注教程,那么方式旧)。你可以运行客户端而不必在本地使用jar(使用远程类加载),但是方式更容易使用本地可用的jar(我亲自完成)相当一部分RMI,从未实际部署过具有远程类加载的系统)。通常,您需要2个jar,一个“客户端”jar只包含远程接口(以及这些接口使用的任何Serializable类)和一个包含实现类的“服务器”jar。然后,您将使用服务器jar运行服务器,使用客户端jar运行rmiregistry / client。
这是一个非常好的(最新且简单的)getting started guide。
答案 2 :(得分:1)
简而言之,其他答案的阐述是:
客户端只需要公共接口(和客户端类),而不需要服务器实现。
服务器需要接口和实现(以及您的服务器主类)。
rmiregistry只需要接口。
(实际上,您可以在服务器进程中启动自己的注册表 - 然后根本不需要rmiregistry
。请查看createRegistry
中的java.rmi.registry.LocateRegistry
方法类。)
“接口”在这里表示远程接口和这些类作为参数或参数类型使用的任何(可序列化)类。
如何将这些类分发到jar文件与此无关。