[TOC] [Prev] [Next]

# Java Distributed Object Model

### Topics:

- Definition of Terms
- The Distributed and Nondistributed Models Contrasted
- RMI Interfaces and Classes
- Implementing a Remote Interface
- Type Equivalency of Remote Objects with Local Stub
- Parameter Passing in Remote Method Invocation
- Exception Handling in Remote Method Invocation
- Object Methods Overridden by the RemoteObject Class
- The Semantics of Object Methods Declared final
- Locating Remote Objects

# Definition of Terms

In the Java distributed object model, a remote object is one whose methods can be invoked from another Java Virtual Machine, potentially on a different host. An object of this type is described by one or more remote interfaces, which are Java interfaces that declare the methods of the remote object.

Remote method invocation (RMI) is the action of invoking a method of a remote interface on a remote object. Most importantly, a method invocation on a remote object has the same syntax as a method invocation on a local object.

# The Distributed and Nondistributed Models Contrasted

The Java distributed object model is similar to the Java object model in the following ways:

- A reference to a remote object can be passed as an argument or returned as a result in any method invocation (local or remote).
- A remote object can be cast to any of the set of remote interfaces supported by the implementation using the built-in Java syntax for casting.
- The built-in Java `instanceof` operator can be used to test the remote interfaces supported by a remote object.

The Java distributed object model differs from the Java object model in these ways:

- Clients of remote objects interact with remote interfaces, never with the implementation classes of those interfaces.
- Nonremote arguments to, and results from, a remote method invocation are passed by copy rather than by reference. This is because references to objects are only useful within a single virtual machine.
- A remote object is passed by reference, not by copying the actual remote implementation.
- The semantics of some of the methods defined by class `Object` are specialized for remote objects.
- Since the failure modes of invoking remote objects are inherently more complicated than the failure modes of invoking local objects, clients must deal with additional exceptions that can occur during a remote method invocation.

# RMI Interfaces and Classes

The interfaces and classes that are responsible for specifying the remote behavior of the RMI system are defined in the `java.rmi` and the `java.rmi.server` packages. The following figure shows the relationship between these interfaces and classes:

## The Remote Interface

All remote interfaces extend, either directly or indirectly, the interface `java.rmi.remote`. The `Remote` interface defines no methods, as shown here:

    public interface Remote {}

For example, the following code fragment defines a remote interface for a bank account that contains methods that deposit to the account, get the account balance, and withdraw from the account:

    public interface BankAccount
           extends Remote
    {
    	public void deposit (float amount)
    		throws java.rmi.RemoteException;
    	public void withdraw (float amount)
    		throws OverdrawnException, java.rmi.RemoteException;
    	public float balance()
    		throws java.rmi.RemoteException;
    }

The methods in a remote interface must be defined as follows:

- Each method must declare `java.rmi.RemoteException` in its throws clause, in addition to any application-specific exceptions.
- A remote object passed as an argument or return value (either directly or embedded within a local object) must be declared as the remote interface, not the implementation class.

## The RemoteException Class

The `java.rmi.RemoteException` class is the superclass of all exceptions that can be thrown by the RMI runtime. To ensure the robustness of applications using the RMI system, each method declared in a remote interface must specify `java.rmi.RemoteException` in its throws clause.

`java.rmi.RemoteException` is thrown when a remote method invocation fails (for example when the network fails or the server for the call cannot be reached). This allows the application making the remote invocation to determine how best to deal with the remote exception.

## The RemoteObject Class and its Subclasses

RMI server functions are provided by `java.rmi.server.RemoteObject` and its subclasses, `java.rmi.server.RemoteServer` and `java.rmi.server.UnicastRemoteObject`:

- The `java.rmi.server.RemoteObject` class provides the remote semantics of `Object` by implementing methods for `hashCode`, `equals`, and `toString`.
- The functions needed to create objects and export them (make them available remotely) are provided abstractly by `java.rmi.server.RemoteServer` and concretely by its subclass(es). The subclass identifies the semantics of the remote reference, for example whether the server is a single object or is a replicated object requiring communications with multiple locations.
- The `java.rmi.server.UnicastRemoteObject` class defines a singleton (unicast) remote object whose references are valid only while the server process is alive.

# Implementing a Remote Interface

The general rules for a class that implements a remote interface are as follows:

- The class usually extends `java.rmi.server.UnicastRemoteObject`, thereby inheriting the remote behavior provided by the classes `java.rmi.server.RemoteObject` and `java.rmi.server.RemoteServer`.
- The class can implement any number of remote interfaces.
- The class can extend another remote implementation class.
- The class can define methods that do not appear in the remote interface, but those methods can only be used locally and are not available remotely.

For example, the following code fragment defines the `BankAcctImpl` class, which implements the `BankAccount` remote interface and which extends the `java.rmi.server.UnicastRemoteObject` class:

    package my_package;

    import java.rmi.RemoteException;
    import java.rmi.server.UnicastRemoteObject;

    public class BankAccountImpl
    	extends UnicastRemoteObject
    	implements BankAccount
    {
    	public void deposit (float amount) throws RemoteException {
    		...
    	}
    	public void withdraw (float amount) throws OverdrawnException,
    		RemoteException {
    		...
    	}
    	public float balance() throws RemoteException {
    		...
    	}
    }

Note that if necessary, a class that implements a remote interface can extend some other class besides `java.rmi.server.UnicastRemoteObject`. However, the implementation class must then assume the responsibility for the correct remote semantics of the `hashCode`, `equals`, and `toString` methods inherited from the `Object` class.

# Type Equivalency of Remote Objects with Local Stub

In the distributed object model, clients interact with stub (surrogate) objects that have exactly the same set of remote interfaces defined by the remote object's class; the stub class does not include the nonremote portions of the class hierarchy that constitutes the object's type graph. This is because the stub class is generated from the most refined implementation class that implements one or more remote interfaces. For example, if C extends B and B extends A, but only B implements a remote interface, then a stub is generated from B, not C.

Because the stub implements the same set of remote interfaces as the remote object's class, the stub has, from the point of view of the Java system, the same type as the remote portions of the server object's type graph. A client, therefore, can make use of the built-in Java operations to check a remote object's type and to cast from one remote interface to another.

Stubs are generated using the rmic compiler.

# Parameter Passing in Remote Method Invocation

An argument to, or a return value from, a remote object can be any Java type that is serializable. This includes Java primitive types, remote Java objects, and nonremote Java objects that implement the `java.io.Serializable` interface. For more details on how to make classes serializable, see the Java "Object Serialization Specification." For applets, if the class of an argument or return value is not available locally, it is loaded dynamically via the AppletClassLoader. For applications, these classes are loaded by the class loader that loaded the application; this is either the default class loader (which uses the local class path) or the RMIClassLoader (which uses the server's codebase).

Some classes may disallow their being passed (by not being serializable), for example for security reasons. In this case the remote method invocation will fail with an exception.

## Passing Nonremote Objects

A nonremote object, that is passed as a parameter of a remote method invocation or returned as a result of a remote method invocation, is passed by copy.

That is, when a nonremote object appears in a remote method invocation, the content of the nonremote object is copied before invoking the call on the remote object. By default, only the nonstatic and nontransient fields are copied.

Similarly, when a nonremote object is returned from a remote method invocation, a new object is created in the calling virtual machine.

## Passing Remote Objects

When passing a remote object as a parameter, the stub for the remote object is passed. A remote object passed as a parameter can only implement remote interfaces.

# Exception Handling in Remote Method Invocation

Since remote methods include `java.rmi.RemoteException` in their signature, the caller must be prepared to handle those exceptions in addition to other application specific exceptions. When a `java.rmi.RemoteException` is thrown during a remote method invocation, the client may have little or no information on the outcome of the call - whether a failure happened before, during, or after the call completed. Therefore, remote interfaces and the calling methods declared in those interfaces should be designed with these failure semantics in mind.

# Object Methods Overridden by the RemoteObject Class

The default implementations in the `Object` class for the `equals`, `hashCode`, and `toString` methods are not appropriate for remote objects. Therefore, the `java.rmi.server.RemoteObject` class provides implementations for these methods that have semantics more appropriate for remote objects. In this way, all objects that need to be available remotely can extend `java.rmi.server.RemoteObject` (typically indirectly via `java.rmi.server.UnicastRemoteObject`).

## equals and hashCode

In order for a remote object to be used as a key in a hash table, the methods `equals` and `hashCode` are overridden by the `java.rmi.server.RemoteObject` class:

- The `java.rmi.server.RemoteObject` class's implementation of the `equals` method determines whether two object references are equal, not whether the contents of the two objects are equal. This is because determining equality based on content requires a remote method invocation, and the signature of `equals` does not allow a remote exception to be thrown.
- The `java.rmi.server.RemoteObject` class's implementation of the `hashCode` method returns the same value for all remote references that refer to the same underlying remote object (because references to the same object are considered equal).

## toString

The `toString` method is defined to return a string which represents the reference of the object. The contents of the string is specific to the reference type. The current implementation for singleton (unicast) objects includes information about the object specific to the transport layer (such as host name and port number) and an object identifier; references to replicated objects would contain more information.

## clone

Objects are only cloneable using the Java language's default mechanism if they support the `java.lang.Cloneable` interface. Remote objects do not implement this interface, but do implement the `clone` method so that if subclasses need to implement `Cloneable` the remote classes will function correctly.

Client stubs are declared final and do not implement `clone`. Cloning a stub is therefore a local operation and cannot be used by clients to create a new remote object.

## finalize

Remote object implementations (subclasses of `RemoteObject`) can use `finalize` to perform their own cleanup as necessary. For example, `finalize` can be used to deactivate an object server.

# The Semantics of Object Methods Declared final

The following methods are declared `final` by the `Object` class and cannot be overridden:

- `getClass`
- `notify`
- `notifyAll`
- `wait`

The default implementation for `getClass` is appropriate for all Java objects, local or remote; the method needs no special implementation for remote objects. When used on a remote object, the `getClass` method reports the exact type of the generated stub object. Note that this type reflects only the remote interfaces implemented by the object, not its local interfaces.

The `wait` and `notify` methods of Object deal with waiting and notification in the context of the Java language's threading model. While use of these methods for remote objects does not break the Java threading model, these methods do not have the same semantics as they do for local Java objects. Specifically, using these methods operates on the client's local reference to the remote object (the stub), not the actual object at the remote site.

# Locating Remote Objects

A simple bootstrap name server is provided for storing named references to remote objects. A remote object reference can be stored using the URL-based methods of the class `java.rmi.Naming`.

For a client to invoke a method on a remote object, that client must first obtain a reference to the object. A reference to a remote object is usually obtained as a return value in a method call. The RMI system provides a simple bootstrap name server from which to obtain remote objects on given hosts. The `java.rmi.Naming` class provides Uniform Resource Locator (URL) based methods to look up, bind, rebind, unbind, and list the name-object pairings maintained on a particular host and port.

Here's an example, (without exception handling) of how to bind and look up remote objects:

    BankAccount acct = new BankAcctImpl();
    String url = "rmi://java.Sun.COM/account";
    // bind url to remote object
    java.rmi.Naming.bind(url, acct);
    	...
    // lookup account
    acct = (BankAccount)java.rmi.Naming.lookup(url);

[TOC] [Prev] [Next]

Copyright © 1996, 1997 Sun Microsystems, Inc. All rights reserved.