From b366d94a7168f486c5e410768cc49689e4c96c96 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Wed, 9 Nov 2016 18:19:11 -0500 Subject: [PATCH] Native Messaging WIP -- file IO, etc. Signed-off-by: Bill Erickson --- run.sh | 17 ++- src/org/evergreen_ils/hatch/FileIO.java | 3 +- src/org/evergreen_ils/hatch/Hatch.java | 105 +------------------ src/org/evergreen_ils/hatch/PrintManager.java | 12 ++- src/org/evergreen_ils/hatch/RequestHandler.java | 132 +++++++++++++++++++++--- src/org/evergreen_ils/hatch/TestHatch.java | 59 ++++++++++- 6 files changed, 202 insertions(+), 126 deletions(-) diff --git a/run.sh b/run.sh index 11292c7038..e6d64e122a 100755 --- a/run.sh +++ b/run.sh @@ -1,10 +1,23 @@ JAVA_HOME=jdk1.8 +JAVA=$JAVA_HOME/bin/java CP=lib:lib/json-20160810.jar +#LOGS="-Djava.util.logging.SimpleFormatter.format='%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n'" +#LOGS=-Djava.util.logging.SimpleFormatter.format='%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %2$s %5$s%6$s%n' +LOGS=-Djava.util.logging.SimpleFormatter.format='%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$-6s %5$s%6$s%n' # compile $JAVA_HOME/bin/javac -Xlint:unchecked -cp $CP -d lib src/org/evergreen_ils/hatch/*.java [ -z "$1" ] && exit; -# run -$JAVA_HOME/bin/java -cp $CP org.evergreen_ils.hatch.Hatch +if [ "$1" == "1" ]; then + + # run + $JAVA "$LOGS" -cp $CP org.evergreen_ils.hatch.Hatch + +else + + $JAVA "$LOGS" -cp $CP org.evergreen_ils.hatch.TestHatch \ + | $JAVA "$LOGS" -cp $CP org.evergreen_ils.hatch.Hatch \ + | $JAVA "$LOGS" -cp $CP org.evergreen_ils.hatch.TestHatch receive +fi; diff --git a/src/org/evergreen_ils/hatch/FileIO.java b/src/org/evergreen_ils/hatch/FileIO.java index 8813b7c1eb..5d857f0866 100644 --- a/src/org/evergreen_ils/hatch/FileIO.java +++ b/src/org/evergreen_ils/hatch/FileIO.java @@ -18,6 +18,7 @@ package org.evergreen_ils.hatch; import java.io.*; import java.util.LinkedList; import java.util.Arrays; +import java.util.logging.Logger; public class FileIO { @@ -58,7 +59,7 @@ public class FileIO { // -------------------------------------------------- // logger - //private static final Logger logger = Log.getLogger("FileIO"); + private static final Logger logger = Hatch.getLogger(); /** * Constructs a new FileIO with the provided base path. diff --git a/src/org/evergreen_ils/hatch/Hatch.java b/src/org/evergreen_ils/hatch/Hatch.java index eff340e03a..12b32c05cd 100644 --- a/src/org/evergreen_ils/hatch/Hatch.java +++ b/src/org/evergreen_ils/hatch/Hatch.java @@ -164,116 +164,13 @@ public class Hatch extends Application { return logger; } - /* - public void onMessage(String message) { - if (session == null || !session.isOpen()) return; - logger.info("onMessage() " + message); - - HashMap params = null; - - try { - params = (HashMap) JSON.parse(message); - } catch (ClassCastException e) { - reply("Invalid WebSockets JSON message " + message, - new Long(-1), false); - } - - Long msgid = (Long) params.get("msgid"); - String action = (String) params.get("action"); - String key = (String) params.get("key"); - String value = (String) params.get("value"); - String mime = (String) params.get("mime"); - - logger.info("Received request for action " + action); - - // all requets require a message ID - if (msgid == null) { - reply("No msgid specified in request", msgid, false); - return; - } - - // all requests require an action - if (action == null || action.equals("")) { - reply("No action specified in request", msgid, false); - return; - } - - Object response = null; - boolean error = false; - FileIO io = new FileIO(profileDirectory, origin); - - switch (action) { - case "keys": - response = io.keys(key); - break; - - case "printers": - response = new PrintManager().getPrintersAsMaps(); - break; - - case "print": - // pass ourselves off to the print handler so it can reply - // for us after printing has completed. - params.put("socket", this); - Hatch.enqueueMessage(params); - - // we don't want to return a response below, since the - // FX thread will handle that for us. - return; - - case "print-config": - try { - response = new PrintManager().configurePrinter(params); - } catch(IllegalArgumentException e) { - response = e.toString(); - error = true; - } - break; - - case "get": - String val = io.get(key); - if (val != null) { - // set() stores bare JSON. We must pass an - // Object to reply so that it may be embedded into - // a larger JSON response object, hence the JSON.parse(). - try { - response = JSON.parse(val); - } catch(java.lang.IllegalStateException e) { - error = true; - response = "Error JSON-parsing stored value " + val; - } - } - break; - - case "remove": - response = io.remove(key); - break; - - case "set" : - response = io.set(key, value); - break; - - case "append" : - response = io.append(key, value); - break; - - default: - response = "No such action: " + action; - error = true; - } - - reply(response, msgid, !error); - } - */ - - /** * Hatch main. * */ public static void main(String[] args) throws Exception { - new RequestHandler().start(); + new RequestHandler().start(); // start the STDIO handlers. launch(args); // launch the FX Application thread } } diff --git a/src/org/evergreen_ils/hatch/PrintManager.java b/src/org/evergreen_ils/hatch/PrintManager.java index 09e7aa4c49..98c8bdc993 100644 --- a/src/org/evergreen_ils/hatch/PrintManager.java +++ b/src/org/evergreen_ils/hatch/PrintManager.java @@ -87,6 +87,9 @@ public class PrintManager { public void print(WebEngine engine, JSONObject request) { JSONObject response = new JSONObject(); + response.put("msgid", request.get("msgid")); + response.put("status", "200"); + response.put("message", "OK"); try { long msgid = request.getLong("msgid"); @@ -103,7 +106,8 @@ public class PrintManager { if (showDialog) { if (!job.showPrintDialog(null)) { job.endJob(); // canceled by user - response.put("error", "Print job canceled by user"); + response.put("status", "200"); + response.put("message", "Print job canceled by user"); RequestHandler.reply(response); return; } @@ -120,7 +124,8 @@ public class PrintManager { + je.toString() + " : " + request.toString(); logger.warning(error); - response.put("error", error); + response.put("status", "400"); + response.put("message", error); } catch(IllegalArgumentException iae) { @@ -128,7 +133,8 @@ public class PrintManager { + iae.toString() + " : " + request.toString(); logger.warning(error); - response.put("error", error); + response.put("status", "400"); + response.put("message", error); } RequestHandler.reply(response); diff --git a/src/org/evergreen_ils/hatch/RequestHandler.java b/src/org/evergreen_ils/hatch/RequestHandler.java index 72224e1521..22064fa0a7 100644 --- a/src/org/evergreen_ils/hatch/RequestHandler.java +++ b/src/org/evergreen_ils/hatch/RequestHandler.java @@ -1,6 +1,7 @@ package org.evergreen_ils.hatch; import org.json.*; +import java.io.File; import java.util.logging.*; /** @@ -14,20 +15,60 @@ public class RequestHandler extends Thread { static final Logger logger = Hatch.getLogger(); + /** Root directory for all FileIO operations */ + private static String profileDirectory = null; + + /** Origin host/domain used for segregating files by browser host */ + private static String origin = null; + + void configure() { + + // Find the profile directory. + // The profile directory + origin string represent the base + // directory for all file I/O for this session. + if (profileDirectory == null) { // TODO: make configurable + String home = System.getProperty("user.home"); + profileDirectory = new File(home, ".evergreen").getPath(); + if (profileDirectory == null) { + logger.warning("Unable to set profile directory"); + } + } + } + /** * Unpack a JSON request and send it to the necessary Hatch handler. + * + * @return True if the calling code should avoid calling reply() with + * the response object. */ - void dispatchRequest(JSONObject request) throws JSONException { - + boolean dispatchRequest( + JSONObject request, JSONObject response) throws JSONException { + long msgid = request.getLong("msgid"); String action = request.getString("action"); + response.put("msgid", msgid); + logger.info("Received message id=" + msgid + " action=" + action); - JSONObject response = new JSONObject(); + // init must be called first to set the origin info + if (action.equals("init")) { + origin = request.getString("origin"); + return false; + } + + if (origin == null) { + response.put("status", 400); + response.put("message", "'init' action must be called first!"); + return false; + } + + String key = null; + String content = null; + FileIO fileIO = new FileIO(profileDirectory, origin); switch (action) { - + case "printers": response.put("printers", new PrintManager().getPrintersAsMaps()); @@ -35,25 +76,79 @@ public class RequestHandler extends Thread { case "print": // Confirm a minimal data set to enqueue print requests. - String content = request.getString("content"); + content = request.getString("content"); String contentType = request.getString("contentType"); if (content == null || "".equals(content)) { - response.put("error", "Empty print message"); + response.put("status", 400); + response.put("message", "Empty print message"); } else { - Hatch.enqueuePrintRequest(request); // Responses to print requests are generated asynchronously // and delivered from the FX print thread via reply(). - return; + return true; + } + + case "keys": // Return stored keys + String pfxKey = request.optString("key"); + response.put("keys", fileIO.keys(pfxKey)); + break; + + case "get": + key = request.getString("key"); + String val = fileIO.get(key); + + if (val != null) { + // set() stores bare JSON strings, but the caller to + // get() expects a unified JSON object in return, not + // a JSON string inside a JSON object. Parse the + // JSON and add it to the response object. + Object jsonBlob = new JSONTokener(val).nextValue(); + response.put("content", jsonBlob); } + break; + + case "remove": + key = request.getString("key"); + + if (!fileIO.remove(key)) { + response.put("status", 500); + response.put("message", "Unable to remove key: " + key); + } + + break; + + case "set" : + key = request.getString("key"); + // content is a JSON string + content = request.getString("content"); + + if (!fileIO.set(key, content)) { + response.put("status", 500); + response.put("message", "Unable to set key: " + key); + } + + break; + + case "append" : + key = request.getString("key"); + content = request.getString("content"); + + if (!fileIO.append(key, content)) { + response.put("status", 500); + response.put("message", "Unable to append to key: " + key); + } + + break; + default: - response.put("error", "Unknown action: " + action); + response.put("status", 404); + response.put("message", "Action not found: " + action); } - reply(response); + return false; } /** @@ -66,15 +161,26 @@ public class RequestHandler extends Thread { public void run() { + configure(); io.listen(); // STDIN/STDOUT handler while (true) { + + boolean skipReply = false; + JSONObject response = new JSONObject(); + + // these values are overidden as needed by the dispatch handler. + response.put("status", 200); + response.put("message", "OK"); + try { - dispatchRequest(io.recvMessage()); + skipReply = dispatchRequest(io.recvMessage(), response); } catch (JSONException je) { - logger.warning( - "JSON request protocol error: " + je.toString()); + response.put("status", 400); + response.put("message", "Bad Request: " + je.toString()); } + + if (!skipReply) reply(response); } } } diff --git a/src/org/evergreen_ils/hatch/TestHatch.java b/src/org/evergreen_ils/hatch/TestHatch.java index 3e172a31b3..7e75099d1d 100644 --- a/src/org/evergreen_ils/hatch/TestHatch.java +++ b/src/org/evergreen_ils/hatch/TestHatch.java @@ -16,24 +16,71 @@ public class TestHatch { public static void doSends() { int msgId = 1; + // initialize connection to set origin info JSONObject obj = new JSONObject(); obj.put("msgid", msgId++); + obj.put("action", "init"); + obj.put("origin", "https://test.hatch.evergreen-ils.org"); + io.sendMessage(obj); + + rest(); + + // get a list of stored keys + obj = new JSONObject(); + obj.put("msgid", msgId++); + obj.put("action", "keys"); + io.sendMessage(obj); + + rest(); + + // store a value + obj = new JSONObject(); + obj.put("msgid", msgId++); + obj.put("action", "set"); + obj.put("key", "eg.hatch.test.key1"); + // "set" expects a pre-JSON-ified string. TODO: reconsider. + obj.put("content", "\"Rando content, now with cheese\""); + io.sendMessage(obj); + + rest(); + + // store a value + obj = new JSONObject(); + obj.put("msgid", msgId++); + obj.put("action", "get"); + obj.put("key", "eg.hatch.test.key1"); + io.sendMessage(obj); + + rest(); + + + obj = new JSONObject(); + obj.put("msgid", msgId++); + obj.put("action", "keys"); + io.sendMessage(obj); + + rest(); + + // get a list of printers + obj = new JSONObject(); + obj.put("msgid", msgId++); obj.put("action", "printers"); io.sendMessage(obj); rest(); /* + // Printing tests + obj = new JSONObject(); obj.put("msgid", msgId++); obj.put("action", "print"); obj.put("contentType", "text/plain"); obj.put("content", "Hello, World!"); - obj.put("showDialog", true); + obj.put("showDialog", true); // avoid auto-print while testing io.sendMessage(obj); rest(); - */ obj = new JSONObject(); obj.put("msgid", msgId++); @@ -42,10 +89,16 @@ public class TestHatch { obj.put("content", "HELLO WORLD"); - obj.put("showDialog", true); + obj.put("showDialog", true); // avoid auto-print while testing + + JSONObject settings = new JSONObject(); + settings.put("copies", 2); + obj.put("settings", settings); io.sendMessage(obj); rest(); + + */ } /** -- 2.11.0