Background
Everyone with Java background would know we can easily call methods on an instantiated object as long as everything is limited to same JVM (Java virtual machine). Even imagined if we can invoke methods on a remote object as if they were local. Well the answer is yes and that is exactly what we are going to see in this post. Infact RMI uses one of the famous design patters - The Proxy Pattern . We will cover that in detail under design patterns. For now keep following in mind -
Proxy Design Pattern involves a representative Object (A proxy) that controls access to another object which may be -
- Remote
- Expensive to Create (Virtual Proxy)
- Needs secure access (Protection Proxy)
This post aims to help you understand how RMI works, how objects interact with a simple demo.
Getting Started
Java RMI APIs essentially help you to invoke methods on remote object. There are four essential entities that take part in RMI -
- Client
- Stub / Client Helper (Proxy)
- Server
- Skeleton / Server Helper
Server is where you have your actual implementation of service. Client want to call it. Client code cannot directly call methods on remote server object as they are on different JVMs .So client creates a representation of actual service called stub or proxy and invokes methods on it. Stub takes this call, serializes method name, parameters etc and send its over network to the skeleton on the server. skeleton then deserializes the information and invokes the method on actual object. It takes the result serializes it and sends in back to stub. Stub deserializes the result and returns it back to the client code.
That's for the overview of how RMI works. Now lets see an actual demo in Java.
To The Code....
Lets start by writing an interface for the remote service -
import java.rmi.Remote; import java.rmi.RemoteException; public interface GreeterService extends Remote { public String displayWelcomeMessage(String name) throws RemoteException; }
Notice how our interface extends java.rmi.Remote interface? It is a marker interface(No methods in it) just like -
- java.lang.Cloneable
- java.io.Serializable
It is used to mark that methods of instances of class that implement this interface can be called from non local (remote) virtual machines.
Also notices how our method declaration throws RemoteException ? Well it's not mandatory but a good practice so that the implementation handles it and is aware that thing may go wrong as calls are made to another virtual machine (which may be remote).
Lets write implementation for this -
import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class GreeterServiceImpl extends UnicastRemoteObject implements GreeterService { protected GreeterServiceImpl() throws RemoteException { } @Override public String displayWelcomeMessage(String name) throws RemoteException { return "Hi " + name + " welcome to Open Source For Geeks!"; } public static void main(String args[]) { try { GreeterService greeterService = new GreeterServiceImpl(); Naming.rebind("greeterServiceObj", greeterService); } catch (Exception e) { e.printStackTrace(); } } }
So we wrote a class that implements the GreeterService and it's methods.
Notice how we have extended UnicastRemoteObject? That is because that super class takes care of actual communication part with remote VM.
Also notice we have declared a no-args constructor that throws RemoteException. That's because super class UnicastRemoteObject has constructor that throws that Exception.
- UnicastRemoteObject is used for exporting a remote object with JRMP and obtaining a stub that communicates to the remote object (Socket level logic).
- We will use this GreeterServiceImpl class to generate skeleton and stub and the communication logic is handled by UnicastRemoteObject that GreeterServiceImpl extends.
- JRMP is Java Remote Method Protocol. It is a Java-specific, stream-based protocol for Java-to-Java remote calls, requiring both clients and server to use Java objects. RMI-IIOP is an alternative protocol which exposes Java objects to CORBA ORBs.
- Create skeleton and stub,
- start rmi registry
- Start Remote Service
- javac *.java
You should have two class files - GreeterService.class and GreeterServiceImpl.class. Now you can go ahead and create stub skeleton . Run
- rmic GreeterServiceImpl
Note : From rmic 1.2 onwards, Java don't generate skeletion class any more. New JRMP protocol supported for RMI has got rid of the use of skeleton files.
Now start rmiregistry. Just type rmiregistry in command line.
Note 1 : Start rmiregistry from location that has access to the stub class file.
Note 2 : If you want to start your rmi registry on different port (defult is 1099) you can use command - rmiregistry [port]
Now start your remote Service -
- java GreeterServiceImpl
import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; public class GreeterClient { public static void main(String args[]) { try { GreeterService greeterService = (GreeterService) Naming.lookup("rmi://127.0.0.1/greeterServiceObj"); System.out.println(greeterService.displayWelcomeMessage("Aniket")); } catch (MalformedURLException | RemoteException | NotBoundException e) { e.printStackTrace(); } } }
Make sure your client Java process has access to following Java class files -
- GreeterClient.class
- GreeterService.class
- GreeterServiceImpl_Stub.class
- javac *.java
- java GreeterClient
Related Links
- Java remote method invocation Wiki
- Why does RMI registry need access to stub class file?(SO)
- what is RMI registry? (SO)
- Introduction to Java RMI
- Dynamic code downloading using Java™ RMI