Do I need all classes on the client, server and registry for RMI to work?
Asked Answered
O

3

7

I'm doing my first steps with RMI, and I have a simple question.

I have a .jar file which has the implementation of several methods from a library. I want to call this methods in the .jar file using RMI.

What I'm trying is to create a kind of a wrapper to do it.

So, I'm working on something like this:

Interface class: This interface has the methods to be implemented by the remote object.

Implementation class: This class, has the implementation of the interface methods, each implementation calls the corresponding method in the .jar file. E.g., the jar file has a method called getDetails(), and it returns a "ResponseDetail" object. ResponseDetail is a response class I have in the .jar.

Server class: it binds the methods to the rmiregistry

Client class: it will consume the methods implemented in implementation.

So far so good? :)

Now, I have a lib folder where resides the .jar file.

In the server machine I have deployed the Interface, Implementation and Server classes. I've generated the stub, and I ran the rmiregistry successfully, but, with these details:

To start the rmiregistry, I had to set the classpath in the command line to reference the .jar files, otherwise I get the java.lang.NoClassDefFoundError. I did it with this .sh file:

THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
    THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

rmiregistry -J-classpath -J".:${THE_CLASSPATH}" 

To start the server, I had to set the classpath as well to reference the .jar files, otherwise, I get the java.lang.NoClassDefFoundError. I've used something like this:

THE_CLASSPATH=
for i in `ls ./lib/*.jar` do
  THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

java -classpath ".:${THE_CLASSPATH}" Server

Client machine: To run the Client.class file from the client machine, I had to copy the .jar files to it, and make reference to them in the classpath, because otherwise, it does not run and I get the java.lang.NoClassDefFoundError. I had to use this on the client machine:

THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
   THE_CLASSPATH=${THE_CLASSPATH}:${i}
done

java -classpath ".:${THE_CLASSPATH}" HelloClient

Is this ok? I mean, do I have to copy the .jar files to the client machine to execute methods through RMI?

Ordnance answered 16/8, 2011 at 19:32 Comment(1)
Welcome to Stack Overflow! I changed your title to be a bit more indicative about your problem, and also reformatted your post a bit. Please check that I didn't destroy anything of your meaning.Psaltery
A
7

Prior to JDK v5 one had to generate the RMI stubc using the rmic (RMI Compiler). This is done automatically from JDK v5 on. Moreover, you can start the RMI Registry from within the Java code as well. To start with a simple RMI application you may want to follow the following steps:

  1. Create the interface:
    
    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. Implement the interface:
    
    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. Implement a non-primitive serializable object that is to be passed between a client and the server:
    
    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. Implement the server:
    
    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. Implement the client:
    
    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();
        }
      }
    }
    

A larger client-server application should be divided into three modules:client, server, and common (for classes shared between the server and client code, i.e. the remote interface and the non-primitive object in this example). The client application will then be created from client + common modules on the classpath and the server from server + common modules on the classpath.

I used this example many years ago to learn basics of RMI and it still works. However it is far from being perfect (default Java package used, incorrect exception handling, hostname and port parameters are hard-coded and not configurable, etc.)

Nevertheless, it is good for starters. All the files can be placed in one directory and compiled using the simple javac *.java command. The server application can then be started using the java SomeServer and the client one by launching the java SomeClient command.

I hope this helps to understand the Java RMI which is, in fact, far more complicated than just this.

Alunite answered 16/8, 2011 at 20:59 Comment(1)
Wow, thanks Jiri, I'll try your example, it looks pretty closer to what I need. Then I'll let you know if it worded or not.Ordnance
L
3

You shouldn't be generating stubs (if you are following a tutorial, it is way old). you can run the client without necessarily having the jars locally (using remote classloading), but it's way easier to do it this with the jars available locally (i've personally done a fair bit of RMI and never actually deployed a system with remote classloading). typically, you want 2 jars, a "client" jar with just the remote interfaces (and any Serializable classes used by those interfaces) and a "server" jar which includes the implementation classes. you would then run the server with the server jar, and the rmiregistry/client with the client jars.

This is a pretty good (up to date and simple) getting started guide.

Lewd answered 16/8, 2011 at 20:24 Comment(2)
Thanks jtalhborn! I'll give a try to that guide and let you know how did it go.Ordnance
Hi jtalhborn! I've tried that sample, but I can't make it run from a remote client machine :(, and it is not including any extra jar files. I'll keep trying, but thanks anyway.Ordnance
E
1

To say it in short what the other answers elaborated:

  • The client needs only the common interfaces (and the client classes), not the server implementation.

  • The server needs interfaces and implementation (and your server main class).

  • The rmiregistry needs only the interfaces.

    (Actually, you can start your own registry inside the server process - then you don't need the rmiregistry at all. Have a look at the createRegistry methods in the java.rmi.registry.LocateRegistry class.)

"Interfaces" here means both the remote interfaces and any (serializable) classes used by these as parameter or argument types.

How you distribute these classes to jar files is independent of this.

Empiricism answered 16/8, 2011 at 21:39 Comment(1)
Thanks Paŭlo! regarding your points, if the first is true: The client needs only the common interfaces (and the client classes), not the server implementation. Then I may be doing something wrong, because I'm having to have the .jar files in the client to run the calls. I'll give a try to the examples above and see how does it works.Ordnance

© 2022 - 2024 — McMap. All rights reserved.