Friday 28 March 2014

RMI By Example

Hello and welcome to my very first blog. I was actually persuaded by someone to join this Blogging thing and help out given the knowledge i have regarding Software Engineering. Well, I shall start by doing a simple RMI application. Tuts about Java are best learnt from Oracle under the docs.oracle.com website. Well, Lets see how this works.
Here's how the work shall proceed. I actually found out that this whole procedure is really simple. Forgive me for wasting time but lets get started

  1. Designing the client stub
  2. Designing the Client
  3. Designing the Server stub
  4. Designing the server stub.
Designing The Client Stub
Look at this as a server's face to the client. it contains the server's functionality (features) presented to the client. It's implemented by the Server not the client but since it is downloaded on to the client side, it's a client stub. Hope that makes sense. We shall do a simple Hello World and adding 2 numbers. We shall add more later as we progress. Here's how it shall look like:

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

     public interface ServerInterface extends Remote 
     {
          public String sayHello(String name) throws RemoteException;
          
          public int getSum(int x, int y) throws RemoteException;
      
     }

Let's go through what we just did. 

  • first we import the rmi (remote method invocation) package Interface Remote and extend it
  • along that we also import the RemoteException Class so we can catch those errors and understand then easily.
  • we then create two methods sayHello() to say hello to the client and getSum(int, int) that takes in two integers and returns their sum. The only difference here is that the server shall perform these tasks and return results to the client as objects. 
  • All Methods in an interface that extends Remote must throw RemoteException. 


Designing the Server
The client shall use these methods declared above to access the server's operations. Here's a tip. These methods in the interface above simply tell the client what the server can do. it provides the client with access to the server. (gateway).
Here's the code, it's really simple.

    import java.rmi.Naming; 
    import java.rmi.RemoteException; 
    import java.rmi.server.UnicastRemoteObject;    
    public class ServerImplem extends UnicastRemoteObject 
                          implements ServerInterface
    {
        //so we can actually catch any exception when creating an object.
        public ServerImplem() throws RemoteException
        {}


        public String sayHello(String name)

          {
             return "Hello " + name;
          }
        public int getSum(int x, int y)
          {
            return (x+y);
          }
       public static void main(String[] args)
          {
               try{


               ServerImplem obj = new ServerImplem();

               //Bind the (obj) instance to the name ExampleApp
               Naming.rebind("ExampleApp", obj);
               System.ot.println("Waiting for connections...");
              }catch(Exception e) {
                 e.printStackTrace();
                }
           
          }
     
    }

Stepping through the code, we find this:

  • When you extend java.rmi.server.UnicastRemoteObject, your class is automatically exported upon creation so we need to extend this Class so we can join the band wagon. 
  • Since exporting an object can potentially throw a java.rmi.RemoteException, you must define a constructor that throws a RemoteException,  A RemoteException can occur during construction if the attempt to export the object fails--due to, for example, communication resources being unavailable or the appropriate stub  class not being found.

Instantiate a remote object

  • The main method of the server creates an instance of the remote object implementation:
    ServerImplem obj = new ServerImplem();
    The constructor exports the remote object: Once created, the remote object is ready to accept incoming calls.

Register the remote object

For a client to invoke a method on a remote object, it must get a reference to the remote object. The RMI system provides a remote object registry that allows you to bind a URL-formatted name of the form "//host/objectname" to the remote object, where objectname is a simple string name, like the one passed in the method rebind("URL", objectname), The RMI registry is a simple server-side name server that allows remote clients to get a reference to a remote object. It typically is used to locate only the first remote object an RMI client needs to talk to. Then, that first object in turn, provides application-specific support getting references for other objects


Designing the Client Interface
With this, we shall simply design out application so that it can contact the registry for the objects using it's reference URL and get the server object so it can make calls (procedures).
Here's the Client code.

 import java.rmi.RMISecurityManager; 
 import java.rmi.Naming; 
 import java.rmi.RemoteException;

 public class Client 
   {
   /* download server's stubs( must set a SecurityManager 
     but for starters, you can omit this out for now but use it in future. )
       */  
      public static void main(String arg[]) 
    {         
     try 
     { 
           System.setSecurityManager(new RMISecurityManager()); 
     ServerInterface obj = (ServerInterface) Naming.lookup( "//" + 
                "localhost" + 
                "/ExampleApp");  //objectname (reference) in registry 
     System.out.println(obj.sayHello("Douglas")); //returns Hello Douglas  
          System.out.println("sum is "+obj.getSum(3,5));  //returns sum of 3 and 5  
     } 
     catch (Exception e) 
    { 
     System.out.println("Client exception: " + e.getMessage()); 
     e.printStackTrace(); 
    } 
   }
 }

Stepping through this one...



  • Set the security manager, so that the client can download the stub code.

    1. We get a reference to the remote object implementation (advertised as "ExampleApp") from the server host's rmiregistry.
    2. Invoke the remote sayHello method  while passing a string by reference on the server's remote object. 
    3. Similarly, we invoke the getSum(int, int) method using the same object and pass two integers, it then returns a sum of the two integers. 
    Compile & Deploy Class Files
    • Now that the code is complete, it's time to test it and see if it actually works. Lets compile the code as below.
    • create a folder and name it anything you like. I'll name mine RMIExamples. 
    • add the three files into it (Client.java, ServerInterface.java and ServerImplem.java) then open your terminal and navigate to the folder.
    • Here's how you can navigate in case you have no idea. 
    • For Unix/Linux
    • cd /home/<path-to-folder>
    • In windows, use the cd command to go to your folder except slashes are backward (\) not Unix Like(/) 
    • I like to separate class files from source code so i create a subfolder inside my RMIExamples folder called classes. then compile like this
    • javac -d classes/ *.java  
    • navigate to the classes folder and create a stub like this
    • rmic ServerImplem
    • Only classes that implement the UnicastRemoteObject can create stubs
    • then you need to start the registry. it differs for each platform
    • Linux/Unix type in the terminal: 
    • rmiregistry 
    • (optionally add the & for background running)
    • Now if everything is going well, it's time to start your server
    • java ServerImplem
    • Should display something like:  Waiting for connections...
    • then open another terminal and run the client
    • java Client 
    • which should display something like 
    • Hello Douglas
    • 8
    • Congs upon your first (i hope so) RMI program. For any questions, please send me a mail to olupotd@aol.com or leave a comment below. Thanks. Our next tutorial is about building a simple chat application using RMI. but any suggestions are welcome. Thanks again.