Section 13.3. Defining Remote Objects


13.3. Defining Remote Objects

Now that you have a basic idea of how Java RMI works, we can explore the details of creating and using distributed objects with RMI in more detail. As mentioned earlier, defining a remote RMI object involves specifying a remote interface for the object, then providing a class that implements this interface. The remote interface and implementation class are then used by RMI to generate a client stub and server skeleton for your remote object. The communication between local objects and remote objects is handled using these client stubs and server skeletons. The relationships among stubs, skeletons, and the objects that use them are shown in Figure 13-2.

Figure 13-2. Relationships among remote object, stub, and skeleton classes


When a client gets a reference to a remote object (details on how this reference is obtained come later) and then calls methods on this object reference, there needs to be a way for the method request to get transmitted back to the actual object on the remote server and for the results of the method call to get transmitted back to the client. This is what the generated stub and skeleton classes are for. They act as the communication link between the client and your exported remote object, making it seem to the client that the object actually exists within its Java VM.

Stubs and skeletons are generated from the classes that implement the remote RMI interfaces that you've exported. The process of generating these stub and skeleton classes is done either dynamically at runtime (in JDK 1.5 or later JVMs) or by using the RMI compiler . Whether you use the RMI compiler or use dynamic stub and skeleton generation, the role of these classes in the RMI runtime are the same. The stub and skeleton classes act as go-betweens for the client application and the actual server object, respectively. The client stub class implements the remote interface that you defined, and its implementations of each remote method package up (marshal) the method arguments provided by the client and transmit them to the server. The skeleton class is responsible for receiving the method arguments from the RMI runtime after they've been transmitted over the network. It unpackages (unmarshals) the method arguments and makes the corresponding method call on the actual remote object implementation on the server. Whatever response the method call generates (return data or an exception), the results are packaged and transmitted back to the remote client by the skeleton and the RMI runtime working together. The client stub method (which is still executing at this point) unpackages the results and delivers them to the client as the result of its remote method call.

As we saw earlier in the chapter, the first step in creating your remote objects is to define the remote interfaces for the types of objects you need to use in a distributed object context. This isn't much different from defining the public interfaces in a nondistributed application, with the following exceptions:

  • Every object you want to distribute using RMI has to directly or indirectly extend an interface that extends the java.rmi.Remote interface.

  • Every method in the remote interface has to declare that it throws a java.rmi.RemoteException or one of the parent classes of RemoteException.[*]

    [*] Note that prior to Java 1.2, the RMI specification required that every method on a remote interface had to throw RemoteException specifically. In Java 1.2 and later, this restriction was loosened to allow any superclass of RemoteException. One reason for this change is to make it easier to define generic interfaces that support both local and remote objects.

RMI imposes the first requirement to allow it to differentiate quickly between objects that are enabled for remote distribution and those that aren't. As we've already seen, during a remote method invocation, the RMI runtime system needs to be able to determine whether each argument to the remote method is a Remote object or not. The Remote interface, which is simply a tag interface that marks remote objects, makes it easy to perform this check.

The second requirement is needed to deal with errors that can happen during a remote session. When a client makes a method call on a remote object, any number of errors can occur, preventing the remote method call from completing. These include client-side errors (e.g., an argument can't be marshaled), errors during the transport of data between client and server (e.g., the network connection is dropped), and errors on the server side (e.g., the method throws a local exception that needs to be sent back to the remote caller). The RemoteException class is used by RMI as a base exception class for any of the different types of problems that might occur during a remote method call. Any method you declare in a Remote interface is assumed to be remotely callable, so every method has to declare that it might throw a RemoteException or one of its parent interfaces.

In Example 13-1 we saw the Account interface, a remote interface that declares six remote methods: getName( ), getBalance( ), withdraw( ), deposit( ) and TRansfer( ). Since we want to use this interface in an RMI setting, we've declared that the interface extends the Remote interface. In addition, each method has arguments and return values that are either Remote or Serializable, and each method is declared as throwing a RemoteException.

With the remote interface defined, the next thing we need to do is write a class that implements the interface. We saw the implementation of the Account interface, the AccountImpl class, in Example 13-2. This class has implementations of the six remote methods defined in the Account interface; it also has a nonremote method, checkTransfer( ), to verify that a funds transfer between two accounts can be made. Notice that the checkTransfer( ) method doesn't have to be declared as throwing a RemoteException, since it isn't a remotely callable method. Only the methods that participate in the remote method protocol of the RMI runtime need to declare that they can throw RemoteException or one of its parent exceptions. The methods in your implementation class that participate in the RMI runtime include implementations of any methods declared in the remote interface (withdraw( ), transfer( ), etc. in our example) and, if you're extending UnicastRemoteObject, any constructors on your implementation class. Any other methods you define in your implementation class (like checkTransfer( )) are considered nonremote (i.e., they are callable only from within the local Java virtual machine where the object exists).

Constructors are required to have RemoteException in their throws clause because an RMI implementation class that extends UnicastRemoteObject is "exported" and made accessible to remote method invocations when it is constructed. The RemoteException in the throws clause of the constructor is required to cover any problems that may occur during the RMI export process. All of the constructors on UnicastRemoteObject throw RemoteException because they all call the static UnicastRemoteObject.exportObject( ) methods internally, so the Java compiler will force you to include RemoteException in the throws clause of your implementation class.

13.3.1. Key RMI Classes for Remote Object Implementations

As you have probably noticed, our AccountImpl class also extends the UnicastRemoteObject class. This is a class in the java.rmi.server package that extends java.rmi.server.RemoteServer, which itself extends java.rmi.server.RemoteObject, the base class for all RMI remote objects. Four key classes are related to writing server object implementations:


RemoteObject

RemoteObject implements both the Remote and Serializable interfaces. Although the RemoteObject class is in the java.rmi.server package, it is used by both the client and server portions of a remote object reference. Both client stubs and server implementations are subclassed (directly or indirectly) from RemoteObject. A RemoteObject contains the remote reference for a particular remote object.

RemoteObject is an abstract class that reimplements the equals( ), hashCode( ), and toString( ) methods inherited from Object in a way that makes sense and is practical for remote objects. The equals( ) method, for example, is implemented to return true if the internal remote references of the two RemoteObject objects are equal (i.e., if they both point to the same server object).


RemoteServer

RemoteServer is an abstract class that extends RemoteObject. It defines a set of static methods that are useful for implementing server objects in RMI, and it acts as a base class for classes that define various semantics for remote objects. In principle, a remote object can behave according to a simple point-to-point reference scheme; it can have replicated copies of itself scattered across the network that need to be kept synchronized or any number of other scenarios. JDK 1.1 supported only point-to-point, nonpersistent remote references with the UnicastRemoteObject class. JDK 1.2 introduced the RMI activation system, so current versions of the core Java API include another subclass of RemoteServer: Activatable.


UnicastRemoteObject

This is a concrete subclass of RemoteServer that implements point-to-point remote references over TCP/IP networks. These references are nonpersistent: remote references to a server object are valid only during the lifetime of the server object. Before the server object is created (inside a virtual machine running on the host) or after the object has been destroyed, a client can't obtain remote references to the object. In addition, if the virtual machine containing the object exits (intentionally or otherwise), any existing remote references on clients become invalid and generate RemoteException objects if used.


Activatable

This concrete subclass of RemoteServer is part of the RMI object activation facility and can be found in the java.rmi.activation package. It implements a server object that supports persistent remote references. If a remote method request is received on the server host for an Activatable object and the target object is not executing at the time, the object can be started automatically by the RMI activation daemon.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net