< Zurück | Inhalt | Weiter >

5.8.1 A Brief Introduction to RMI

Remote Method Invocation is a basic client-server model for invoking Java methods over a network.


5.8.1.1 History and Background

One of the most common problems in computing is how best to make an ap- plication available to the largest number of users at the least cost. To this end we have seen the development of “timeshare” systems with dumb terminals all over the place. We have seen the evolution of distributed GUI systems with X Windows and its network display system, and with tools like VNC (Virtual Network Console).16 We have seen the emergence of the PC, providing autonomous computing at each worker’s desktop. And finally we have seen the desktop turning slowly back into a terminal (albeit a prettier one) with the emergence of client-server computing.

What seems to have emerged from this progression is two major kinds of software systems. One is the PC and associated hardware and software. Devel- opments here have dramatically increased the power and productivity of indi- vidual work. The PC revolution was indeed a real change throughout the world of business and technology. But even with this, there are a host of applications and business functions that require a collection of data and resources to be available to multiple workers at the same time. This is the second major kind


image

16. http://www.realvnc.com/


of software systems. This second kind used to be the only kind, but now it may, in a sense, be the minority of applications, but the most critical to an operation. This second class of system has come to be called enterprise systems.

In enterprise computing, we have the same problem we opened with: How do you make the information and resources available to everyone who needs them at the lowest cost? And the answer is (as it always is) “that depends.”

These days, one of the most common solutions is to use a Web server to publish an application. This works well for a great many applications and it is much easier to do than many other methods. That explains its popularity. The Web interface is quite limited, however, so for more user interface intensive applications, client-server computing evolved. To us techies, all of this stuff is client-server. In this context however, client-server refers to a 2-tier system where the UI and logic exist in a GUI application on a PC and common resources are in an SQL database all the clients share.

This is also commonly used, but becomes expensive in a couple of cases. The first is when the database itself becomes a bottleneck because the number of users grows and grows but only one database can exist. The second is simply the cost of maintenance. Since the logic exists in the client, any change to the logic requires updating the software on all clients. Even when a scheme for au- tomating this exists, it is still time-consuming and costly to get all the changes out to all users simultaneously. There are workarounds for both of these issues, but here we are concerned with a different solution altogether.

So, how can we have a UI richer than with a Web application but avoid the pitfalls of the traditional 2-tier client-server computing? The answer is to separate the UI from the business logic and the business logic from the under- lying data store. This results in 3 tiers—presentation, business logic, and data.

Much of this book will concern itself with 3-tier computing solutions. Java has four major architectures for building 3-tier solutions. One of them is RMI.17


5.8.1.2 RMI Basics

RMI works by sharing an interface between the client and the server. The inter- face groups together the methods that a client may call on a server. A class is


image

17. The others are Enterprise JavaBeans, servlets, and JavaServer Pages. The latter two are Web- based, and therefore suffer from the UI deficiencies of Web forms, but Sun calls them part of Enterprise Java, so we will too.


written on the server side that implements the interface, and a special compiler is used to generate stubs for the server side and the client side. On the client side, a call to an RMI method looks like any other method call, but it is sent across the network to the server, where the actual instructions are carried out. Any return value is then passed back over the network to the client.

We will walk you through a very simple (and very pointless) example just to show you the tools.


5.8.1.3 Writing the Interface

Our interface is pathetically simple. It is a class that sums two integer arguments and returns an integer result. Example 5.10 shows the interface file.



NOTE

The names of the classes in the following examples may seem a bit strange, and they are. It is because we aim to build on this example later.

image


image

Example 5.10 The Session interface

package net.multitool.RMIDemo; import java.rmi.*;

public interface Session extends Remote {

public int add(int x, int y) throws RemoteException;

}


image


The two important things to note here are that the interface must extend java.rmi.Remote and that any remote method must be defined as throwing java.rmi.RemoteException. If anything goes wrong during an RMI call, like someone tripping over a network cable, the call will not complete success- fully and an exception of that type will be thrown. It is not possible to have a RMI method that cannot throw this exception.

Beyond those features, you can see that defining remote methods is quite familiar and easy.


5.8.1.4 Writing the Server Class

An interface is an “empty vessel.” Before any interface can be used, you must have an actual class that implements the interface. In an RMI application, the implementing class is the server (Example 5.11).

The class is named SessionImpl to emphasize its relationship with the Session interface. There is no requirement to match up such names. Likewise, the RMI name given, //penfold/Session, uses the interface name, but it could use any name. It is a good idea to develop a naming convention for RMI interfaces and their implementations. It is critical to develop naming conven- tions for RMI registry names, particularly in production environments. With- out a naming convention, it is difficult to avoid confusion and even chaos. What happens when multiple business units develop RMI code destined for a single production server, and they have all made an RMI interface named Session, or Payment?18 Bad things happen.

There is no “one size fits all” naming convention that we can offer. Possi- bilities include using package names in RMI registry names, using some element of the business area as a component of the name (such as AccountingSession, ShippingSession, ExecutiveSession). All that matters is that an unambigu- ous standard be created and followed.

Let’s spend some time talking about what this code does.

First, notice that the class extends UnicastRemoteObject. This is not necessary, but using that as a base class saves a lot of server setup. There are times when you would want to do such setup manually, but for our purpose here it saves a lot of effort. The class also implements our remote interface.

The first method is a constructor that calls the superclass constructor. At first glance, this is pointless. Any Java class gets a default constructor that just calls the superclass constructor, so why is this here? It is here because the super- class constructor throws RemoteException. If we didn’t define a constructor like the one here specifying that it throws RemoteException, the compiler would complain that there is an unhandled exception. So we define a construc- tor identical to a default constructor except that it specifies that it can throw the exception.



image

18. One solution is to use a more advanced naming system, such as LDAP. See Section 21.3.2.3.


image

Example 5.11 The Session server implementation

package net.multitool.RMIDemo;


import net.multitool.RMIDemo.*; import java.rmi.*;

import java.rmi.server.*;


/** SessionImpl is the server class for the Session RMI interface.

*/

public class SessionImpl

extends UnicastRemoteObject implements Session

{

/** Constructor needed to ensure call to UnicastRemoteObject

* constructor and to thus propagate the possible exception.

*/

public SessionImpl() throws RemoteException { super();

}


/** A static main() for the server. */ public static void main(String[] arglist)

{

if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager());

}


String rmiName = "//penfold/Session"; try {

Session adder = new SessionImpl(); Naming.rebind(rmiName, adder);

} catch (Exception e) { e.printStackTrace();

}

}


/** Implementation of the RMI method, add. */

public int add(int x, int y) throws java.rmi.RemoteException

{

return x+y;

}

}


image


Next, we have the server main() method. It first sets a security manager. The security manager controls what the VM is allowed to do. A number of default security managers are provided, and here we use one that is designed specifically to give safe and reasonable defaults for RMI applications. You can, of course, write your own security manager. Security managers use “policy specifications” to alter their capabilities. For now, we will explain enough to run a simple example. See Section 5.8.4.2 for more information on policies for our example.

Remember that main() is static, so there is no instance of SessionImpl yet, and thus also no instance of Session. We declare a variable of type Session, and set it to a new instance of SessionImpl. (There is no need to typecast here because SessionImpl implements Session, therefore SessionImpl is, among other things, a Session.) We now have an instance of the server class.

Next, the server must make itself available to the world. It does this by registering itself with the RMI registry (see Section 5.8.3). This is done through a static method of the java.rmi.Naming class, rebind(). Put simply, this maps a remote object to a string name in the registry. When clients contact the registry looking for a name then, if a remote object is mapped to that name, the communication can take place (yes, we are simplifying at the moment). The call to rebind() does not return. The server is up and running.

Finally, we have the implementation of our remote method, add().

This looks like a lot of hassle to go through, and it is, but consider writing an interface that offers, for example, methods like getDirContents(), chDir(), downloadFile(), uploadFile(). You’ve just written something like an FTP server. No matter how many methods you add to your interface, the complexity of the setup code does not increase. Maybe now it looks a little more useful?


5.8.1.5 Writing the Client Class

At this point, Example 5.12 should be fairly obvious. Our class has just a single static method, main(). It, like our server side main(), sets up a security man- ager. It then contacts a registry on the machine named penfold looking for an instance of a remote interface named Session (again, lookup() is a static method of the java.rmi.Naming class). We store that reference in a variable of type Session called sess. We can then call the add() on sess. We’ll show the server and client running shortly.


image

Example 5.12 The RMI client program

package net.multitool.RMIDemo; import java.rmi.*;

public class Client {

public static void main(String[] arglist) { if (System.getSecurityManager() == null) {

System.setSecurityManager(new RMISecurityManager());

}


try {

String name = "//penfold/Session";

// Obtain reference to the remote object Session sess = (Session) Naming.lookup(name);


System.out.println("Pointless RMI Client. 47 + 13 = " +

sess.add(47,13) + ", right?");

} catch (Exception e) { e.printStackTrace();

}

}

}


image