The Printer Interface Implementing the Basic Objects

comes with RMI. Its a small application with a minimal interface and some very curious restrictions. Among the most severe of those restrictions is this: launch code the code that binds servers into the registry has to be running on the same computer as the RMI registry. This restriction was implemented for security reasons. If there are no restrictions on what can be bound into the registry, then it becomes very easy for malicious code to subvert a registry. For example, a malicious application could use this weakness to replace the real servers with counterfeit ones. However, this causes fairly significant difficulties™ getting servers bound into the registry becomes somewhat convoluted. In most cases, well want to replace the registry with a more flexible naming service well talk about this in much greater detail in Chapt er 14 and Chapt er 15 . Until we do so, however, Ill present these examples with little thought to these restrictions.

4.3 Implementing the Basic Objects

Now lets start implementing the RMI-based printer server. As in the socket-based version, we have three basic objects: the Printer interface, the DocumentDescription object, and the PrinterException object. Conceptually, these objects are the same as their counterparts in Chapt er 3 . However, as might be expected, using RMI will force us to change a few details.

4.3.1 The Printer Interface

There are two basic changes to the Printer interface: it now extends the Remote interface, and every method is defined to throw RemoteException , a subclass of Exception defined in the package java.rmi . This class is shown in Exam ple 4- 1 . Example 4-1. Printer.java public interface Printer extends PrinterConstants, Remote { public boolean printerAvailable throws RemoteException; public boolean printDocumentDocumentDescription document throws RemoteException, PrinterException; } That Printer extends Remote shouldnt be a surprise™the whole point of the application is to turn a local printer into a server that can receive calls from clients applications running on other computers. The other change involves adding RemoteException to each method signature. RemoteException is an exception thrown by RMI to signal that something unforeseen has happened at the network level. That is, its a way for the RMI infrastructure to tell a client application that something went wrong in the RMI infrastructure. For example, if the server crashes while handling a clients request, RMI will automatically throw a RemoteException on the client side. Adding RemoteException to every method has one important consequence. Recall that rmic is used to automatically generate a stub class for each implementation of Printer . This stub implements the Printer interface and, therefore, every method implemented by the stub is declared to throw RemoteException . However, because RemoteException is a checked exception, any client-side code trying to invoke a method on the server must do so inside a try catch block and explicitly catch RemoteException . Making RemoteException a checked exception is one of the most controversial design decisions in the entire RMI framework. On one hand, it forces programmers writing client-side code to think about how to gracefully handle network failures. On the other hand, it is often the case that the catch block for a RemoteException doesnt do anything interesting. Moroever, forcing programmers to catch RemoteException in their client code merely winds up making the code much harder to read and understand. Notice that the printDocument method is still defined as throwing PrinterException . If the implementation of Printer throws a PrinterException , the RMI skeleton will automatically marshall the PrinterException object and send it across the wire to the stub. The stub will demarshall the instance of PrinterException and throw it again. At this point, the exception will be caught by the catch block. Whats happening here is simple: since RMI, via the stub and skeleton, controls the communication between the client and server, it can also automatically propagate exceptions across the network and rethrow them on the client side. Contrast this to the socket-based version, where Printer returned a status argument that the client was free to ignore.

4.3.2 Implementing a Printer