Please go through Java RMI Basics before reading this topic.
How does RMI works?
RMI (Remote Method Invocation): RMI enables us to call a Remote java API as if it is a local method and we gets the results. EJBs (Enterprise Java Beans) were primarily built using RMI. Here is what happens behind the scene when you invoke a remote API. RMI requires communication across the network for executing an API on a remote server machine. The remote method invocation is done by using RMI Stub at client side and RMI server handling at server (replacing the earlier version of RMI Skeleton).
|Working of RMI|
RMI Stub is generated from the Remote API class using RMI compilation (rmic utility comes with JDK). A remote API requires to implement a Remote interface (Interface extending java.rmi.Remote) for the JVM to identify that this API can be invoked remotely. The Remote API need to extend java.rmi.UnicastRemote for making use of export method in the default constructor and make the Remote Object available to accept client connections by listening on a TCP port.
The Remote API needs to be registered in RMI Registry for making it available for the client access. When then Remote API is registered in the RMI registry, the Remote API Stub object is loaded into the RMI Registry and made available for client invocations. The Remote API does not leave its JVM and it is the stub object that the client receives on lookup.
RMI client access the Remote API via RMI Stub. So, the RMI client needs to have RMI Stub and Remote Interface in the class path. The Remote Interface needs to be there at the client side for the client application to have reference to the Remote API.
Java don't require to generate a skeleton class any more for server side parsing of the RMI client message from Java 1.2. A skeleton for a remote object is a JRMP protocol server-side entity that has a method that dispatches calls to the actual remote object implementation. A sub protocol is introduced that eliminates the need for skeletons.
The client java program performs a lookup in RMI registry to get the RMI Stub of the actual server side implementation. The client side Application makes call to the Remote API via Stub and Stub makes a network socket connection to the server and converts the API invocation data into a byte stream along with relevant method parameters and passes on this information in byte stream.The server side RMI container will listen to such messages sent to the RMI port and passes it on to the RMI Server.
The RMI Server will receive this byte stream information, reconstruct java Objects and uses it to invoke the required Java API implementation. The return value will again go to the Server and get converted into byte stream and RMI container returns it to the client side Stub. The client side Stub reconstruct the return value to the java object and returns to the calling java program.
Need for Serialization
While discussing RMI, we had seen the necessity of sending java objects across the network as byte streams and reconstructing them back as java objects. Here comes the requirement for serialization.
The purpose of serialization in Java is to support transfer an object from one JVM to another. This is done by converting java objects into byte stream so that it can be transported through the network via socket or stored in a file or repository. The transported or stored stream is de-serialized back to java object at the target JVM. In use cases where we have heavy complex object, the byte stream can be written into a database and read back when we require the data to be displayed again.
Steps for making a class serializable
1. Implement Serializable interface: This is an empty interface and have no definitions. This is a marker interface and just tells JVM that this class can be serialized. An instance of a Serializable java object can be written into an ObjectOutputStream for transporting across network or for storing and while deserialization, the stream is taken as ObjectInputStream and read back to Object.
2. All instance variables used in the Serializable class or it's super class should also be Serializable: We may end up having a variable with an inbuilt data type with variables that are not Serializable. For example, elementData of ArrayList.
If the data is not important in the new JVM, we can declare this variable as transient. Transient variables are not considered during serialization.
If the data is important to us, then the ideal solution is to implement custom serialization deserialization logic using writeObject and readObject methods respectively. The JVM will invoke these methods instead of the default methods for serialization.
3. Override equals() and hashCode() if necessary: Suppose if we are making a deep copy of the java object using serialization, the default equals() method would return false as it compare the reference of the java objects but they are the copy of same objects. In such cases, we may require to override equals() and hashCode() methods.
Serial Version UID and its significanceWhat happens if the class definition of the JVM is old from the current version of the serializable object? What if a new property is added to the Serializable object and the remote JVM does not know about it?
When the class is serialized, the serial version UID is serialized along with other contents. The JVM which deserializes the object compares the serial version UID of loaded class with that in the content.
If they do not match, then InvalidClassException is thrown.
In such scenarios, the more desirable behaviour is to ignore the value of the newly added variable for backward compatibility. For achieving this, we can declare the serial version UID ourselves. This means the logic of JVM will not be used for generating the serial version UID. If the serial version UID of two class definitions are same, there won't be any exception thrown and the newly added variable will have the default value.
For creating a sample RMI Application, please go through Java RMI Basics