From cf7081025db8777f63a1effdc5d81a35ebf80640 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 7 Nov 2016 13:50:12 -0500 Subject: [PATCH] stdin/stdout stream based hatch WIP Signed-off-by: Bill Erickson --- README | 20 +- run.sh | 11 +- src/org/evergreen_ils/hatch/FileIO.java | 56 ++-- src/org/evergreen_ils/hatch/Hatch.java | 238 ++++++++-------- .../evergreen_ils/hatch/HatchWebSocketHandler.java | 314 --------------------- .../evergreen_ils/hatch/HatchWebSocketServlet.java | 40 --- src/org/evergreen_ils/hatch/MessageIO.java | 123 ++++++++ src/org/evergreen_ils/hatch/PrintManager.java | 37 ++- 8 files changed, 299 insertions(+), 540 deletions(-) delete mode 100644 src/org/evergreen_ils/hatch/HatchWebSocketHandler.java delete mode 100644 src/org/evergreen_ils/hatch/HatchWebSocketServlet.java create mode 100644 src/org/evergreen_ils/hatch/MessageIO.java diff --git a/README b/README index 7c06daae5..733d9f1de 100644 --- a/README +++ b/README @@ -2,26 +2,14 @@ Hatch - Java Print / Storage / Etc Service ** ROUGH SETUP NOTES ** -Install Hatch on your desktop -- Linux edition: - -% wget http://download.eclipse.org/jetty/stable-9/dist/jetty-distribution-9.2.5.v20141112.tar.gz -% tar -zxf jetty-distribution-9.2.5.v20141112.tar.gz -% ln -s jetty-distribution-9.2.5.v20141112 jetty - # download jdk1.8 (requires license agreement) -- haven't tested on openjdk yet. # http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html # and extract in the same directory % ln -s jdk1.8.0_25 jdk1.8 % mkdir lib -% wget -O lib/jetty-util-ajax-9.2.5.v20141112.jar \ - 'http://central.maven.org/maven2/org/eclipse/jetty/jetty-util-ajax/9.2.5.v20141112/jetty-util-ajax-9.2.5.v20141112.jar' - -# create an SSL certificat for jetty -# if you use a password other than "password", modify references to -# "password" in hath.xml (in the top directory). -% cd jetty/etc/ -% ../../jdk1.8/bin/keytool -keystore keystore -alias jetty -genkey -keyalg RSA +Download javax.json-api jar file from +http://search.maven.org/remotecontent?filepath=javax/json/javax.json-api/1.0/javax.json-api-1.0.jar # compile % ./run.sh @@ -29,7 +17,3 @@ Install Hatch on your desktop -- Linux edition: # compile + run % ./run.sh 1 -# open https://localhost:8443/ in Chrome and click through the security warning. -# Then open the browser client. -# Set "This workstation uses a remote print / storage service ("Hatch")?" under Admin -> Workstation -# optionally configure / test printing diff --git a/run.sh b/run.sh index 91d2dca3d..1433313f0 100755 --- a/run.sh +++ b/run.sh @@ -1,15 +1,10 @@ JAVA_HOME=jdk1.8 -JETTY_HOME=jetty +CP=lib:lib/javax.json-api-1.0.jar # compile -$JAVA_HOME/bin/javac \ - -cp "$JETTY_HOME/lib/*:$JETTY_HOME/lib/websocket/*:lib/*" \ - -Xdiags:verbose -d lib \ - src/org/evergreen_ils/hatch/*.java +$JAVA_HOME/bin/javac -cp $CP -d lib src/org/evergreen_ils/hatch/*.java [ -z "$1" ] && exit; # run -$JAVA_HOME/bin/java \ - -cp "$JETTY_HOME/lib/*:$JETTY_HOME/lib/websocket/*:lib/*:lib" \ - org.evergreen_ils.hatch.Hatch +$JAVA_HOME/bin/java -cp $CP org.evergreen_ils.hatch.Hatch diff --git a/src/org/evergreen_ils/hatch/FileIO.java b/src/org/evergreen_ils/hatch/FileIO.java index 0aa2fc785..8813b7c1e 100644 --- a/src/org/evergreen_ils/hatch/FileIO.java +++ b/src/org/evergreen_ils/hatch/FileIO.java @@ -18,8 +18,6 @@ package org.evergreen_ils.hatch; import java.io.*; import java.util.LinkedList; import java.util.Arrays; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; public class FileIO { @@ -60,7 +58,7 @@ public class FileIO { // -------------------------------------------------- // logger - private static final Logger logger = Log.getLogger("FileIO"); + //private static final Logger logger = Log.getLogger("FileIO"); /** * Constructs a new FileIO with the provided base path. @@ -84,7 +82,7 @@ public class FileIO { File dir = new File(basePath); if (!dir.exists()) { if (!dir.mkdir()) { - logger.info("Unable to create directory: " + dir.getName()); + //logger.info("Unable to create directory: " + dir.getName()); return null; } } @@ -93,12 +91,12 @@ public class FileIO { File subDir = new File(basePath, originDomain); if (!subDir.exists()) { if (!subDir.mkdir()) { - logger.info("Unable to create directory: " + subDir.getName()); + //logger.info("Unable to create directory: " + subDir.getName()); return null; } } - logger.info("baseDir: " + subDir.getName()); + //logger.info("baseDir: " + subDir.getName()); return subDir; } @@ -124,7 +122,7 @@ public class FileIO { * @return success or failure */ public boolean set(String key, String text) { - logger.info("set => " + key); + //logger.info("set => " + key); File file = getFile(key); if (text == null) return false; @@ -133,8 +131,8 @@ public class FileIO { // delete the file if it exists if (!file.exists() && !file.createNewFile()) { - logger.info( - "Unable to create file: " + file.getCanonicalPath()); + //logger.info( + //"Unable to create file: " + file.getCanonicalPath()); return false; } @@ -146,8 +144,8 @@ public class FileIO { outStream.close(); } catch(IOException e) { - logger.warn("Error calling set() with key " + key); - logger.warn(e); + //logger.warn("Error calling set() with key " + key); + //logger.warn(e); return false; } @@ -164,15 +162,15 @@ public class FileIO { * @return success or failure */ public boolean append(String key, String text) { - logger.info("append => " + key); + //logger.info("append => " + key); File file = getFile(key); try { // create the file if it doesn's already exist if (!file.exists() && !file.createNewFile()) { - logger.info( - "Unable to create file: " + file.getCanonicalPath()); + //logger.info( + //"Unable to create file: " + file.getCanonicalPath()); return false; } @@ -183,8 +181,8 @@ public class FileIO { outStream.close(); } catch(IOException e) { - logger.warn("Error in append() with key " + key); - logger.warn(e); + //logger.warn("Error in append() with key " + key); + //logger.warn(e); return false; } @@ -198,7 +196,7 @@ public class FileIO { * @return The text content of the file */ public String get(String key) { - logger.info("get => " + key); + //logger.info("get => " + key); File file = getFile(key); if (!file.exists()) return null; @@ -213,8 +211,8 @@ public class FileIO { buf.append(line); } } catch (IOException e) { - logger.warn("Error reading key: " + key); - logger.warn(e); + //logger.warn("Error reading key: " + key); + //logger.warn(e); return null; } @@ -228,20 +226,20 @@ public class FileIO { * @return success or failure */ public boolean remove(String key) { - logger.info("remove => " + key); + //logger.info("remove => " + key); File file = getFile(key); - try { + //try { if (file.exists() && !file.delete()) { - logger.info( - "Unable to delete file: " + file.getCanonicalPath()); + //logger.info( + //"Unable to delete file: " + file.getCanonicalPath()); return false; } return true; - } catch (IOException e) { - logger.warn("Error deleting key: " + key); - logger.warn(e); - return false; - } + //} catch (IOException e) { + //logger.warn("Error deleting key: " + key); + //logger.warn(e); + //return false; + //} } /** @@ -261,7 +259,7 @@ public class FileIO { * @return Array of keys */ public String[] keys(String prefix) { - logger.info("keys => " + prefix); + //logger.info("keys => " + prefix); File dir = baseDir(); if (dir == null || !dir.exists()) diff --git a/src/org/evergreen_ils/hatch/Hatch.java b/src/org/evergreen_ils/hatch/Hatch.java index 0bed93f9c..3b1df8dae 100644 --- a/src/org/evergreen_ils/hatch/Hatch.java +++ b/src/org/evergreen_ils/hatch/Hatch.java @@ -15,23 +15,6 @@ */ package org.evergreen_ils.hatch; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.xml.XmlConfiguration; - import javafx.application.Application; import javafx.application.Platform; import javafx.scene.Scene; @@ -49,8 +32,7 @@ import javafx.concurrent.Task; import javafx.event.EventHandler; import javafx.concurrent.WorkerStateEvent; import java.util.concurrent.LinkedBlockingQueue; - -import org.eclipse.jetty.util.ajax.JSON; +import java.util.logging.*; import java.util.Map; @@ -88,15 +70,11 @@ public class Hatch extends Application { private Stage primaryStage; /** Our logger instance */ - static final Logger logger = Log.getLogger("Hatch"); - - /** Message queue for passing messages from the Jetty thread into - * the JavaFX Application thread */ - private static LinkedBlockingQueue requestQueue = - new LinkedBlockingQueue(); + static Logger logger; + /** - * Printable region containing a browser + * Printable region containing a browser. */ class BrowserView extends Region { WebView webView = new WebView(); @@ -107,47 +85,11 @@ public class Hatch extends Application { } /** - * Service task which listens for inbound messages from the - * servlet. - * - * The code blocks on the concurrent queue, so it must be - * run in a separate thread to avoid locking the main FX thread. - */ - private static class MsgListenService extends Service> { - protected Task> createTask() { - return new Task>() { - protected Map call() { - while (true) { - logger.info("MsgListenService waiting for a message..."); - try { - // take() blocks until a message is available - return requestQueue.take(); - } catch (InterruptedException e) { - // interrupted, go back and listen - continue; - } - } - } - }; - } - } - - - /** * JavaFX startup call */ @Override public void start(Stage primaryStage) { this.primaryStage = primaryStage; - startMsgTask(); - } - - /** - * Queues a message for processing by the queue processing thread. - */ - public static void enqueueMessage(Map params) { - logger.debug("queueing print message"); - requestQueue.offer(params); } /** @@ -159,7 +101,7 @@ public class Hatch extends Application { String contentType = (String) params.get("contentType"); if (content == null) { - logger.warn("handlePrint() called with no content"); + //logger.warn("handlePrint() called with no content"); return; } @@ -170,9 +112,9 @@ public class Hatch extends Application { browser.webEngine.getLoadWorker() .stateProperty() .addListener( (ChangeListener) (obsValue, oldState, newState) -> { - logger.info("browser load state " + newState); + //logger.info("browser load state " + newState); if (newState == State.SUCCEEDED) { - logger.info("Print browser page load completed"); + //logger.info("Print browser page load completed"); // Avoid nested UI event loops -- runLater Platform.runLater(new Runnable() { @@ -183,73 +125,145 @@ public class Hatch extends Application { } }); - logger.info("printing " + content.length() + " bytes of " + contentType); + //logger.info("printing " + content.length() + " bytes of " + contentType); browser.webEngine.loadContent(content, contentType); - - // After queueing up the HTML for printing, go back to listening - // for new messages. - startMsgTask(); } - /** - * Fire off the Service task, which checks for queued messages. - * - * When a queued message is found, it's sent off for printing. - */ - public void startMsgTask() { - MsgListenService service = new MsgListenService(); + /* + public void onMessage(String message) { + if (session == null || !session.isOpen()) return; + //logger.info("onMessage() " + message); + + HashMap params = null; - logger.info("starting MsgTask"); + try { + params = (HashMap) JSON.parse(message); + } catch (ClassCastException e) { + reply("Invalid WebSockets JSON message " + message, + new Long(-1), false); + } - service.setOnSucceeded( - new EventHandler() { + 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"); - @Override - public void handle(WorkerStateEvent t) { - logger.info("MsgTask handling message.. "); - Map message = - (Map) t.getSource().getValue(); + //logger.info("Received request for action " + action); - // avoid nesting UI event loops by kicking off the print - // operation from the main FX loop after this event handler - // has exited. - Platform.runLater( - new Runnable() { - @Override public void run() { - handlePrint(message); - } + // 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; + } - service.start(); + reply(response, msgid, !error); } + */ + + + /* TODO: make me configurable, + * see java.util.logging docs for xml config file + */ + public static Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger("Hatch"); + logger.setLevel(Level.ALL); + ConsoleHandler handler = new ConsoleHandler(); + handler.setLevel(Level.ALL); + handler.setFormatter(new SimpleFormatter()); + logger.addHandler(handler); + logger.setUseParentHandlers(false); + } + return logger; + } + /** * Hatch main. * - * Reads the Jetty configuration, starts the Jetty server thread, - * then launches the JavaFX Application thread. */ public static void main(String[] args) throws Exception { - // build a server from our hatch.xml configuration file - XmlConfiguration configuration = - new XmlConfiguration(new FileInputStream("hatch.xml")); - - Server server = (Server) configuration.configure(); - - logger.info("Starting Jetty server"); - - // start our server, but do not join(), since we want to server - // to continue running in its own thread - server.start(); - - logger.info("Launching FX Application"); + // start the messageIO reader/writer threads + new MessageIO().start(); // launch the FX Application thread launch(args); + + System.out.println("Here"); + } + } diff --git a/src/org/evergreen_ils/hatch/HatchWebSocketHandler.java b/src/org/evergreen_ils/hatch/HatchWebSocketHandler.java deleted file mode 100644 index bb2ee9c89..000000000 --- a/src/org/evergreen_ils/hatch/HatchWebSocketHandler.java +++ /dev/null @@ -1,314 +0,0 @@ -/* ----------------------------------------------------------------------- - * Copyright 2014 Equinox Software, Inc. - * Bill Erickson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * ----------------------------------------------------------------------- - */ -package org.evergreen_ils.hatch; - -import java.io.IOException; -import java.io.File; -import java.io.BufferedReader; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import javax.servlet.ServletConfig; - -import org.eclipse.jetty.util.ajax.JSON; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import java.util.Arrays; -import java.util.List; -import java.util.HashMap; -import java.util.Map; - -@WebSocket -public class HatchWebSocketHandler { - - /** A single connection to a WebSockets client */ - private Session session; - - /** Current origin domain */ - private String origin; - - /** List of Origin domains from which we allow connections */ - private static String[] trustedDomains; - - /** True if we trust all Origin domains */ - private static boolean trustAllDomains = false; - - /** Root directory for all FileIO operations */ - private static String profileDirectory; - - /** Our logger instance */ - private static final Logger logger = Log.getLogger("WebSocketHandler"); - - /** - * Apply trusted domains. - * - * If the first domain in the list equals "*", that signifies that - * all domains should be trusted. - * - * @param domains Array of domains to trust. - */ - public static void setTrustedDomains(String[] domains) { - trustedDomains = domains; - - if (domains.length > 0 ) { - - if ("*".equals(domains[0])) { - logger.info("All domains trusted"); - trustAllDomains = true; - - } else { - - for(String domain : trustedDomains) { - logger.info("Trusted domain: " + domain); - } - } - } else { - logger.warn("No domains are trusted. All requests will be denied"); - } - } - - /** - * Sets the profile directory - * - * @param directory Directory path as a String - */ - public static void setProfileDirectory(String directory) { - profileDirectory = directory; - } - - - /** - * Runs the initial, global configuration for this handler. - * TODO: move this into setProfileDirectory() (which will need to - * be force-called regardless of config)? - */ - public static void configure() { - logger.info("WebSocketHandler.configure()"); - - // default to ~/.evergreen - if (profileDirectory == null) { - String home = System.getProperty("user.home"); - profileDirectory = new File(home, ".evergreen").getPath(); - if (profileDirectory == null) { - logger.info("Unable to set profile directory"); - } - } - } - - /** - * Compares the Origin of the current WebSocket connection to the list - * of allowed domains to determine if the current connection should - * be allowed. - * - * @return True if the Origin domain is allowed, false otherwise. - */ - protected boolean verifyOriginDomain() { - logger.info("received connection from IP " + - session.getRemoteAddress().getAddress()); - - origin = session.getUpgradeRequest().getHeader("Origin"); - - if (origin == null) { - logger.warn("No Origin header in request; Dropping connection"); - return false; - } - - logger.info("connection origin is " + origin); - - if (trustAllDomains) return true; - - if (java.util.Arrays.asList(trustedDomains).indexOf(origin) < 0) { - logger.warn("Request from un-trusted domain: " + origin); - return false; - } - - return true; - } - - - /** - * WebSocket onConnect handler. - * - * Verify the Origin domain before any communication may take place - */ - @OnWebSocketConnect - public void onConnect(Session session) { - this.session = session; - if (!verifyOriginDomain()) session.close(); - } - - /** - * WebSocket onClose handler. - * - * Clears our current session. - */ - @OnWebSocketClose - public void onClose(int statusCode, String reason) { - logger.info("onClose() statusCode=" + statusCode + ", reason=" + reason); - this.session = null; - } - - /** - * Send a message to our connected client. - * - * @param json A JSON-encodable object to send to the caller. - * @param msgid The message identifier - */ - protected void reply(Object json, Long msgid) { - reply(json, msgid, true); - } - - /** - * Send a message to our connected client. - * - * @param json A JSON-encodable object to send to the caller. - * @param msgid The message identifier - * @param success If false, the response will be packaged as an error - * message. - */ - protected void reply(Object json, Long msgid, boolean success) { - - Map response = new HashMap(); - response.put("msgid", msgid); - - if (success) { - response.put("content", json); - } else { - response.put("error", json); - } - - String jsonString = JSON.toString(response); - logger.info("replying with : " + jsonString); - - try { - if (!success) logger.warn(jsonString); - session.getRemote().sendString(jsonString); - } catch (IOException e) { - logger.warn(e); - } - } - - /** - * WebSocket onMessage handler. - * - * Processes the incoming message and passes the request off to the - * necessary handler. Messages must be encoded as JSON strings. - */ - @OnWebSocketMessage - @SuppressWarnings("unchecked") // direct casting JSON-parsed objects - 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); - } -} diff --git a/src/org/evergreen_ils/hatch/HatchWebSocketServlet.java b/src/org/evergreen_ils/hatch/HatchWebSocketServlet.java deleted file mode 100644 index b7e07ec67..000000000 --- a/src/org/evergreen_ils/hatch/HatchWebSocketServlet.java +++ /dev/null @@ -1,40 +0,0 @@ -/* ----------------------------------------------------------------------- - * Copyright 2014 Equinox Software, Inc. - * Bill Erickson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * ----------------------------------------------------------------------- - */ -package org.evergreen_ils.hatch; - -import javax.servlet.annotation.WebServlet; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import org.eclipse.jetty.websocket.servlet.WebSocketServlet; -import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; - -/** - * Links HatchWebSocketHandler in as a Servlet handler. - */ -public class HatchWebSocketServlet extends WebSocketServlet { - - @Override - public void configure(WebSocketServletFactory factory) { - factory.register(HatchWebSocketHandler.class); - } - - @Override - public void init(ServletConfig config) throws ServletException { - super.init(config); // required for WS - HatchWebSocketHandler.configure(); - } -} - diff --git a/src/org/evergreen_ils/hatch/MessageIO.java b/src/org/evergreen_ils/hatch/MessageIO.java new file mode 100644 index 000000000..e040b27aa --- /dev/null +++ b/src/org/evergreen_ils/hatch/MessageIO.java @@ -0,0 +1,123 @@ +package org.evergreen_ils.hatch; + +import java.util.logging.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.nio.ByteBuffer; +import javax.json.*; +import java.io.IOException; + +public class MessageIO { + + private LinkedBlockingQueue inQueue; + private LinkedBlockingQueue outQueue; + //private static Logger logger = Logger.getLogger("org.evergreen_ils.hatch"); + private static Logger logger = Hatch.getLogger(); + + private MessageReader reader; + private MessageWriter writer; + + public MessageIO() { + inQueue = new LinkedBlockingQueue(); + outQueue = new LinkedBlockingQueue(); + reader = new MessageReader(); + writer = new MessageWriter(); + } + + public void start() { + writer.start(); + reader.start(); + } + + class MessageReader extends Thread { + + private class EndOfStreamException extends IOException { + } + + private int bytesToInt(byte[] bytes) { + return + (bytes[3] << 24) & 0xff000000 + | (bytes[2] << 16) & 0x00ff0000 + | (bytes[1] << 8) & 0x0000ff00 + | (bytes[0] << 0) & 0x000000ff; + } + + private String readOneMessage() throws EndOfStreamException, IOException { + byte[] lenBytes = new byte[4]; + int bytesRead = System.in.read(lenBytes); + + if (bytesRead == -1) { + throw new EndOfStreamException(); + } + + int msgLength = bytesToInt(lenBytes); + + if (msgLength == 0) { + throw new IOException("Inbound message is 0 bytes. Interrupted?"); + } + + logger.info("MessageReader read message length: " + msgLength); + + byte[] msgBytes = new byte[msgLength]; + + bytesRead = System.in.read(msgBytes); + + if (bytesRead == -1) { + throw new EndOfStreamException(); + } + + String message = new String(msgBytes, "UTF-8"); + + logger.info("MessageReader read message " + message); + + return message; + } + + public void run() { + + while (true) { + + try { + + String message = readOneMessage(); + + } catch (EndOfStreamException eose) { + + logger.warning("STDIN closed. MessageReader thread exiting"); + return; + + } catch (IOException ioe) { + logger.warning(ioe); + } + + // TODO: convert to JSON + // TODO: push onto inQueue + } + + } + } + + class MessageWriter extends Thread { + + private byte[] intToBytes(int length) { + byte[] bytes = new byte[4]; + bytes[0] = (byte) (length & 0xFF); + bytes[1] = (byte) ((length >> 8) & 0xFF); + bytes[2] = (byte) ((length >> 16) & 0xFF); + bytes[3] = (byte) ((length >> 24) & 0xFF); + return bytes; + } + + + public void writeOneMessage(String message) throws IOException { + + System.out.write(intToBytes(message.length())); + System.out.write(message.getBytes("UTF-8")); + System.out.flush(); + } + + public void run() { + + } + } + +} diff --git a/src/org/evergreen_ils/hatch/PrintManager.java b/src/org/evergreen_ils/hatch/PrintManager.java index 40a4003d7..f63d7e71b 100644 --- a/src/org/evergreen_ils/hatch/PrintManager.java +++ b/src/org/evergreen_ils/hatch/PrintManager.java @@ -15,9 +15,6 @@ */ package org.evergreen_ils.hatch; -// logging -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; // printing import javafx.print.*; @@ -47,7 +44,7 @@ import java.util.LinkedHashSet; public class PrintManager { /** Our logger instance */ - static final Logger logger = Log.getLogger("PrintManager"); + //static final Logger logger = Log.getLogger("PrintManager"); /** * Shows the print dialog, allowing the user to modify settings, @@ -95,43 +92,45 @@ public class PrintManager { Map settings = (Map) params.get("config"); + /* HatchWebSocketHandler socket = (HatchWebSocketHandler) params.get("socket"); + */ PrinterJob job = null; try { job = buildPrinterJob(settings); } catch(IllegalArgumentException e) { - socket.reply(e.toString(), msgid, false); + //socket.reply(e.toString(), msgid, false); return; } if (showDialog != null && showDialog.booleanValue()) { - logger.info("Print dialog requested"); + //logger.info("Print dialog requested"); if (!job.showPrintDialog(null)) { // job canceled by user - logger.info("after dialog"); + //logger.info("after dialog"); job.endJob(); - socket.reply("Print job canceled", msgid); + //socket.reply("Print job canceled", msgid); return; } } else { - logger.info("No print dialog requested"); + //logger.info("No print dialog requested"); } Thread[] all = new Thread[100]; int count = Thread.currentThread().enumerate(all); - logger.info(count + " active threads in print"); - logger.info("Thread " + Thread.currentThread().getId() + " printing..."); + //logger.info(count + " active threads in print"); + //logger.info("Thread " + Thread.currentThread().getId() + " printing..."); engine.print(job); - logger.info("after print"); + //logger.info("after print"); job.endJob(); - socket.reply("Print job succeeded", msgid); + //socket.reply("Print job succeeded", msgid); } /** @@ -193,7 +192,7 @@ public class PrintManager { Set papers = printerAttrs.getSupportedPapers(); for (Paper source : papers) { if (source.getName().equals(paperName)) { - logger.info("Found matching paper for " + paperName); + //logger.info("Found matching paper for " + paperName); paper = source; break; } @@ -258,7 +257,7 @@ public class PrintManager { // meaning no source.. meaning let the printer decide. for (PaperSource source : paperSources) { if (source.getName().equals(paperSource)) { - logger.info("matched paper source for " + paperSource); + //logger.info("matched paper source for " + paperSource); jobSettings.setPaperSource(source); break; } @@ -267,7 +266,7 @@ public class PrintManager { if (pageRanges != null) { - logger.info("pageRanges = " + pageRanges.toString()); + //logger.info("pageRanges = " + pageRanges.toString()); List builtRanges = new LinkedList(); int i = 0, start = 0, end = 0; do { @@ -296,7 +295,7 @@ public class PrintManager { Map settings = new HashMap(); JobSettings jobSettings = job.getJobSettings(); - logger.info("Extracting print job settings from " + job); + //logger.info("Extracting print job settings from " + job); settings.put( jobSettings.collationProperty().getName(), @@ -358,7 +357,7 @@ public class PrintManager { } } - logger.info("compiled printer properties: " + settings.toString()); + //logger.info("compiled printer properties: " + settings.toString()); return settings; } @@ -397,7 +396,7 @@ public class PrintManager { printer.getName().equals(defaultPrinter.getName())) { printerMap.put("is-default", new Boolean(true)); } - logger.info("found printer " + printer.getName()); + //logger.info("found printer " + printer.getName()); } return printerMaps; -- 2.11.0