ince their introduction in 1997, Remote Method Invocation (RMI) and Enterprise JavaBeans (EJB) have represented a new direction in the development, installation, and management of distributed Java applications in the enterprise. The surprisingly powerful RMI ushered Java developers into the world of distributed object transactions, while the componential architecture of EJB greatly simplified the development and management of corporate applications.
Today, Java developers frequently ask whether using EJB is even necessary when they can easily do the same things using only RMIand vice versa in some cases. Certainly, you can use pure RMI to work with distributed objects in an architecture that also partially uses EJB, but whether you choose RMI or EJB depends almost completely on your needs and the required scalability of your application.
This article provides practical demonstrations for using RMI and EJB technologies, both individually and in tandem. It pays particular attention to the problems that arise when sharing EJB and RMI and discusses the scenarios where one is a better choice than the other. All the working methods include simple examples and diagrams that illustrate the working principles behind these technologies.
The basic objective of RMI is to allow programmers who develop distributed Java applications to use the same syntax and semantics that they use for non-distributed applications. To do this, the creators had to carefully investigate how Java classes and objects function on separate Java Virtual Machines (JVMs) and invent a new model for working with classes and objects accordingly. The new model would have to accommodate environments in which distributed object transactions occur (e.g., on several JVMs).
The architecture of RMI defines:
- How objects are conducted;
- How memory management works, in which place;
- Why exceptions exist; and
- Which parameters (arguments) are sent into remote methods, and how they return.
RMI technology architecture is based on one principle: the definition of behavior and the implementation of this behavior should be kept separate. RMI allows developers to separately store the behavior description and its implementation code on several, separately working JVMs. This concept is ideal for distributed systems where the client needs to know only the definition of a service used and the server directly provides this service (its implementation).
Initially, RMI uses Java interfaces as the definition of the remote service. Accordingly, the implementation of the service is coded in Java classes. So a key to understanding RMI is realizing that interfaces define behavior, while classes define implementation. (Figure 1 shows this concept of division.)
Remember that interfaces do not contain any executable code. RMI supports two classes that implement these interfaces. The first class directly implements behavior and works on the server side. The second plays the role of proxy for the remote service and works on the client side.
When the client program sends a call of a proxy object method, RMI sends the request to a remote JVM, which sends this request to the implementation code. Any returned values of a called method are sent back to the proxy object and then to the client.
RMI implementation consists of three abstract layers:
- Stub and Skeleton classes, which hide implementation from the developer. This layer intercepts the set interface's methods calls (which the client sends) and redirects them to their remote RMI service.
- Remote reference layer. This layer knows how to interpret and operate the references from the client to the remote service's objects.
- Transport layer. This layer works with TCP/IP connections between networked machines. It enables the creation of connections and provides some detour strategies for firewall systems.
This architecture allows developers to replace any of these three layers with a new layer that contains different behavior logic. For example, you can replace a transport layer with a layer that supports UDP/IP protocols. As a result, none of the top layers will be visible.
You may be asking, how does the client define a location for the RMI service (server)? Special name services and directories, including Java Naming and Directory Interface (JNDI), are used for this purpose. But RMI includes a similar, rather simple service called RMI Registry (
rmiregistry). RMI Registry must be started on each machine that acts as a storehouse of objects of the remote service (i.e., on a server) and accepts requests/connections to a specified port (the default port is 1099).
On the server side, before creating a remote service, the program first creates a local object, which is an implementation of this service. It then exports this object in RMI, and RMI creates a service that waits for the client's connections and requests. After export, the server registers this object in RMI Registry under a certain public name.
On the client side, the program addresses the RMI Registry with the help of the Naming static class. The program uses the
lookup() method of this class to receive a URL string, which defines the names of a server and a desired service. This method returns the remote reference to the service object. The URL usually looks like this:
HOST_NAME is the host name,
PORT tells the port number (e.g., 1099 by default) and is not necessarily an argument, and
SERVICE_NAME is the name of the service.