ServerNetworkWrapper Network-Aware Wrapper Objects

IOException { InputStream inputStream = connection.getInputStream ; DataInputStream dataInputStream= new DataInputStreaminputStream; BufferedReader reader = new BufferedReadernew InputStreamReaderinputStream; boolean response = dataInputStream.readBoolean ; if response { return; } throw new PrinterExceptioninputStream; } The points to note about this method are: • It is a straightforward procedural wrapper around the protocol; a connection is made, an instance of DocumentDescription is sent, a return value is read from the sockets input stream, and a PrinterException may be thrown based on the return value. Once youve defined the protocol, writing ClientNetworkWrapper is straightforward. • Once again, the information that has already been read is used to help interpret the remainder of the stream. Since the communication is entirely via a stream of bytes, the server cant simply throw an exception and expect it to be propagated to the client application you cant throw an exception through a socket connection. But the server can send a boolean that the client interprets as a signal that there is more information available. The client knows that if there is more information sent, it is a marshalled instance of PrinterException and behaves accordingly. Were cheating a bit on exceptions. In particular, what if there were more than one type of exception that could be thrown? Instead of just passing a boolean back from the server, wed need to pass more metadata. For example, we could pass back a boolean indicating that an exception has been thrown, followed by a string containing the name of the exception class, followed by the actual instance data. The client would receive the boolean and create an instance of the class by getting the class object with Class.forName and then using the reflection API to find the appropriate constructor.

3.3.2.2 ServerNetworkWrapper

We need to implement a companion object on the server side. This class, Server - NetworkWrapper , has to listen for connections using an instance of ServerSocket and then implement the mirror image of the client-side protocol. In other words, when the client side is sending information, the server should be listening. And when the client side is listening, the server should be sending. The details of managing the connection are contained in the accept method: public void accept { while true { Socket clientSocket = null; try { clientSocket = _serverSocket.accept ; blocking call processPrintRequestclientSocket; } catch IOException e {e.printStackTrace ;} closeSocketclientSocket; } } The protocol itself is implemented in the processPrintRequest method. As on the client side, this method is a straightforward implementation of the protocol™it reads an instance of DocumentDescription from the stream, tries to print the document, and encodes the printers response: private void processPrintRequestSocket clientSocket { InputStream clientRequestStream; OutputStream clientResponseStream; DataOutputStream dataOutputStream; DocumentDescription documentToPrint; try { clientRequestStream = clientSocket.getInputStream ; clientResponseStream = clientSocket.getOutputStream ; dataOutputStream = new DataOutputStreamclientResponseStream; documentToPrint = new DocumentDescriptionclientRequestStream; } catch IOException e { e.printStackTrace ; return; } try { try { _printer.printDocumentdocumentToPrint; dataOutputStream.writeBooleantrue; } catch PrinterException printerError { dataOutputStream.writeBooleanfalse; printerError.writeToStreamdataOutputStream; } } catch IOException ee {ee.printStackTrace ;} }

3.4 The Application Itself

Once weve written the data objects and the objects that encapsulate the network protocol, writing the rest of the application is easy. The server doesnt even need a user interface; it consists of the main function, which instantiates a printer, creates an instance of ServerNetworkWrapper , and then calls accept on the instance of ServerNetworkWrapper : public static void mainString args[] {