我是否需要客户端,服务器和注册表上的所有类才能使用RMI?

时间:2011-08-16 19:32:31

标签: java classpath rmi

我正在使用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执行方法?

3 个答案:

答案 0 :(得分:7)

在JDK v5之前,必须使用rmic RMI编译器)生成RMI存根。这是从JDK v5自动完成的。此外,您还可以从Java代码中启动RMI注册表。要从简单的RMI应用程序开始,您可能需要执行以下步骤:

  1. 创建界面:
    
    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;
    }
    
  2. 实现界面:
    
    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;
      }
    }
    
  3. 实现要在客户端和服务器之间传递的非原始可序列化对象:
    
    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;
      }
    }
    
  4. 实现服务器:
    
    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);
        }
      }
    }
    
  5. 实施客户:
    
    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();
        }
      }
    }
    
  6. 更大的客户端 - 服务器应用程序应分为三个模块:clientservercommon(对于服务器和客户端代码之间共享的类,即远程接口和本例中的非原始对象)。然后,将从类路径上的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文件与此无关。