Hi Team,

I have been trying to understand the concept of RMI by writing a simple classes for

  1. RMIInterface
  2. RMIServer
  3. RMI Client

I have written three classes in eclipse and started RMIRegistry by running start rmiregistry command. When i tried to compile RMI server class im getting an error as mentioned below. MY code for RMI server, RMI interface and RMIClient is shown below, Please explain how to get my code to work.

FYI cross posted in Dreamincode.net Click Here

ERROR :

Unknown host: sample.test; nested exception is:
java.net.UnknownHostException: sample.test

RMI Server

package com.rmi.client;

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RMIServer extends UnicastRemoteObject implements RMIInterface{

    /**
     * @param args
     */

    public RMIServer() throws RemoteException {

        super();

        try {

            Naming.rebind("//sample.test/RMIServer", this);
        }catch(Exception e){
            System.out.println(e.getMessage());
        }

    }

    public static void main(String[] args) throws RemoteException {
        // TODO Auto-generated method stub

        RMIServer reference = new RMIServer();

    }

    @Override
    public String query(String request) throws RemoteException {
        // TODO Auto-generated method stub
        return "working";
    }

}

RMI Interface

public interface RMIInterface extends java.rmi.Remote{


    public String query(String request)throws java.rmi.RemoteException;


}

RMI Client

package com.rmi.client;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient {



    public RMIClient() throws MalformedURLException, RemoteException, NotBoundException{

        RMIInterface remoteObject = (RMIInterface)Naming.lookup("//sample.test/RMIServer");
        String reply = remoteObject.query("sample");
        System.out.println(reply);

    }
    public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
        // TODO Auto-generated method stub

        RMIClient reference = new RMIClient();

    }

}

The name being bound is normally just an ordinary string, maybe starting with a package name just to keep it unique. The doc says it's not parsed, but it looks like the // at the front is causing someone to try to parse & interpret it. This is just a guess, but try something simpler eg "sample.test.RMIserver"

I have tried giving "sample.test.RMIServer". Now im getting a different error as below

RemoteException occurred in server thread; nested exception is: 
    java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: RMIInterface

I only have .java files of RMIserver and RMIInterface in the source folder of the package. Since RMIInterface has no main Method I cannot compile that and create a .class file right ? Then please explain how can I have the .class file of RMIInterface in classpath of the server

Yes, I found the class files of RMIserver and interface in the same folder which is C:\Users\jacksons5\workspace\rm\bin\com\rm\server

Still getting the same error.

FYI I am using eclipse. DO I have to specify any configurations in order to run it successfully.

I'm comparing your code with a small demo I wrote a while ago - I thought the demo was pretty minimal, but your code is a lot smaller, so maybe you're missing something essential. Just in case you may find it useful, here's that demo...

The interface is just one method that takes an int, adds one to it, and returns the result...

package misc;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RMIInterface extends Remote {

   public int plusOne(int i) throws RemoteException;
}

The client just locates the server, calls the remote method and prints the result...

package misc;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIClient {

   public static void main(String args[]) {

      try {
         RMIServer.rmiStarter(RMIInterface.class);
         Registry registry = LocateRegistry.getRegistry();
         String serviceName = "PlusOne";
         RMIInterface server = (RMIInterface) registry.lookup(serviceName);
         int i = server.plusOne(2);
         System.out.println("RMIClient result: " + i);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

}

The server is more interesting because it includes some bullt-proofing code to avoid some common configuration problems... (1) start the naming server if one isn't already running and available, (2) set the java.rmi.server.codebase System property, and (3) creates a security policy file that allows everything and uses that.

package misc;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class RMIServer implements RMIInterface {

   @Override
   public int plusOne(int i) throws RemoteException {
      return ++i;
   }

   public static void main(String[] args) {

      try {
         rmiStarter(RMIInterface.class);
         Registry registry = startRegistry();
         String serviceName = "PlusOne";
         RMIInterface server = new RMIServer();
         RMIInterface stub = (RMIInterface) UnicastRemoteObject.exportObject(server, 0);
         registry.rebind(serviceName, stub);
         System.out.println("RMIServer ready.");
      } catch (RemoteException e) {
         e.printStackTrace();
      }
   }


   /* Set up the codebase and security policy, starts registry if not already running, returns the
    * Registry Avoids those horrible problems getting the paths right
    *
    * Based on code by srasul (publshed under an Apache 2 License)
    * http://code.nomad-labs.com/2010/03/26/an-improved-rmi-tutorial-with- eclipse/
    */

   public static <T extends Remote> void rmiStarter(Class<T> classToAddToServerCodebase) {

      // set the codebase to the place where we know the class is
      System.setProperty("java.rmi.server.codebase", classToAddToServerCodebase
              .getProtectionDomain().getCodeSource().getLocation().toString());

      // create a simple security file in a known location, and use that
      final String POLICY_FILE_CONTENT = "grant {\n"
              + "permission java.security.AllPermission;\n" + "};\n";
      try {
         File tempFile = File.createTempFile("rmi-base", ".policy");
         BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
         writer.write(POLICY_FILE_CONTENT);
         writer.close();
         tempFile.deleteOnExit();
         System.setProperty("java.security.policy", tempFile.getAbsolutePath());
         if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   static Registry startRegistry() {
      // Try to start registry (so no need to start manually)
      // If it already exists, just get the existing one
      Registry registry = null;
      try {
         registry = LocateRegistry.createRegistry(1099);
      } catch (RemoteException e) {
         // System.out.println("Create failed, locating pre-existing registry");
         try {
            registry = LocateRegistry.getRegistry();
         } catch (RemoteException e1) {
            e1.printStackTrace();
         }
      }
      return registry;
   }

}

Edited 3 Years Ago by JamesCherrill

Thanks for the demo, im going thru it and will let you know my understanding shortly

Hi James,

Demo works perfectly and one thing i completely dont understand is the below line

public static <T extends Remote> void rmiStarter(Class<T> classToAddToServerCodebase)

Normally i have seen and written method names which goes like

public static void method name(argument) {

}

what is that <T extends Remote> and what is inside parenthesis (class<T> classToAddToServerCodebase)

I does not look like a usual method definition convention i know, Please throw some light on it

Its an example of Java Generics - introduced with Java 5 (AKA 1.5).
I could try to explain then more, but Oracle have already done that in their excellent tutorials
http://docs.oracle.com/javase/tutorial/java/generics/index.html

This particular example means the parameter classToAddToServerCodebase must be an instance of Class, but that Class must be a class that extends (implements) the Remote interface.
I could just say

    public static void rmiStarter(Class classToAddToServerCodebase)

but then it would allow any old Class to be passed and the code would fail at runtime. Through the generic spec the compiler will know to check that any Class passed to my method will implement Remote and thus work properly.

This question has already been answered. Start a new discussion instead.