Native Messaging WIP -- file IO, etc.
authorBill Erickson <berickxx@gmail.com>
Wed, 9 Nov 2016 23:19:11 +0000 (18:19 -0500)
committerBill Erickson <berickxx@gmail.com>
Wed, 9 Nov 2016 23:19:11 +0000 (18:19 -0500)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
run.sh
src/org/evergreen_ils/hatch/FileIO.java
src/org/evergreen_ils/hatch/Hatch.java
src/org/evergreen_ils/hatch/PrintManager.java
src/org/evergreen_ils/hatch/RequestHandler.java
src/org/evergreen_ils/hatch/TestHatch.java

diff --git a/run.sh b/run.sh
index 11292c7..e6d64e1 100755 (executable)
--- 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;
index 8813b7c..5d857f0 100644 (file)
@@ -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.
index eff340e..12b32c0 100644 (file)
@@ -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<String,Object> params = null;
-
-        try {
-            params = (HashMap<String,Object>) 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
     }
 }
index 09e7aa4..98c8bdc 100644 (file)
@@ -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);
index 72224e1..22064fa 100644 (file)
@@ -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);
         }
     }
 }
index 3e172a3..7e75099 100644 (file)
@@ -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", "<html><body><b>HELLO WORLD</b><img src='" +
             "http://evergreen-ils.org/wp-content/uploads/2013/09/copy-Evergreen_Logo_sm072.jpg"
             + "'/></body></html>");
-        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();
+        
+        */
     }
 
     /**