The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search

Trail: Putting It All Together
Lesson: BINGO!

Handling Player Requests

During its life, the Player application needs to make three different kinds of requests of the Game application.
  1. When the Player application first starts up, it needs to ask the Game for status.
  2. To join the next game, the Player must send a request to the Game.
  3. When the player detects a BINGO, the Player must notify the Game.
The nature of these requests dictates the following: All of this leads us to Remote Method Invocation, a feature of the JDK that allows code in one application to invoke the methods of an object resident in another application. The object whose methods are invoked is known as a remote object. The methods invoked are called remote methods.

In BINGO, the Game application contains the remote object (which we call the "Registrar") and the Player calls its remote methods. This satisfies the above requirements in that the Player initiates the communication (by invoking a method), the Game can send a reply (via the method's return value), and the channel is reliable and point-to-point (because RMI is implemented using TCP).

The next section talks about the code that glues the remote methods on the Game side to the remote method calls on the Player side. The two sections following that describe the RMI code in the Game and the RMI code in the Player, respectively.

The Glue

The Game's remote methods that can be called from the Player are codified in the bingo.shared.Registrar interface shown here:
package bingo.shared;

import java.rmi.*;

public interface Registrar extends Remote {

    public String whatsHappening()
        throws RemoteException;

    public Ticket mayIPlay(String playerName,
                           int numCards, long seed)
        throws RemoteException;

    public Answer BINGO(int playerID, Card c)
        throws RemoteException;
}
As you can see, the interface contains one method per request that can be made by the Player. Each method has a different type of return value that contains the Game's response.


This figure has been reduced to fit on the page.
Click the image to view it at its natural size.

The method declarations for remote methods look just like method declarations for "regular" methods: they have return values, arguments lists, and so on. However, these method declarations are different in that they contain a throws RemoteException clause at the end. This identifies them as remote methods. All remote methods may throw a RemoteException and so, must have this throws clause.

While not apparent from the code in the Registrar interface, the arguments and the return value of the remote methods must be Serializable. That is, the types of the arguments and return value of a remote method must either be primitive types, or if a reference type, then the class [PENDING: check about interfaces] or interface must implement the Serializable interface.

The whatsHappening method returns a String which like many of the classes in the java.* packages are Serializable. The mayIPlay method returns an bingo.game.Ticket and the Answer method returns an bingo.game.Answer. Upon inspection of these two classes you will notice each class declaration contains the following clause thereby satisfying the requirement:

implements Serializable
For information about object serialization, see Object Serialization(in the Learning the Java Language trail).

Now let's look at the code in the Game that supports RMI.

The Game Side of RMI

The bingo.game.RegistrarImpl class implements the Registrar interface and consequently the Game's response to each of the Player's requests. This class implements the Game's remote object and its remote methods.

Here are the relevant lines of code from RegistrarImpl:

class RegistrarImpl extends UnicastRemoteObject
                    implements Registrar, Constants
{

    RegistrarImpl (RingMaster ringMaster)
        throws RemoteException
    {
	. . .
    }

    public String whatsHappening()
        throws RemoteException
    {
	. . .
    }

    public Ticket mayIPlay(String playerName,
                                   int numCards, long seed)
        throws RemoteException
    {
	. . .
    }

    public Answer BINGO(int playerID, Card c)
        throws RemoteException
    {
	. . .
    }

}
You'll note that the constructor for the RegistrarImpl class throws a RemoteException because the creation of any remote object may result in a RemoteException.

The Game's main method, which appears in the BINGO class, creates an instance of RegistrarImpl and registers it for RMI with this code:

System.setSecurityManager(new RMISecurityManager());

// within a try clause

. . .

    ringMaster = new RingMaster();

    RegistrarImpl registrar = new RegistrarImpl(ringMaster);
    hostname = InetAddress.getLocalHost().getHostName();
    Naming.rebind("//" + hostname + "/Registrar", registrar);

. . .
The RegistrarImpl object is registered as a remote object under the name of //hostname/Registrar (where hostname is the name of the machine on which the Game program is running).

The Player Side of RMI

To invoke methods on the RegistrarImpl object that was instantiated in the Game application, the Player needs two pieces of information:
  1. The interface that defines the remote object's remote methods (RegistrarImpl). From the Player's perspective, this interface is the data type of the remote object.
  2. The name under which the remote object registered for RMI. Remember that this includes the hostname on which the Game is running. By default, the Player uses the hostname on which the Player is running. To play with a Game on another host, the user must type a different hostname into the textfield provided in the Player's GUI.
With these two pieces of information, the Player can get an object reference to the RegistrarImpl object that was instantiated in the Game. Here's the relevant code from the Player class that does this:
Registrar registrar;
. . .
registrar = (Registrar)Naming.lookup("//" + host + "/Registrar");
The code declares a member variable, registrar, whose type is Registrar--the interface that defines all of the Game's remote methods. Next, the code looks up the remote object by name and assigns it to the registrar variable.

After these two lines of code have been successfully executed, registrar is an object reference to the RegistrarImpl object in the Game application. Through the registrar reference the Player can invoke methods just like with any other object reference--the difference is that the object referred to resides in a separate VM. The Player invokes methods on registrar on three separate occasions:

  1. When the Player application first starts up, it queries the Game for the game status with this line of code:
    statusText = registrar.whatsHappening();
    
  2. To join the next game, the Player calls mayIPlay:
    ticket = registrar.mayIPlay(nameField.getText(),
                                numCards, seed);
    
  3. When the player detects a BINGO, the player notifies the Game by calling the BINGO method:
    Answer a = registrar.BINGO(ticket.ID, event.getCard());
    
This has been a description of how the Game and the Player applications use RMI to communicate. This has not been a discussion of RMI in general. For general RMI documentation see [PENDING].

Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search