Java and RMI—Architecture of an RMI system—Syntax of RMI—First principles—Baby's first words—Exercises
This chapter provides a high-level introduction to Remote Method Invocation (RMI) in Java. It outlines the architecture of an RMI system—from a simple configuration to a more advanced system such as RMI over Internet Inter-ORB Protocol (IIOP). As well as briefly stating the basic principles of RMI, this chapter also provides an RMI version of a simple echo service.
In 1995, Sun Microsystems introduced the Java programming language. Since then it has been enthusiastically adopted by software developers looking for ways to build secure, platform-independent applications for distributed network environments like the World Wide Web. Along with the advantages of its portability between platforms, Java is an attractively simple language to learn, combining many of the runtime advantages of Smalltalk and the programming concepts of Modula-3 with the familiar syntax of C++. Developers can also benefit from its remarkably complete runtime library, its designed-in security model, and its price—to most developers Java is free.
Increasingly complex computing environments require increasingly powerful programming techniques like object-oriented programming. The Internet enables the deployment of enormous distributed applications. Java is an object-oriented language that allows developers to produce distributed client-server applications. Distributed applications depend on the communication between “objects” residing in different locations.
RMI adds to Java the power and flexibility of remote procedure calls (RPC), in a way which preserves the “object-oriented” nature of Java. It provides a framework within which Java objects in distinct Java virtual machines (JVMs) can interact. RMI is built on top of Java's object and class facilities, its object serialization protocol, and its TCP/IP networking support.
The following high-level diagrams illustrate the components that are involved in:
a simple RMI system
an advanced RMI system
an RMI over IIOP system.
These examples illustrate the architecture that you will be working with when designing and building an RMI system. You can find out more about each component in the subsequent chapters of the book.
Figure 1.1 illustrates the RMI system at its simplest: a remote interface, a client, and one or more servers—remote objects—residing on a host. The client invokes methods on remote objects via the remote interface. A naming service such as the RMI registry resides on the host, to provide the mechanism the client uses to find one or more initial RMI servers. You can find out more about the RMI registry in Chapter 6.
Figure 1.2 illustrates a more advanced RMI system, including RMI Activation—a system which allows servers to be activated on demand. The RMI Activation system is discussed in detail in Chapter 10.
This diagram also illustrates that a web server providing an RMI codebase service may also be present in an RMI system. A codebase is a global location for Java class files and Java Archive (JAR) files, accessible to RMI clients and servers. You can find out more about using codebases in Chapter 9.
Figure 1.3 illustrates RMI over IIOP.In RMI/IIOP, the COS Naming service, accessed via the Java Naming and Directory Interface (JNDI), is used instead of the RMI registry. In RMI over IIOP you cannot use RMI Activation, which is only supported under the Java remote method protocol (JRMP). Find out more about these topics in Chapter 13 and Chapter 14.
RMI reduces the complexities of distributed computing—such as locating the server, network connections, data transfer, synchronization, and propagating errors—to a simple method call and exception handler in the client, as shown in Example 1.1.
Remote method invocation is the invocation of a method in a remote object.
A remote object is an object whose remote methods can be invoked—via a remote interface—from another Java virtual machine.[1] A remote object has all the usual properties of a Java object: it has state and methods; it can refer to other objects. It even has implementations of Object.clone
, Object.equals
, Object.hashCode
, and Object.toString
, with behaviour that is reasonable for a remote object.
A remote method is a method defined in a remote interface; it is invoked via that interface.
A remote interface is a Java interface which extends java.rmi.Remote
. Its methods must all throw RemoteException
.
Because Java interfaces cannot specify static methods, it follows that a remote method cannot be static.
Any object, even a local one, can be thought of as a server; its users are its clients. A local object is essentially a local server; a remote object is a remote server.
Remote methods in a remote object can be invoked via RMI even if the object is in fact in the same JVM. Obviously in this case they could also be invoked directly, i.e. via local method invocation.
To be accessible via RMI, a remote object must:
implement a remote interface
be exported to the RMI system.
An object is exported to the RMI system implicitly on construction, or explicitly by an exportObject
method (to be discussed in a later chapter).
A remote object is accessed via a remote stub. A remote stub is an object which implements the same remote interface(s) as the remote object it refers to. Its class is generated from the corresponding remote object by the RMI system at compile-time.
A remote stub can only be obtained as the result of another remote method invocation.[2] The client uses the remote stub as an instance of the remote interface implemented by the remote object. The remote stub is not itself the remote object; nor is it an instance of the remote class. A remote stub is really a proxy for the remote object.
For the record, a remote stub also has all the usual properties of a Java object: state, methods, and external references, although only the methods (not the state or external references) are of interest to the RMI client. It also has reasonable implementations of
Object.clone
,Object.equals
,Object.hashCode
, andObject.toString
.
Consider a simple echo service as shown in Example 1.2.
Example 1.2. Simple echo service and client
class EchoServer { public Object echo(Object object) { return object;} } class EchoClient { public static void main(String[] args) throws Exception { EchoServer echo = new EchoServer(); System.out.println(echo.echo("O che bon eccho")); } }
We can improve on this, by decoupling the server from the client—specifying an intermediate interface—and by using a ServerFactory
for creating server objects. Such a version might look like Example 1.3.
Example 1.3. Decoupled echo service with factory
public interface Echo { Object echo(Object object); } class EchoServer implements Echo { public Object echo(Object object) { return object;} } class EchoFactory { public static Echo getEcho() { return new EchoServer();} } class EchoClient { public static void main(String[] args) throws Exception { Echo echo = EchoFactory.getEcho(); System.out.println(echo.echo("o che bon eccho")); } }
An RMI version of the echo service is only a slight modification of Example 1.3, as shown in Example 1.4.
Example 1.4. RMI Echo interface, service, factory, and client
// imports not shown public interface RemoteEcho extends Remote { Object echo(Object object) throws RemoteException; } class RemoteEchoServer extends UnicastRemoteObject implements RemoteEcho { public RemoteEchoServer() throws RemoteException {} public Object echo(Object object) throws RemoteException { return object;} public static void main(String[] args) throws Exception { RemoteEchoServer server = new RemoteEchoServer(); Naming.rebind(RemoteEcho.class.getName(),server); } } public class RemoteEchoFactory { public static RemoteEcho getEcho() throws Exception { return (RemoteEcho)Naming.lookup (RemoteEcho.class.getName()); } } class RemoteEchoClient { public static void main(String[] args) throws Exception { RemoteEcho echo = RemoteEchoFactory.getEcho(); System.out.println(echo.echo("o che bon eccho")); } }
In the RMI version, we made the following changes:
we imported the RMI packages
the Echo
interface now extends java.rmi.Remot
e, and its methods now throw RemoteException
the EchoServer
class now extends java.rmi.server.UnicastRemoteObject
the EchoServer
class now has a main procedure so it can execute in its own JVM
the EchoFactory
now obtains its Echo
object via the RMI registry instead of directly instantiating an EchoServer
object
various exceptions can be thrown; we have shown this by just declaring the main
methods to throw Exception
, for brevity, but in practice you will want extensive try ... catch
exception handling.
That's it!
The original version and both client/server versions can only execute in a single Java Virtual Machine (JVM). The RMI version can execute in a single JVM, in two JVMs on the same computer, or in two JVMs in two machines connected by a network.
[1] RMI specification, § 2.2.
[2] Normally. Obviously this statement implies an infinite regress. Where do we get the initial remote stub? It is specially constructed for the RMI registry, which is a bootstrapping mechanism provided in order to break out of the regress. See the separate chapter on the registry. It is also possible to acquire remote stubs via serialized MarshalledObjects.
3.139.70.101