DocumentDescription The Data Objects

} } This is exactly what a generic exception [ 5] should be; it has enough state for the catch block to print out or display a meaningful error message. You can easily imagine a client popping up a dialog box to tell the user what went wrong, as in the following code snippet: [ 5] This is a generic exception because it covers a wid e range of devices. Since its impossible to define all the different types of exceptions a printer can generate and create subclasses of PrinterException for each one, we simply rely on the user to interpret the exception. java.sql.SQLException follows a similar design strategy. catch PrinterException printerException { String errorMessage = Print failed after + printerException getNumberOfPagesPrinted + pages.; JOptionPane.showMessageDialogSimpleClientFrame.this, errorMessage, Error in printing , JOptionPane.INFORMATION_MESSAGE; _messageBox.setTextException attempting to print + _fileChooser getSelectedFile .getAbsolutePath + \n\t Error was: + printerException getHumanReadableErr orDescription ; } Even more impressively, PrinterException has no extra networking code. For example, it does not contain any code that either reads or writes from a stream. This is possible because RMI automatically uses serialization to send objects over the wire.

4.3.3.1 DocumentDescription

The other object we pass by value is an instance of DocumentDescription . However, we have a problem here: DocumentDescription stores the document as an instance of InputStream , and InputStream doesnt implement the Serializable interface. This means that the generic serialization mechanism wont work with DocumentDescription . Were going to have to write custom marshalling and demarshalling code ourselves. The code is shown in Exam ple 4- 4 . Example 4-4. DocumentDescription.java public class DocumentDescription implements Serializable, PrinterConstants { private transient InputStream _actualDocument; private int _length; private int _documentType; private boolean _printTwoSided; private int _printQuality; public DocumentDescription { zero arg constructor needed for serialization } .... private void writeObjectjava.io.ObjectOutputStream out throws IOException { out.defaultWriteObject ; copy_actualDocument, out; } private void readObjectjava.io.ObjectInputStream in throws IOException, ClassNotFoundException { in.defaultReadObject ; ByteArrayOutputStream temporaryBuffer = new ByteArrayOutputStream ; copyin, temporaryBuffer, _length; _actualDocument = new DataInputStreamnew ByteArrayInputStreamtemporaryBuffer toByteArray ; } We start by declaring _actualDocument to be transient . transient is a Java keyword that tells the serialization mechanism not to serialize the variables value out. We then implement writeObject , which does two things: • Calls out.defaultWriteObject . This invokes the generic serialization mechanism which is the default to write out all nontransient objects. That is, when out.defaultWriteObject is called, everything but _actualDocument has been encoded in the stream. • Copies _actualDocument to the stream, exactly as we did for the socket-based version of the program. Similarly, in readObject , we first call defaultReadObject , which retrieves all the nontransient values, including _length , from the stream. We then read _actualDocument from the stream. Why doesnt InputStream implement the Serializable interface? The answer is that InputStream is an abstract base class whose concrete subclasses often have machine - specific state. For example, File - InputStream explicitly refers to a file on a hard drive and probably has a file descriptor as part of its state. Making objects such as FileInputStream serializable makes very little sense, since you cant guarantee that either the file or the file descriptor will be available or meaningful when the information is deserialized. Similarly, classes such as Frame or Thread , which encapsulate operating-system resources, are not serializable.

4.4 The Rest of the Server

To finish building our server, we need to write launch code. Launch code is code that is application-specific, but not business-domain specific, and handles the details of registering a server with a naming service such as the RMI registry. In our case, this boils down to two pieces of code: a Java program that runs PrinterServer and a batch file that starts the RMI registry and then runs our program. The former is shown in Exam ple 4- 5 . Example 4-5. SimpleServer.java public class SimpleServer implements NetworkConstants { public static void mainString args[] { try { File logfile = new FileC:\\temp\\serverLogfile;