Previous | Next | Trail Map | RMI | Using Java RMI

Designing a Remote Interface

At the heart of the compute engine is a protocol that allows jobs to be submitted to the compute engine, the compute engine to run those jobs, and the results of the job to be returned to the client. This protocol is expressed in interfaces supported by the compute engine and by the objects that are submitted to the compute engine, as shown in the following figure.

Each of the interfaces contains a single method. The compute engine's interface, Compute, allows jobs to be submitted to the engine; the client interface, Task, defines how the compute engine executes a submitted task.

The compute.Compute interface defines the remotely accessible part--the compute engine itself. Here is the remote interface with its single method:

package compute;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
    Object executeTask(Task t) throws RemoteException;
}

By extending the interface java.rmi.Remote, this interface marks itself as one whose methods can be called from any virtual machine. Any object that implements this interface becomes a remote object.

As a member of a remote interface, the executeTask method is a remote method. Therefore the method must be defined as being capable of throwing a java.rmi.RemoteException. This exception is thrown by the RMI system during a remote method call to indicate that either a communication failure or a protocol error has occurred. A RemoteException is a checked exception, so any code making a call to a remote method needs to handle this exception by either catching it or declaring it in its throws clause.

The second interface needed for the compute engine defines the type Task. This type is used as the argument to the executeTask method in the Compute interface. The compute.Task interface defines the interface between the compute engine and the work that it needs to do, providing the way to start the work.

package compute;

import java.io.Serializable;

public interface Task extends Serializable {
    Object execute();
}

The Task interface defines a single method, execute, which returns an Object, has no parameters, and throws no exceptions. Since the interface does not extend Remote, the method in this interface doesn't need to list java.rmi.RemoteException in its throws clause.

The return value for the Compute's executeTask and Task's execute methods is declared to be of type Object. This means that any task that wants to return a value of one of the primitive types, such as an int or a float, needs to create an instance of the equivalent wrapper class for that type, such as an Integer or a Float, and return that object instead.

Note that the Task interface extends the java.io.Serializable interface. RMI uses the object serialization mechanism to transport objects by value between Java virtual machines. Implementing Serializable marks the class as being capable of conversion into a self-describing byte stream that can be used to reconstruct an exact copy of the serialized object when the object is read back from the stream.

Different kinds of tasks can be run by a Compute object as long as they are implementations of the Task type. The classes that implement this interface can contain any data needed for the computation of the task and any other methods needed for the computation.

Here is how RMI makes this simple compute engine possible. Since RMI can assume that the Task objects are written in the Java programming language, implementations of the Task object that were previously unknown to the compute engine are downloaded by RMI into the compute engine's virtual machine as needed. This allows clients of the compute engine to define new kinds of tasks to be run on the server machine without needing the code to be explicitly installed on that machine. In addition, because the executeTask method returns a java.lang.Object, any type of object can be passed as a return value in the remote call.

The compute engine, implemented by the ComputeEngine class, implements the Compute interface, allowing different tasks to be submitted to it by calls to its executeTask method. These tasks are run using the task's implementation of the execute method. The compute engine reports results to the caller through its return value: an Object.


Previous | Next | Trail Map | RMI | Using Java RMI