more websockety bits
authorBill Erickson <berick@esilibrary.com>
Tue, 18 Mar 2014 18:11:53 +0000 (14:11 -0400)
committerBill Erickson <berick@esilibrary.com>
Tue, 18 Mar 2014 18:11:53 +0000 (14:11 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
websocket/fetch_comp_run.sh [new file with mode: 0755]
websocket/org/evergreen_ils/hatch/EventClient.java [new file with mode: 0644]
websocket/org/evergreen_ils/hatch/EventServer.java [new file with mode: 0644]
websocket/org/evergreen_ils/hatch/EventSocket.java [new file with mode: 0644]
websocket/org/evergreen_ils/hatch/FileIO.java [new file with mode: 0644]
websocket/org/evergreen_ils/hatch/PrintDriver.java [new file with mode: 0644]

diff --git a/websocket/fetch_comp_run.sh b/websocket/fetch_comp_run.sh
new file mode 100755 (executable)
index 0000000..4c0b4f3
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/bash
+set -e
+
+BASE_DIR=$PWD
+JETTY_VERSION=9.1.2.v20140210
+JETTY_DIR="jetty-distribution-$JETTY_VERSION"
+JETTY_PACKAGE="$JETTY_DIR.tar.gz"
+JETTY_UTIL_AJAX=jetty-util-ajax-$JETTY_VERSION.jar
+
+OPT_FETCH=0;
+OPT_COMPILE=0
+OPT_RUN=0
+
+DEPENDS="javax.websocket-api-1.0.jar javax-websocket-client-impl-$JETTY_VERSION.jar javax-websocket-server-impl-$JETTY_VERSION.jar jetty-http-$JETTY_VERSION.jar jetty-io-$JETTY_VERSION.jar jetty-security-$JETTY_VERSION.jar jetty-server-$JETTY_VERSION.jar jetty-servlet-$JETTY_VERSION.jar jetty-util-$JETTY_VERSION.jar servlet-api-3.1.jar websocket-api-$JETTY_VERSION.jar websocket-client-$JETTY_VERSION.jar websocket-common-$JETTY_VERSION.jar websocket-server-$JETTY_VERSION.jar websocket-servlet-$JETTY_VERSION.jar"
+
+# return here on exit
+trap "{ cd $BASE_DIR; }" EXIT
+
+function usage() {
+    echo ""
+    echo "Fetch dependencies, compile, and run Hatch:";
+    echo ""
+    echo "$0 -fcr";
+    echo ""
+}
+
+while getopts  "fcrh" flag; do                                         
+    case $flag in                                                              
+        "f")   OPT_FETCH=1;;
+        "c")   OPT_COMPILE=1;;
+        "r")   OPT_RUN=1;;
+        "h"|*)  usage;;                                                        
+    esac;                                                                      
+done 
+
+if [ $OPT_FETCH -eq 1 ]; then
+
+    mkdir -p lib;
+
+    if [ ! -f $JETTY_PACKAGE ]; then
+        echo "Fetching Jetty package...";
+        wget "http://carroll.aset.psu.edu/pub/eclipse/jetty/$JETTY_VERSION/dist/$JETTY_PACKAGE";
+    fi;
+
+    if [ ! -d $JETTY_DIR ]; then
+        echo "Unpacking Jetty...";
+        tar xf $JETTY_PACKAGE;
+        for jar in $DEPENDS; do
+            find $JETTY_DIR -name "$jar" -exec cp -v {} lib/ \; 
+        done;
+    fi;
+fi;
+
+if [ $OPT_COMPILE -eq 1 ]; then
+    echo "Compiling..."
+    javac -Xlint:unchecked -cp "lib/*" org/evergreen_ils/hatch/*.java
+fi;
+
+if [ $OPT_RUN -eq 1 ]; then
+    echo "Running..."
+    java -cp ".:lib/*" org.evergreen_ils.hatch.EventServer
+fi;
diff --git a/websocket/org/evergreen_ils/hatch/EventClient.java b/websocket/org/evergreen_ils/hatch/EventClient.java
new file mode 100644 (file)
index 0000000..25a903d
--- /dev/null
@@ -0,0 +1,46 @@
+package org.evergreen_ils.hatch;
+
+import java.net.URI;
+import javax.websocket.ContainerProvider;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+
+import org.eclipse.jetty.util.component.LifeCycle;
+
+public class EventClient
+{
+    public static void main(String[] args)
+    {
+        URI uri = URI.create("ws://localhost:8080/hatch/");
+
+        try
+        {
+            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
+
+            try
+            {
+                // Attempt Connect
+                Session session = container.connectToServer(EventSocket.class,uri);
+                // Send a message
+                session.getBasicRemote().sendText("Hello");
+                // Close session
+                session.close();
+            }
+            finally
+            {
+                // Force lifecycle stop when done with container.
+                // This is to free up threads and resources that the
+                // JSR-356 container allocates. But unfortunately
+                // the JSR-356 spec does not handle lifecycles (yet)
+                if (container instanceof LifeCycle)
+                {
+                    ((LifeCycle)container).stop();
+                }
+            }
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/websocket/org/evergreen_ils/hatch/EventServer.java b/websocket/org/evergreen_ils/hatch/EventServer.java
new file mode 100644 (file)
index 0000000..7966725
--- /dev/null
@@ -0,0 +1,44 @@
+package org.evergreen_ils.hatch;
+
+// Code derived from 
+// https://github.com/jetty-project/embedded-jetty-websocket-examples/tree/master/javax.websocket-example
+
+import javax.websocket.server.ServerContainer;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+
+public class EventServer
+{
+    public static void main(String[] args)
+    {
+        Server server = new Server();
+        ServerConnector connector = new ServerConnector(server);
+        connector.setPort(8080);
+        server.addConnector(connector);
+
+        // Setup the basic application "context" for this application at "/"
+        // This is also known as the handler tree (in jetty speak)
+        ServletContextHandler context = 
+            new ServletContextHandler(ServletContextHandler.SESSIONS);
+        context.setContextPath("/");
+        server.setHandler(context);
+
+        try {
+            // Initialize javax.websocket layer
+            ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
+
+            // Add WebSocket endpoint to javax.websocket layer
+            wscontainer.addEndpoint(EventSocket.class);
+
+            server.start();
+            server.dump(System.err);
+            server.join();
+
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+        }
+    }
+}
diff --git a/websocket/org/evergreen_ils/hatch/EventSocket.java b/websocket/org/evergreen_ils/hatch/EventSocket.java
new file mode 100644 (file)
index 0000000..26b3986
--- /dev/null
@@ -0,0 +1,39 @@
+package org.evergreen_ils.hatch;
+
+import javax.websocket.ClientEndpoint;
+import javax.websocket.CloseReason;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.ServerEndpoint;
+
+@ClientEndpoint
+@ServerEndpoint(value="/hatch/")
+public class EventSocket
+{
+    @OnOpen
+    public void onWebSocketConnect(Session sess)
+    {
+        System.out.println("Socket Connected: " + sess);
+    }
+    
+    @OnMessage
+    public void onWebSocketText(String message)
+    {
+        System.out.println("Received TEXT message: " + message);
+    }
+    
+    @OnClose
+    public void onWebSocketClose(CloseReason reason)
+    {
+        System.out.println("Socket Closed: " + reason);
+    }
+    
+    @OnError
+    public void onWebSocketError(Throwable cause)
+    {
+        cause.printStackTrace(System.err);
+    }
+}
diff --git a/websocket/org/evergreen_ils/hatch/FileIO.java b/websocket/org/evergreen_ils/hatch/FileIO.java
new file mode 100644 (file)
index 0000000..95fc012
--- /dev/null
@@ -0,0 +1,145 @@
+package org.evergreen_ils.hatch;
+import java.io.*;
+import java.util.LinkedList;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class FileIO {
+
+    String basePath;
+    private static final Logger logger = Log.getLogger("FileIO");
+
+    public FileIO(String directory) {
+        basePath = directory;
+    }
+
+    protected File getFile(String key) {
+        File dir = new File(basePath);
+        if (!dir.exists()) {
+            if (!dir.mkdir()) {
+                logger.info("Unable to create director: " + basePath);
+                return null;
+            }
+        }
+        return new File(dir, key);
+    }
+
+    public boolean set(String key, String text) {
+        logger.info("set => " + key);
+        File file = getFile(key);
+
+        try {
+
+            // delete the file if it exists
+            if (!file.exists() && !file.createNewFile()) {
+                logger.info(
+                    "Unable to create file: " + file.getCanonicalPath());
+                return false;
+            }
+
+            // destructive write (replace existing text)
+            Writer outStream = new BufferedWriter(
+                new FileWriter(file.getAbsoluteFile()));
+
+            outStream.write(text);
+            outStream.close();
+
+        } catch(IOException e) {
+            logger.warn("Error calling set() with key " + key);
+            logger.warn(e);
+            return false;
+        }
+
+        return true;
+    }
+
+    public boolean append(String key, String text) {
+        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());
+                return false;
+            }
+
+            // non-destructive write (append)
+            Writer outStream = new BufferedWriter(
+                new FileWriter(file.getAbsoluteFile(), true));
+            outStream.write(text);
+            outStream.close();
+
+        } catch(IOException e) {
+            logger.warn("Error in append() with key " + key);
+            logger.warn(e);
+            return false;
+        }
+
+        return true;
+    }
+
+    public BufferedReader get(String key) {
+        logger.info("get => " + key);
+        File file = getFile(key);
+        if (!file.exists()) return null;
+
+        StringBuffer sbuf = new StringBuffer();
+        try {
+            return new BufferedReader(
+                new FileReader(file.getAbsoluteFile()));
+        } catch (IOException e) {
+            logger.warn("Error reading key: " + key);
+            logger.warn(e);
+            return null;
+        }
+    }
+
+    public boolean delete(String key) {
+        logger.info("delete => " + key);
+        File file = getFile(key);
+        try {
+            if (file.exists() && !file.delete()) {
+                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;
+        }
+    }
+
+    public String[] keys() {
+        return keys(null);
+    }
+
+    public String[] keys(String prefix) {
+        logger.info("keys => " + prefix);
+        File dir = new File(basePath);
+        if (!dir.exists()) return new String[0];
+
+        LinkedList<String> nameList = new LinkedList<String>();
+        File[] files = dir.listFiles();
+
+        for (File file : files) {
+            if (file.isFile()) {
+                String name = file.getName();
+                if (prefix == null) {
+                    nameList.add(name);
+                } else {
+                    if (name.startsWith(prefix)) {
+                        nameList.add(name);
+                    }
+                }
+            }
+        }
+
+        return (String[]) nameList.toArray(new String[0]);
+    }
+}
diff --git a/websocket/org/evergreen_ils/hatch/PrintDriver.java b/websocket/org/evergreen_ils/hatch/PrintDriver.java
new file mode 100644 (file)
index 0000000..5247e38
--- /dev/null
@@ -0,0 +1,226 @@
+package org.evergreen_ils.hatch;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.awt.print.*;
+
+import javax.print.PrintService;
+import javax.print.PrintServiceLookup;
+import javax.print.attribute.Attribute;
+import javax.print.attribute.AttributeSet;
+
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.text.AttributedString;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+public class PrintDriver implements Printable {
+
+    private String printText;
+    private static final Logger logger = Log.getLogger("PrintDriver");
+
+    private final static int POINTS_PER_INCH = 72;
+
+    public int print(Graphics g, PageFormat pf, int page) 
+        throws PrinterException {
+
+        // for now, assume we only have one page
+        if (page > 0) return NO_SUCH_PAGE;
+
+        // find the imageable area
+        Graphics2D g2d = (Graphics2D)g;
+        g2d.translate(pf.getImageableX(), pf.getImageableY());
+
+        /* --------------------------------------------- */
+        /* -- rendering the print text as paragraph ---  */
+        Point2D.Double pen = new Point2D.Double(
+            0.25 * POINTS_PER_INCH, 0.25 * POINTS_PER_INCH);
+        double width = 7.5 * POINTS_PER_INCH;
+        AttributedString paragraphText = new AttributedString(printText);
+
+        LineBreakMeasurer lineBreaker = new LineBreakMeasurer(
+             paragraphText.getIterator(), new FontRenderContext(null, true, true));
+
+        //--- Create the TextLayout object
+        TextLayout layout;
+
+        //--- LineBreakMeasurer will wrap each line to correct length and
+        //--- return it as a TextLayout object
+        while ((layout = lineBreaker.nextLayout((float) width)) != null) {
+            //--- Align the Y pen to the ascend of the font, remember that
+            //--- the ascend is origin (0, 0) of a font. Refer to figure 1
+            pen.y += layout.getAscent();
+
+            //--- Draw the line of text
+            layout.draw(g2d, (float) pen.x, (float) pen.y);
+
+            //--- Move the pen to the next position adding the descent and
+            //--- the leading of the font
+            pen.y += layout.getDescent() + layout.getLeading();
+        }
+        /* -------------------------------------------- */
+
+        /*
+        int x = 5;
+        int y = 5;
+        for (String line : printText.split("\n"))
+            g.drawString(line, x, y += g.getFontMetrics().getHeight());
+        */
+
+        return PAGE_EXISTS;
+    }
+
+    /**
+     * Spawns standard JAVA-driven print dialog and prints text
+     */
+    public boolean printWithDialog(String key, String text) {
+        debugPrintService(null); // testing
+        printText = text;
+        PrinterJob job = PrinterJob.getPrinterJob();
+        job.setPrintable(this);
+        if (!job.printDialog()) return true; // print canceled by user
+        try {
+            job.print();
+        } catch (PrinterException ex) {
+            logger.warn("Error printing document for key " + key);
+            logger.warn(ex);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Print using defaults
+     *
+     * Sends the print job to the configured printer based on the key
+     * and user settings.
+     */
+    public boolean printWithoutDialog(String key, String text) {
+        printText = text;
+        PrinterJob job = PrinterJob.getPrinterJob();
+        job.setPrintable(this);
+
+        // TODO: load user settings, find the right printer, send the
+        // correct attributes, etc.
+
+        try {
+            job.print();
+        } catch (PrinterException ex) {
+            logger.warn("Error printing document for key " + key);
+            logger.warn(ex);
+            return false;
+        }
+        return true;
+    }
+
+    private void debugPrintService(PrintService printer) {
+
+        PrintService[] printServices;
+        String defaultPrinter = "";
+
+        if (printer != null) {
+            printServices = new PrintService[] {printer};
+        } else {
+            printServices = PrintServiceLookup.lookupPrintServices(null, null);
+            PrintService def = PrintServiceLookup.lookupDefaultPrintService();
+            if (def != null) defaultPrinter = def.getName();
+        }
+
+        for (PrintService service : printServices) {
+            logger.info("Printer Debug: found printer " + service.getName());
+            if (service.getName().equals(defaultPrinter)) {
+                logger.info("    Printer Debug: Is Default");
+            }
+
+            AttributeSet attributes = service.getAttributes();
+            for (Attribute a : attributes.toArray()) {
+                String name = a.getName();
+                String value = attributes.get(a.getClass()).toString();
+                logger.info("    Printer Debug: " + name + " => " + value);
+            }
+        }
+    }
+
+    public List<HashMap> getPrinters() {
+
+        List<HashMap> printers = new LinkedList<HashMap>();
+        PrintService[] printServices = 
+            PrintServiceLookup.lookupPrintServices(null, null);
+
+        String defaultPrinter = "";
+        PrintService def = PrintServiceLookup.lookupDefaultPrintService();
+        if (def != null) defaultPrinter = def.getName();
+
+        for (PrintService service : printServices) {
+            HashMap<String, Object> printer = new HashMap<String, Object>();
+            printers.add(printer);
+
+            if (service.getName().equals(defaultPrinter)) 
+                printer.put("is-default", new Boolean(true));
+
+            AttributeSet attributes = service.getAttributes();
+            for (Attribute a : attributes.toArray()) {
+                String name = a.getName();
+                String value = attributes.get(a.getClass()).toString();
+                printer.put(name, value);
+            }
+        }
+
+        return printers;
+    }
+
+    // experiment
+    // show our own print dialog before the real print action takes over
+    // currently just shows Print and Cancel.
+    // Not sure if there is a need for such a thing..
+    public void printWithCustomDialog(String key, String msg) {
+        UIManager.put("swing.boldMetal", Boolean.FALSE);
+        JFrame f = new JFrame("Hello World Printer");
+
+        // close the frame when Cancel / X are clicked
+        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+
+        final String printMsg = msg;
+        final String printKey = key;
+        JButton printButton = new JButton("Print '" + msg + "'");
+        printButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                printWithDialog(printKey, printMsg);
+            }
+        });
+
+        JButton cancelButton = new JButton("Cancel");
+        cancelButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                Component component = (Component) e.getSource();
+                JFrame frame = (JFrame) SwingUtilities.getRoot(component);
+                WindowEvent windowClosing = 
+                    new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
+                frame.dispatchEvent(windowClosing);
+            }
+        });
+
+        JPanel grid = new JPanel(new FlowLayout(FlowLayout.LEFT,3,3));
+        f.add(grid);
+        grid.add(cancelButton);
+        grid.add(printButton);
+        f.pack();
+        f.setVisible(true);
+   }
+}
+
+