After the earlier Back to Basics: JDBC Revisited, here's a quick look at RMI, the enabling technology behind today's Enterprise Java and what makes it tick.
Intro and BasicsRMI
forms the basis for Java's own remoting capabilities powering the
platform's domination on servers everywhere. This article attempts to
see through all the frameworks, programming models and technology built
on top of it, and tries to get to the lower level basics of RMI. RMI
remoting transparent by making remote objects look like local objects.
The only difference from a user of a remote service perspective would
be how the object itself is obtained. RMI is based on three abstraction
- Stub / Skeleton layer
- Remote Reference layer
- Transport layer
Stubs and Skeletons
Stubs and skeletons are special objects that make remoting with RMI possible. When the client requests for a remote object, it in-fact gets a stub to the object and not the object itself. The stub masks the complexity of invoking a method remotely and poses as a local object to the client. To make this process of remote method invocation transparent to the client, the programming model mandates the creation of an interface that extends java.RMI.Remote which forms the contract between the client and the server. This interface defines the methods that would be available for remote invocation, in other words, the methods a remote object wants to expose to the world. The client is only aware of this interface and not the concrete classes that implement it. Therefore when the client receives the stub, which implements the contractual remote interface, it does not know the difference between the actual implementation and the stub. When a method is invoked on the stub, it cleverly handles all the networking details, servicing the client just like any method call.
The method call and the parameters are intercepted by the stub, marshalled (something like serialized, well look at this a bit later) across to the skeleton, a server side object which un-marshalls (de-serializes) the method call and parameters and makes the call to the actual object. The return value from the actual method invocation is intercepted by the skeleton and sent back to the Stub, who in turn returns it to the client. The stub is a proxy on the client side and the skeleton is a helper class that acts as liaison or connector between the Stub and the actual object. The Stub passes the data to the remote reference layer, which hands it to the skeleton. Note that even though we discuss skeletons here, from Java 2 onwards, skeletons are no longer used or recommended.
is very much like serialization and if fact, its a smart abstraction
that uses serialization under the hood. What makes marshalling and
serialization different is that while serialization is a general way to
pack and transport objects, Marshalling is RMI sensitive; If the object
to be serialized is an instance of java.RMI.Remote, or simply a remote
object, the actual instance is not serialized but instead its stub is
serialized and sent across the wire to the client. While Serialization
is pass-by-value for objects, marshalling can be thought of as
pass-by-reference for remote objects.
Remote Reference Layer
The remote reference layer defines the semantics for remote object references. It connects and governs the communication between the the stub and the skeleton. Its is JRMP specific and defines classes like RemoteRef which are used by the stub to get data across to the skeleton. From Java 2 on, remote reference layer adds semantics for Activatable objects. Another example of the semantics defined and managed by this layer is Multi-cast , in which the method is invoked on several remote implementations and he first response to return is used.
The Transport Layer
The transport layer is responsible for the low level connection between the two JVMs, and management of these connections. RMI defines a wire level protocol called JRMP, JRMP is on TCP/IP and also provides some firewall penetration strategies. JRMP was updated in Java 2, to eliminate skeletons and to use reflection instead.
Looking up Remote objectsA
client can use either JNDI or the more simpler RMI registry to look-up
the remote objects. We will not discuss JNDI here. The RMI registry
itself is a remote object., and implements the interface
java.RMI.registry.Registry. RMI registries can be started as a separate
process or in a programmatic way, by an object itself, and by default
runs on the port 1099. Starting a registry means exporting the remote
object that implements the Registry interface( which means the actual
registry itself is a remote object). Once a remote object is bound to a
registry under a public name, a client can look-up the registry for a
remote object based on the name with which it was bound. Here's what
- The client tries to get a registry, which gets him a Stub that acts as the client proxy to the registry, [This step is usually transparent because JDK classes do the job of getting the registry]
- The client makes a "look-up" on the registry with the pubic name with which the remote object was bound
- A Stub to the remote object is returned to the client.
- The client can now invoke methods on the remote object just as if it were a local object. The client does not know the difference between the stub and the actual object, as the stub, even though generated by the developer, would implement of the remote interface containing the methods the client would expect.