Remote Method Invocation in Java: Callback

1. CALLBACK

In a typical RMI application, clients invoke methods on remote objects created by the server. Callback mechanism (as the name suggests) allows server to call methods back on remote objects created and passed by the clients. This service is necessary in many cases. Consider the following situations:

When a client invokes a method on a remote object and the method takes a long time to complete, the client has to wait for the method to return. Callback mechanism allows the client to return to whatever it was doing quickly after making a call. The server, having once completed the method execution, sends the response back to the client asynchronously by calling a method on a remote object that was created and passed by the client to the remote object created by the server.

Consider another situation. A client wants to display some data (say score of a game) as soon as it arrives on a server. Since, the client does not have any idea when new data will arrive at the server, it may periodically fetch data from the server. This may result in fetching the same data unnecessarily, probably many times, that leads to consumption of network bandwidth as well as client and server resources. To avoid this problem, instead of getting data by the client from server, the server may send data to the client only when new data arrives.

The implementation of callback mechanism is very simple. As mentioned earlier, Java RMI allows us to pass even a remote object (i.e its class implements Remote interface) to a remote method. To understand callback mechanism, it is necessary to understand what happens when remote objects are passed in Java RMI.

We know when an object is exported, an instance of its skeleton is created. This skeleton wraps around the object and has similar methods of the remote object. So, when a client creates, exports and passes a remote object to a remote method, the following actions are taken by Java RMI [Figure 14.11:].

  • An instance a proxy (stub) of this remote object is created.
  • This stub knows everything (port number and IP address) about the arguments object’s skeleton.
  • This stub is then serialized to have a linear array of bytes.
  • This serialized data is sent to the recipient (i.e. remote method) over the network using socket.
  • At the recipient, the stub instance is reconstructed and installed.
  • Remote methods formal parameter refers to this instance.

Since, remote method’s formal parameter now refers to a proxy to the actual object passed by the client, a method call through the formal parameter results in a similar method invocation on local proxy object, which in turn forwards the method invocation information towards the actual object. The server can then asynchronously invoke the remote methods on remote client objects in the same way the client asynchronously invokes methods on remote-server objects.

In the following section, we shall develop a simple application as follows:

A client creates a receiver object and exports it. It then gets a reference to a remote notifier object. The client, to register itself, calls registerMe() method of notifier object passing this receiver object. Inside registerMe() method, the server calls the notify() method back on the receiver object passing a string.

Again note that the application that we are going to develop does not exhibit all the power of callback mechanism. It merely shows us to write an RMI application using call back mechanism.

1.1. Creating interfaces

Since, the client and server separately create their remote objects, we need two interfaces:

//Receiver.java import java.rmi.*;

public interface Receiver extends Remote {

public void notify(String s) throws RemoteException;

}

This is the interface for a remote object to be created by client. This declares a single method notify() that accepts a String. The interface for server-side object is as follows:

//Notifier.java import java.rmi.*;

public interface Notifier extends Remote {

public void registerMe(Receiver r) throws RemoteException;

}

This interface defines a single method registerMe() to register a Receiver object.

2.2. Implementing interfaces

The following is a simple implementation class for Notifier:

//SimpleNotifier.java import java.rmi.*;

public class SimpleNotifier implements Notifier

{

public void registerMe(Receiver r)

{

try

{

System.out.println(”registered the receiver : ”+r);

String msg = ”A message from SimpleNotifier”;

r.notify(msg); //callback

System.out.println(”Sent : ”+msg);

}catch(RemoteException e) {}

}

}

The registerMe() method simply calls the notify() method on specified Receiver with a message.

The crucial part for the client to have callback service is to create remote object and export it. A client can do this in two ways:

  • Create a class implementing a remote interface and extending rmi.server.unicastRemoteObject or javax.rmi.PortabieRemoteObject. In this case, the object is exported automatically during its instantiation.
  • Create a class implementing a remote interface but, the class does not extend rmi.server. UnicastRemoteObject or javax.rmi.PortableRemoteObject. Export the object explicitly using static exportObject() method of java.rmi.server.UnicastRemoteObject or javax.rmi. PortableRemoteObject class.

Since in our previous calculator application, we have used the second method, let us use the first method for this application. The implementation of Receiver is as follows:

//SimpleReceiver.java import java.rmi.*;

import java.rmi.server.*;

public class SimpleReceiver extends UnicastRemoteObject implements Receiver, java.io.Serializable

{

SimpleReceiver() throws RemoteException {}

public void notify(String msg)

{

try {

System.out.println(”received : ” + msg);

}catch(Exception e) {e.printStackTrace();}

}

}

Note that this implementation class implements Serializable interface. This is necessary as Java RMI uses serialization to send and receive objects. It also extends UnicastRemoteObject. So, its instance is exported automatically during its creation. The notify() method simply displays the string received.

1.3. Writing the server

The server application is as simple as our previous Calculator server program. The complete source code for the server (stored in CallbackServer.java) is shown below:

import java.rmi.*;

import java.rmi.registry.*;

import java.rmi.server.*;

public class CallbackServer {

public static void main(String args[]) {

try {

String name = “notifier”;

SimpleNotifier notifier = new SimpleNotifier();

Notifier stub = (Notifier)UnicastRemoteObject.exportObject(notifier, 0);

Registry registry = LocateRegistry.createRegistry(1099);

registry.rebind(name, stub);

System.out.println(”Notifier ready…”);

}catch (Exception e) { e.printStackTrace(); }

}

}

1.4. Writing the client

The complete source code for the client (stored in CaiibackClient.java) is shown below:

import java.rmi.*;

public class CallbackClient {

public static void main(String args[]) {

try {

String url = ”rmi://” + args[0] + “/notifier”;

Notifier notifier = (Notifier)Naming.lookup(url);

SimpleReceiver recv = new SimpleReceiver();

notifier.registerMe(recv);

}

catch (Exception e) { e.printStackTrace();

}

}

}

Note that the instance recv is not exported explicitly. Since, its class (SimpleReceiver) extends, UnicastRemoteObject, it is exported during its creation by the constructor of UnicastRemoteObject class.

1.5. Compiling the application

Compile the programs using javac command as described earlier.

1.6. Running the application

Go to the server_home directory and use the following command to start the server.

javac CallbackServer

Note that, a registry is created on a port 1099 in the server program using static createRegistry() method on LocateRegistry class. So, starting a registry using rmic is no longer needed. We have assumed that the server is started in a computer having IP address 172.16.5.81.

Now go to the client_home directory and use the following command to start the client.

javac CallbackClient 172.16.5.81

A sample result is shown in Figure 14.12:

2. ANOTHER CALLBACK APPLICATION

In our previous application, we have shown how to develop a simple callback application. In this section, we shall develop a sophisticated application as follows:

The application consists of a server and one or more clients. The server is capable of notifying the score of a game. The clients register themselves to receive the score from the server. To implement this idea, the same Notifier and Receiver interfaces are used, The implementation of Notifier is as follows:

//ScoreNotifier.java import java.rmi.*;

import java.util.*;

public class ScoreNotifier implements Notifier, Runnable

{

Hashtable receivers = new Hashtable();

ScoreNotifier() {new Thread(this).start();}

public void registerMe(Receiver r) { try {

receivers.put(String.valueOf(receivers.size()), r);

System.out.println(“Registered a receiver”);

System.out.println(“No of registered receivers: “+receivers.size());

}catch(Exception e) {e.printStackTrace();}

}

public void run()

{

Random rand = new Random();

int score = 0, run;

while(true) {

do {

try {

Thread.sleep(1000+rand.nextInt(1000));

}catch(Exception e) {e.printStackTrace();}

}while((run = rand.nextInt(7)) == 0);

score += run;int no = receivers.size();

for(int i = 0;i<no;i++)                      {

Receiver r = (Receiver)receivers.get(String.valueOf(i));

System.out.println(“Sending : “+score);

new Sender(r, score).start();

}

}

}

private class Sender extends Thread

{

Receiver receiver;

int score;

Sender(Receiver r, int s) { this.receiver = r;

this.score = s;

}

public void run() {

try {

receiver.notify(String.valueOf(score));

}catch(Exception e) {e.printStackTrace();}

}

}

}

It generates an artificial score and notifies all the registered clients. The server program creates such a notifier as before. The result of such an application is shown in Figure 14.13:

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *