Java RMI Distributed objects example
Image Source: Wikimedia.org
In Java RMI Hello World example we introduced the Java Remote Method Invocation with a very basic String-based communication between Server-Client. In this example we will take it one small step further and introduce Server-Client communication using Distributed Objects.
1. The Remote Interface
First we will develop the Remote Interface which contains all the methods that the Server implements. The Interface must always be public and extend Remote. All methods described in the Remote Interface must list RemoteException in their throws clause.
package com.mkyong.rmiinterface; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.List; public interface RMIInterface extends Remote { Book findBook(Book b) throws RemoteException; List<Book> allBooks() throws RemoteException;
2. The Distributed Object class
This is the class of the Object that the Server and Client will exchange, and must implement the Serializable Interface. It is also extremely important that this class declares an explicit serialVersionUID value to guarantee the consistency across different java compiler implementations.
If we ignore that or if the Distributed Object class of the Server declares a different serialVersionUID than the Distributed Object class of the Client, then the deserialization process will result in an InvalidClassException.
On Eclipse IDE you can generate a serialVersionUID like this:
package com.mkyong.rmiinterface; import java.io.Serializable; public class Book implements Serializable { private static final long serialVersionUID = 1190476516911661470L; private String title; private String isbn; private double cost; public Book(String isbn) { this.isbn = isbn; public Book(String title, String isbn, double cost) { this.title = title; this.isbn = isbn; this.cost = cost; public String getTitle() { return title; public String getIsbn() { return isbn; public double getCost() { return cost; public String toString() { return "> " + this.title + " ($" + this.cost + ")";
3. The Server
The Server extends UnicastRemoteObject and implements the RMIInterface. In the main method we bind the server on localhost with the name “MyBookstore”. For simplicity instead of using a database or a File, we created the method initializeList() which fills a List with Book type Objects that represent the books our Bookstore has (… yes only 5. But 5 awesome books!).
We also need to add a serialVersionUID for the Server, but since the Server we designed in this example intends to exist only on one machine we don’t need to think about it twice, we can just set a default serialVersionUID. If, however, the Server Class was also distributed, we would have to do make sure the serialVersionUID for the class was the same across all platforms that implemented it.
package com.mkyong.rmiserver; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; import com.mkyong.rmiinterface.Book; import com.mkyong.rmiinterface.RMIInterface; public class Bookstore extends UnicastRemoteObject implements RMIInterface { private static final long serialVersionUID = 1L; private List<Book> bookList; protected Bookstore(List<Book> list) throws RemoteException { super(); this.bookList = list; //The client sends a Book object with the isbn information on it //(note: it could be a string with the isbn too) //With this method the server searches in the List bookList //for any book that has that isbn and returns the whole object @Override public Book findBook(Book book) throws RemoteException { Predicate<Book> predicate = x -> x.getIsbn().equals(book.getIsbn()); return bookList.stream().filter(predicate).findFirst().get(); @Override public List<Book> allBooks() throws RemoteException { return bookList; private static List<Book> initializeList() { List<Book> list = new ArrayList<>(); list.add(new Book("Head First Java, 2nd Edition", "978-0596009205", 31.41)); list.add(new Book("Java In A Nutshell", "978-0596007737", 10.90)); list.add(new Book("Java: The Complete Reference", "978-0071808552", 40.18)); list.add(new Book("Head First Servlets and JSP", "978-0596516680", 35.41)); list.add(new Book("Java Puzzlers: Traps, Pitfalls, and Corner Cases", "978-0321336781", 39.99)); return list; public static void main(String[] args) { try { Naming.rebind("//localhost/MyBookstore", new Bookstore(initializeList())); System.err.println("Server ready"); } catch (Exception e) { System.err.println("Server exception: " + e.getMessage());
4. The Client
The Client “finds” the Server through the RMIInterface Object that “looks” for a reference to the remote object associated with the name we pass as parameter. What we just described is what Naming.lookup("//localhost/MyBookstore"); does.
The rest of the code for the Client is just to make a working example that you could hopefully experiment with.
package com.mkyong.rmiclient; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.util.List; import java.util.NoSuchElementException; import javax.swing.JOptionPane; import com.mkyong.rmiinterface.Book; import com.mkyong.rmiinterface.RMIInterface; public class Customer { private static RMIInterface look_up; public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException { look_up = (RMIInterface) Naming.lookup("//localhost/MyBookstore"); boolean findmore; do { String[] options = {"Show All", "Find a book", "Exit"}; int choice = JOptionPane.showOptionDialog(null, "Choose an action", "Option dialog", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); switch (choice) { case 0: List<Book> list = look_up.allBooks(); StringBuilder message = new StringBuilder(); list.forEach(x -> { message.append(x.toString() + "\n"); }); JOptionPane.showMessageDialog(null, new String(message)); break; case 1: String isbn = JOptionPane.showInputDialog("Type the isbn of the book you want to find."); try { Book response = look_up.findBook(new Book(isbn)); JOptionPane.showMessageDialog(null, "Title: " + response.getTitle() + "\n" + "Cost: $" + response.getCost(), response.getIsbn(), JOptionPane.INFORMATION_MESSAGE); } catch (NoSuchElementException ex) { JOptionPane.showMessageDialog(null, "Not found"); break; default: System.exit(0); break; findmore = (JOptionPane.showConfirmDialog(null, "Do you want to exit?", "Exit", JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION); } while (findmore);
5. How to run it
5.1 After you create the four java files with your favorite IDE or Download the code below, navigate to your source folder as shown below.
5.2 First thing we need to do is compile our sources. Run 1. compileEverything.bat if you downloaded the code below or open a command window at your directory and run:
$ javac src/com/mkyong/rmiinterface/RMIInterface.java src/com/mkyong/rmiinterface/Book.java src/com/mkyong/rmiserver/Bookstore.java src/com/mkyong/rmiclient/Customer.java
5.3 Confirm that your sources were compiled by accessing their respective directories:
5.4 Next we need to start the rmiregistry. Again either run the 2. startServer.bat or open a command window and run:
$ cd src $ start rmiregistry $ java com.mkyong.rmiserver.Bookstore
5.5 If RmiRegistry started successfully, there will be another window that looks like this:
5.6 Now we are ready to run our Client:
Open a new command prompt window (or run the 3. runClient.bat from the downloaded files) and run this:
$ cd src $ java com.mkyong.rmiclient.Customer
5.6.1 The Customer class runs and prompts us for action:
5.6.2 When we click “Show All” button:
5.6.3 After clicking “OK” button:
5.6.4 We click “No” button, since we don’t want to exit yet and the following dialog comes up. We type an ISBN (ex. “978-0596009205”) and click “OK”.
5.6.5 If the book was found in the Server’s list:
5.6.6 If it wasn’t found:
5.6.7 The program will continue by asking us:
If we click “Yes” button the program will exit. If we click “No” it will bring us back to the main menu to choose an action as shown in 5.6.1.
The Server will keep running until we close its window. Even after the Client closes we can open a new one or even multiple Clients that run simultaneously.
References
- Serializable Interface – Java API
- Remote – Java API
- UnicastRemoteObject – Java API
- Java RMI Hello World example
- Wikipedia – Java Remote Method Invocation
- Wikipedia – Distributed Objects
From:一号门
COMMENTS