Implement the client-side interface

public interface CallbackPrinter extends PrinterConstants, Remote { public void printDocumentCallbackClient clientMakingRequest, DocumentDescription document throws RemoteException, PrinterException; public void printDocument DocumentDescription document throws RemoteException, PrinterException; for when we dont need a callback } public interface CallbackClient extends Remote{ public void documentIsDone throws RemoteException; public void documentFailedToPrintString reason throws RemoteException; } The second approach is to use a single instance of CallbackClient on the client side, which receives all the documentIsDone messages. In this case, an extra piece of information, which uniquely identifies the printRequest , also needs to be passed back and forth. One way to do this is via the following interfaces: public interface CallbackPrinter extends PrinterConstants, Remote { public void printDocumentCallbackClient clientMakingRequest , DocumentDescription document, String documentKey throws RemoteException, PrinterException; public void printDocument DocumentDescription document throws RemoteException, PrinterException; for when we dont need a callback } public interface CallbackClient extends Remote{ public void documentIsDoneString documentKey throws RemoteException; public void documentFailedToPrintString documentKey, String reason throws RemoteException; } In this second set of interfaces, the client is responsible for generating a unique-to-the-client documentKey . In both of these approaches, the same general flow of control occurs: 1. The client sends a print request to the server and gets back an immediate response. 2. After the document is done printing, the server calls either documentIsDone or documentFailedToPrint on the client.

21.2.2.5 Implement the client-side interface

We will implement the first version of CallbackClient , the one without documentKey . We need to do two things: actually implement CallbackClient and then adapt our outside frame to use it correctly. Heres an implementation of CallbackClient . As in our previous examples, all it does is display a dialog box with the outcome of the print request: public class CallbackClient_DialogImpl extends UnicastRemoteObject implements CallbackClient { private static final String SUCCESS_WINDOW_TITLE = Success; private static final String FAILURE_WINDOW_TITLE = Failure; private String _documentName; public CallbackClient_DialogImplString documentName throws RemoteException { _documentName = documentName; } public void documentIsDone throws RemoteException { reportResultOfPrintRequestSUCCESS_WINDOW_TITLE, _documentName + is done printing.; ceaseBeingAServer ; } public void documentFailedToPrintString reason throws RemoteException { reportResultOfPrintRequestFAILURE_WINDOW_TITLE, _documentName + failed to print because + reason; } private void reportResultOfPrintRequestString windowTitle, String message { SwingUtilities.invokeLaternew SendMessageToUserwindowTitle, message; } private void ceaseBeingAServer { try { unexportObjectthis, true; } catch NoSuchObjectException e { Not much to do. The RMI runtime thinks were not a server. } } private class SendMessageToUser implements Runnable { private String _windowTitle; private String _message; public SendMessageToUserString windowTitle, String message { _windowTitle = windowTitle; _message = message; } public void run { JOptionPane.showMessageDialognull, _windowTitle, _message, JOptionPane.INFORMATION_MESSAGE; } } } As you can see, this is a one-shot RMI server. CallbackClient_DialogImpl extends UnicastRemoteObject and, therefore, instances of CallbackClient_DialogImpl can receive remote method invocations. However, they never get registered in any naming service, and once an instance of CallbackClient_DialogImpl receives a remote method invocation, it immediately unexports itself. There are three convenient aspects to this. First, the client application doesnt need to create any thread objects; the RMI runtime handles thread management issues for it. Second, most of the reported back code is encapsulated in a single class. We can easily change the method of reporting by simply using a different implementation of CallbackClient . Third, the rest of the client application doesnt need to keep a reference to instances of CallbackClient_DialogImpl at all. That is, an instance of CallbackClient_DialogImpl has the following lifecycle: 1. It is created. 2. It waits for a message. While it is waiting, it is exported, and the server has a reference to it. Therefore, the instance will not be garbage collected. 3. It receives a message. At this point, it unexports itself and is no longer a server. The RMI runtime no longer has any references to the instance of CallbackClient_DialogImpl . This means that, as long as we dont keep any references to the instance of CallbackClient_DialogImpl within the rest of the client application, it will automatically become eligible for garbage collection as soon as it receives a message from the server. With that in mind, heres the new implementation of the Print File buttons action listener: private class PrintFile implements ActionListener { private CallbackPrinter _printer; public void actionPerformedActionEvent event { try { File fileToPrint = _fileChooser.getSelectedFile ; FileInputStream documentStream = new FileInputStreamfileToPrint; DocumentDescription documentDescription = new DocumentDescriptiondocumentStream; _printer = CallbackPrinter Naming.lookupDEFAULT_PRINTER_NAME; CallbackClient callbackClient = new CallbackClient_DialogImplfileToPrint.getName ; _printer.printDocumentcallbackClient, documentDescription; } catch Exception exception { SwingUtilities.invokeLaternew ExceptionMessageexception; return; } } } private class ExceptionMessage implements Runnable { private Exception _exception; public ExceptionMessageException exception { _exception = exception; } public void run { JOptionPane.showMessageDialogCallbackClientFrame.this , Print failed , Error in printing , JOptionPane.INFORMATION_ MESSAGE; _messageBox.setTextException attempting to print + _fileChooser.getSelectedFile.getAbsolutePath + \n\t Error was: + _exception.toString ; _exception.printStackTrace ; } }

21.3 Handling Report-Type Methods