Java Gateway interface improved exception handling
authorBill Erickson <berick@esilibrary.com>
Fri, 16 Mar 2012 13:56:13 +0000 (09:56 -0400)
committerDan Scott <dscott@laurentian.ca>
Sun, 29 Apr 2012 18:22:44 +0000 (14:22 -0400)
Handle any exceptions that should not reasonably occur in normal
operation under the covers.  Bubble the rest up.  Update test code with
examples.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Dan Scott <dscott@laurentian.ca>
src/java/org/opensrf/net/http/GatewayRequest.java
src/java/org/opensrf/net/http/HttpConnection.java
src/java/org/opensrf/net/http/HttpRequest.java
src/java/org/opensrf/net/http/HttpRequestHandler.java
src/java/org/opensrf/test/TestGateway.java

index 00631bf..696d8d4 100644 (file)
@@ -28,74 +28,75 @@ public class GatewayRequest extends HttpRequest {
         readComplete = false;
     }
 
-    public GatewayRequest send() {
-        try {
-
-            String postData = compilePostData(service, method);
+    public GatewayRequest send() throws java.io.IOException {
+        String postData = compilePostData(service, method);
 
-            urlConn = (HttpURLConnection) httpConn.url.openConnection();
-            urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
-            urlConn.setDoInput(true);
-            urlConn.setDoOutput(true);
+        urlConn = (HttpURLConnection) httpConn.url.openConnection();
+        urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
+        urlConn.setDoInput(true);
+        urlConn.setDoOutput(true);
 
-            OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
-            wr.write(postData);
-            wr.flush();
-            wr.close();
-
-        } catch (java.io.IOException ex) {
-            failed = true;
-            failure = ex;
-        }
+        OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
+        wr.write(postData);
+        wr.flush();
+        wr.close();
 
         return this;
     }
 
-    public Object recv() {
+    public Object recv() throws java.io.IOException {
 
-        if (readComplete) 
-            return nextResponse();
+        if (!readComplete) {
+            readResponses();
+            readComplete = true;
+        }
 
-        try {
+        Object response = nextResponse();
+        if (response == null) 
+            complete = true;
+
+        return response;
+    }
+
+    /**
+     * Reads responses from network and populdates responseList. 
+     */
+    private void readResponses() throws java.io.IOException {
 
-            InputStream netStream = new BufferedInputStream(urlConn.getInputStream());
-            StringBuffer readBuf = new StringBuffer();
+        InputStream netStream = new BufferedInputStream(urlConn.getInputStream());
+        StringBuffer readBuf = new StringBuffer();
 
-            int bytesRead = 0;
-            byte[] buffer = new byte[1024];
+        int bytesRead = 0;
+        byte[] buffer = new byte[1024];
 
-            while ((bytesRead = netStream.read(buffer)) != -1) {
-                readBuf.append(new String(buffer, 0, bytesRead));
-            }
-            
-            netStream.close();
-            urlConn = null;
+        while ((bytesRead = netStream.read(buffer)) != -1) 
+            readBuf.append(new String(buffer, 0, bytesRead));
+        
+        netStream.close();
+        urlConn = null;
 
-            Map<String,?> result = null;
+        // parse the JSON response
+        Map<String,?> result = null;
 
-            try {
-                result = (Map<String, ?>) new JSONReader(readBuf.toString()).readObject();
-            } catch (org.opensrf.util.JSONException ex) {
-                ex.printStackTrace();
-                return null;
-            }
+        try {
+            result = (Map<String, ?>) new JSONReader(readBuf.toString()).readObject();
 
-            String status = result.get("status").toString(); 
-            if (!"200".equals(status)) {
-                failed = true;
-                // failure = <some new exception>
-            }
+        } catch (org.opensrf.util.JSONException ex) {
+            // if this happens, something is wrong
+            Logger.error("Gateway returned bad data " + ex.getStackTrace());
+            return;
+        }
 
-             // gateway always returns a wrapper array with the full results set
-             responseList = (List) result.get("payload"); 
+        // extract the gateway status value
+        String status = result.get("status").toString(); 
 
-        } catch (java.io.IOException ex) { 
-            failed = true;
-            failure = ex;
+        if (!"200".equals(status)) {
+            return;
+            // throw some new exception
         }
 
-        readComplete = true;
-        return nextResponse();
+        // gateway always returns a wrapper array with the full results set
+        responseList = (List) result.get("payload"); 
     }
 
     private String compilePostData(String service, Method method) {
@@ -116,10 +117,15 @@ public class GatewayRequest extends HttpRequest {
         }
 
         try {
+
             // not using URLEncoder because it replaces ' ' with '+'.
             uri = new URI("http", "", null, postData.toString(), null);
+
         } catch (java.net.URISyntaxException ex) {
-            ex.printStackTrace(); 
+
+            // if this happens, something is wrong
+            Logger.error("Error compiling POST data " + ex.getStackTrace());
+            return null;
         }
 
         return uri.getRawQuery();
index 32fdebc..c40b9f9 100644 (file)
@@ -29,12 +29,17 @@ public class HttpConnection {
         url = new URL(fullUrl);
     }
 
+    /** 
+     * Maximun number of threads allowed to communicate with the server.
+     */
     public int getMaxThreads() {
         return maxThreads;
     }
 
     /** 
-     * Set the maximum number of actively communicating threads allowed 
+     * Set the maximum number of actively communicating (async) threads allowed.
+     *
+     * This has no effect on synchronous communication.
      */
     public void setMaxThreads(int max) {
         maxThreads = max;
@@ -66,19 +71,22 @@ public class HttpConnection {
          //thread if necessary, then pass the result to the handler
         Runnable r = new Runnable() {
             public void run() {
-
                 Object response;
-                request.send();
 
-                while ((response = request.recv()) != null) {
-                    if (request.handler != null) 
+                try {
+                    request.send();
+                    while ((response = request.recv()) != null)
                         request.handler.onResponse(request, response);
-                }
 
-                if (request.handler != null)
                     request.handler.onComplete(request);
 
-                activeThreads--;
+                } catch (Exception ex) {
+                    request.handler.onError(request, ex);
+
+                } finally {
+                    // server communication has completed
+                    activeThreads--;
+                }
 
                 if (activeThreads < maxThreads) {
                     try {
index d91a127..8a4a39f 100644 (file)
@@ -8,23 +8,41 @@ import java.net.HttpURLConnection;
 
 public abstract class HttpRequest {
 
+    /** OpenSRF service. */
     protected String service;
+    /** OpenSRF method. */
     protected Method method;
+    /** Connection to server. */
     protected HttpURLConnection urlConn;
+    /** Connection manager. */
     protected HttpConnection httpConn;
-    protected HttpRequestHandler handler;
+
+    /** 
+     * List of responses.
+     *
+     * Responses are not kept after being passed to onResponse .
+     */
     protected List<Object> responseList;
-    protected Exception failure;
-    protected boolean failed;
+
+    /** True if all responses data has been read and delivered */
     protected boolean complete;
 
+    // use a no-op handler by default
+    protected HttpRequestHandler handler = new HttpRequestHandler() {};
+
     public HttpRequest() {
-        failed = false;
         complete = false;
         handler = null;
         urlConn = null;
     }
 
+    /**
+     * Constructor.
+     *
+     * @param conn Connection 
+     * @param service The OpenSRF service.
+     * @param method The method to send to the server.
+     */
     public HttpRequest(HttpConnection conn, String service, Method method) {
         this();
         this.httpConn = conn;
@@ -32,8 +50,14 @@ public abstract class HttpRequest {
         this.method = method;
     }
 
+    /**
+     * Send a request asynchronously (via threads).
+     *
+     * @param handler Manages responses
+     */
     public void sendAsync(final HttpRequestHandler handler) {
-        this.handler = handler;
+        if (handler != null) 
+            this.handler = handler;
         httpConn.manageAsyncRequest(this);
     }
 
@@ -48,19 +72,24 @@ public abstract class HttpRequest {
     }
     
     protected Object nextResponse() {
-        if (complete || failed) return null;
+        if (complete || responseList == null) 
+            return null;
         if (responseList.size() > 0)
             return responseList.remove(0);
         return null;
     }
 
-    public Exception getFailure() {
-        return failure;
-    }
-
-    public abstract HttpRequest send();
+    /**
+     * Send a request to the server.
+     */
+    public abstract GatewayRequest send() throws java.io.IOException;
 
-    public abstract Object recv();
+    /**
+     * Receive one response from the server.
+     *
+     * @return The response object
+     */
+    public abstract Object recv() throws java.io.IOException;
 }
 
 
index 9c0f9e5..c30a4f6 100644 (file)
@@ -1,5 +1,6 @@
 package org.opensrf.net.http;
 
+import org.opensrf.util.*;
 import java.util.List;
 
 /*
@@ -9,8 +10,6 @@ public abstract class HttpRequestHandler {
 
     /**
      * Called when all responses have been received.
-     *
-     * If discardResponses() returns true, will be passed null.
      */
     public void onComplete(HttpRequest request) {
     }
@@ -22,4 +21,8 @@ public abstract class HttpRequestHandler {
      */
     public void onResponse(HttpRequest request, Object response) {
     }
+
+    public void onError(HttpRequest request, Exception ex) {
+        Logger.error("HttpRequest Error: " + ex.getStackTrace());
+    }
 }
index 967e929..1ac6d90 100644 (file)
@@ -10,47 +10,67 @@ import java.util.Arrays;
 
 public class TestGateway {
     
-    public static void main(String[] args) throws java.net.MalformedURLException {
+    public static void main(String[] args) {
 
         if (args.length == 0) {
             System.err.println("Please provide a gateway URL: e.g. http://example.org/osrf-gateway-v1");
             return;
         }
 
-        // configure the connection
-        HttpConnection conn = new HttpConnection(args[0]);
+        try {
 
-        Method method = new Method("opensrf.system.echo");
-        method.addParam("Hello, Gateway");
-        method.addParam(new Integer(12345));
-        method.addParam(new Boolean(true));
-        method.addParam(Arrays.asList(8,6,7,5,3,0,9));
+            // configure the connection
+            HttpConnection conn = new HttpConnection(args[0]);
 
-        // sync test
-        HttpRequest req = new GatewayRequest(conn, "opensrf.math", method).send();
-        Object resp;
-        while ( (resp = req.recv()) != null) {
-            System.out.println("Sync Response: " + resp);
-        }
+            Method method = new Method("opensrf.system.echo");
+            method.addParam("Hello, Gateway");
+            method.addParam(new Integer(12345));
+            method.addParam(new Boolean(true));
+            method.addParam(Arrays.asList(8,6,7,5,3,0,9));
 
-        // exceptions are captured instead of thrown, 
-        // primarily to better support async requests
-        if (req.failed()) {
-            req.getFailure().printStackTrace();
-            return;
-        }
+            // sync test
+            HttpRequest req = new GatewayRequest(conn, "opensrf.math", method).send();
+            Object resp;
+            while ( (resp = req.recv()) != null) {
+                System.out.println("Sync Response: " + resp);
+            }
+
+            // async test
+            for (int i = 0; i < 10; i++) {
+                final int ii = i; // required for nested class
+                HttpRequest req2 = new GatewayRequest(conn, "opensrf.math", method);
+
+                req2.sendAsync(
+                    new HttpRequestHandler() {
 
-        // async test
-        for (int i = 0; i < 10; i++) {
-            final int ii = i; // required for nested class
-            HttpRequest req2 = new GatewayRequest(conn, "opensrf.math", method);
-            req2.sendAsync(
-                new HttpRequestHandler() {
-                    public void onResponse(HttpRequest req, Object resp) {
-                        System.out.println("Async Response: " + ii + " : " + resp);
+                        // called once per response
+                        public void onResponse(HttpRequest req, Object resp) {
+                            System.out.println("Async Response: " + ii + " : " + resp);
+                        }
+
+                        // called after all responses have been received
+                        // used primarily when you don't know how many responses will be returned
+                        public void onComplete(HttpRequest req) {
+                            System.out.println("Async Request complete : " + ii);
+                        }
+
+                        // ruh-roh
+                        public void onError(HttpRequest req, Exception ex) {
+                            if (ex instanceof java.io.IOException) 
+                                System.err.println("Trouble communicating with gateway server!");
+                            ex.printStackTrace();
+                        }
                     }
-                }
-            );
+                );
+            }
+
+        } catch (java.net.MalformedURLException ex) {
+            System.err.println("Malformed Gateway URL! " + args[0]);
+            ex.printStackTrace();
+
+        } catch (java.io.IOException ex) {
+            System.err.println("Trouble communicating with gateway server!");
+            ex.printStackTrace();
         }
     }
 }