From b4c8d84b00e23084a0ea47c0d9b00ac291efb047 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 16 Mar 2012 09:56:13 -0400 Subject: [PATCH] Java Gateway interface improved exception handling 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 Signed-off-by: Dan Scott --- .../org/opensrf/net/http/GatewayRequest.java | 108 +++++++++--------- .../org/opensrf/net/http/HttpConnection.java | 24 ++-- .../org/opensrf/net/http/HttpRequest.java | 53 +++++++-- .../opensrf/net/http/HttpRequestHandler.java | 7 +- src/java/org/opensrf/test/TestGateway.java | 80 ++++++++----- 5 files changed, 169 insertions(+), 103 deletions(-) diff --git a/src/java/org/opensrf/net/http/GatewayRequest.java b/src/java/org/opensrf/net/http/GatewayRequest.java index 00631bf..696d8d4 100644 --- a/src/java/org/opensrf/net/http/GatewayRequest.java +++ b/src/java/org/opensrf/net/http/GatewayRequest.java @@ -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 result = null; + // parse the JSON response + Map result = null; - try { - result = (Map) new JSONReader(readBuf.toString()).readObject(); - } catch (org.opensrf.util.JSONException ex) { - ex.printStackTrace(); - return null; - } + try { + result = (Map) new JSONReader(readBuf.toString()).readObject(); - String status = result.get("status").toString(); - if (!"200".equals(status)) { - failed = true; - // failure = - } + } 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(); diff --git a/src/java/org/opensrf/net/http/HttpConnection.java b/src/java/org/opensrf/net/http/HttpConnection.java index 32fdebc..c40b9f9 100644 --- a/src/java/org/opensrf/net/http/HttpConnection.java +++ b/src/java/org/opensrf/net/http/HttpConnection.java @@ -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 { diff --git a/src/java/org/opensrf/net/http/HttpRequest.java b/src/java/org/opensrf/net/http/HttpRequest.java index d91a127..8a4a39f 100644 --- a/src/java/org/opensrf/net/http/HttpRequest.java +++ b/src/java/org/opensrf/net/http/HttpRequest.java @@ -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 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; } diff --git a/src/java/org/opensrf/net/http/HttpRequestHandler.java b/src/java/org/opensrf/net/http/HttpRequestHandler.java index 9c0f9e5..c30a4f6 100644 --- a/src/java/org/opensrf/net/http/HttpRequestHandler.java +++ b/src/java/org/opensrf/net/http/HttpRequestHandler.java @@ -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()); + } } diff --git a/src/java/org/opensrf/test/TestGateway.java b/src/java/org/opensrf/test/TestGateway.java index 967e929..1ac6d90 100644 --- a/src/java/org/opensrf/test/TestGateway.java +++ b/src/java/org/opensrf/test/TestGateway.java @@ -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(); } } } -- 2.43.2