Project Scenario for OpX
High-level Overview In the Test GUI Controller, opXController, for the opX command there is conceptually a call to an appropriate Class, C, of the form OpXResponseInfo opXResponse = C.opX(OpXRequest); Where OpXRequst is an instance of OpXRequestInfo. Then we show the response in the response window with IView.setResponse(opXResponse.toString());
Things to Understand First, things to do in the OpXController of the GUI is to get ready to make the call The class, C, is not on the same machine as the GUI. So, how do we call the operation across the internet? – We use a client and a server We call the opX command on the client communicator, CC. OpXResponseInfo opXResponse = CC.opX(opXRequest); The client communicator encodes the operation and its parameters (opXRequest) and sends them to the server. The server gets the operation and parameters (bundled in the object opXRequest) and decodes them. The server then calls C.opX(opXRequest) and gets a response, opXResponse, of type OpXResponseInfo
Getting Ready to make the call Get the parameters from the GUI – Use IView.getParameters() Use them to create an instance of OpXRequestInfo, opXRequest – I have a constructor whose single parameter is the output of IView.getParameters() OpXRequestInfo opXRequest = new OpXRequestInfo(Iview.getParameters) Get a string representation of the opXRequest – String stringRepresentation = opXRequest.toString(); Use IView.setRequst(stringRepresentation) to display the string in the GUI
Calling the opX operation across the Internet We use a client and a server – In the opXController we call the opX command in the client communicator, CC. OpXResponseInfo opXResponse = CC.OpX(OpXRequest); – The client communicator encodes the operation and its parameters (opXRequest) and sends them to the server. – The server gets the operation and parameters (bundled in the object opXRequest) and decodes them. – The server then makes the call we wanted to make in the first place OpXResponseInfo opXResponse = C.opX(opXRequest) – The server then encodes the response and sends it back to the client – The client communicator then decodes the response (opXResponse) as the return value for the method call made in the opXController.
The Client Communicator Design There exists one method for every operation opX. – OpXRequestInfo opX(OpXRequestInfo opXRequest) Each of the methods for the 7 commands are all most identical. We will take all of the common code and put it in its own method which we will call “doPost” If done well, the OpXResponseInfo opX(OpXRequestInfo) method becomes a one liner: return (OpXResponseInfo) doPost(opXName, opXRequest)
The “doPost” method First, set up the connection Encode the opXRequest and send it Handle any problems (the connection failed, the response is not OK, or the response is empty). Decode the response as an object and send it to the opX method that called “doPost”
Setting up the Connection Create a string representation of the address of the server – + opXName opXName is an input parameter to doPost Create a URL URL url = new URL(the string you just created) Create HttpURLConnection HttpURLConnection connection = (HttpURLConnection)url.open(); The HttpURLConnection class is in the Java library
Setting up the Connection (Continued) Set the request method type connection.setRequestMethod(“POST”); Indicate there will be information in the body of the request connection.setDoOutput(true); Now connect connection.connect();
Encoding and Sending using XStream Encode and store in the body of the request xStream.toXML(opXRequst, connection.getOutputStream()) – The opXRequest is a parameter to doPost – The xStream is an instance of XStream Send the request to the server connection.getOutputStream.close();
Handling the Response Get the response code connection.getResponseCode() Get the response length connection.getResponseLength() If everything is correct – Decode the response and store it in an object Object object = xStream.fromXML(connection.getInputStream) Return the object to the opX method that called doPost (the calling method then casts the object to the appropriate type)
Handling Errors Use a try/catch block Throw errors for – Response code not equal to HttpURLConnection.HTTP_OK – contentLength not equal to 0 – IOException Thrown when trying to connection or communicate
The Server Perspective Create a Server class containing a HttpServer we will call httpServer. When you set up the server – Initialize anything that you need For example, initialize the datebase – You may need to use the DataImporter to fill the Database – Create the HttpServer HttpServer httpServer = HttpServer.create(new InetSocketAddress(8080), 10) – Set the Executor httpServer.setExecutor(null) – Create the contexts for each command. For the opX command it might be httpServer.createContext(“/opX”, opXHandler); – There are 7 such commands – The name of the operation is the one appended to the end of the url string in the client » There is one context (“/”, fileTransfer) for the 8 th command. – The opXHandler is described on the next page – Start the server server.start()
Server Handlers Each of the handlers is an instance of a subClass of the library class HttpHandler abstract public HttpHandler { public HttpHandler(){} abstract void handle(HttpExchange exchange); } Create an HttpHandler for every operation opX. This is done in the Server class – Do this by subclassing HttpHandler, overloading the handle method, and creating an instance. This is best done using an anonymous inner class. – Assign it to a variable name of your choice which is the name used when creating a context (see previous slide) – Example private HttpHandler opXHandler = new HttpHandler() { public void handle(HttpExchange exchange) throws IOException { //see next slide }
The Body of every Handler Retrieve and decode the request OpXRequestInfo opXRequestInfo = (OpXRequestInfo)xStream.fromXML(exchange.getRequestBody()) The xStream is a variable in the Server class The exchange is the input parameter in the handle method getRequestBody() actually gets an InputStream Execute the operation on the appropriate class OpXResponseInfo opXResponse = C.opX(opXRequestInfo) Encode the response and send it back to the client communicator – First, set the headers Exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, 0); – Endcode and write the response to the response body xStream.toXML(opXResponse, exchange.getResponseBody()) getResponseBody actually returns an OutputStream – Send the response exchange.getResponseBody.close();
Handling Errors on the Server Side Wrap everything in try/catch block – Catch IOExceptions when creating the server – Catch any exceptions thrown when the operation in the model class is called If there is an error send and error response back exchange.sendResponseHeaders( HttpURLConnection.HTTP_INTERNAL_ERROR, -1) – You don’t have to write anything to the response body (using xStream.toXML) nor do you perform exchange.getResponseBody().close()
The opX operations in the Server This is up to you I use a single Class called Controller that has a method for each of the 8 commands Most of the logic should go here – Including error detection and reporting The database access classes should primarily by “getters” and “setters” – The may be of a higher level and thus not match one for one with sql commands on a table.
The 8 th Command FileDownload Command Differences – We don’t really use the “/opX” method of identifying the corresponding handler for file downloading You might consider httpServer.createContext(“/”, fileDownloader); – The fileDownloader doesn’t encode the response. It opens up the output file (an OutputStream) of the exchange It also opens the source file as an InputStream. It then copies the contents of the source file into the output stream of the exchange When finished, it closes the OutputStream.