Class JsonNioService

  • All Implemented Interfaces:
    JsonNioServer

    public class JsonNioService
    extends naga.NIOService
    implements JsonNioServer
    A class for interacting with Jmol over local sockets. See also org.molecularplayground.MPJmolApp.java for how this works. Note that this service does not require MPJmolApp -- it is a package in the standard Jmol app. Listens over a port on the local host for instructions on what to display. Instructions come in over the port as JSON strings. This class uses the Naga asynchronous socket network I/O package (NIO), the JSON.org JSON package and Jmol. http://code.google.com/p/naga/ Initial versions of this code, including the JSON-base protocol were created by Adam Williams, U-Mass Amherst see http://MolecularPlayground.org and org.openscience.jmol.molecularplayground.MPJmolApp.java Sequence of events: 1) Jmol initiates server listening on a port using the JmolScript command with an arbitrary negative port number. (-30000 used here just for an example): sync -30000 This can be done also through the command line using jmol -P -30000 or jmol --port -30000 Jmol will respond to System.out: JsonNioServerThread-JmolNioServer JsonNioServerSocket on 30000 2) Client sends handshake to port 30000. As with all communications to this service, there must be no new-line characters (\n) ANYWHERE in the JSON being sent EXCEPT for a single message terminator: {"magic": "JmolApp", "role": "out"}\n where "out" here indicates that this socket is for Jmol (reply) output. Jmol will reply with the 30-byte response: {"type":"reply","reply":"OK"}\n (The client may see only 29 bytes, as it may or may not strip the final \n.) Optionally, the client may also indicate a specified port for Jmol input. But typically this is just the currently active port. {"magic": "JmolApp", "role": "in"}\n Jmol will reply with {"type": "reply", "reply": "OK"}\n; 3) Client sequentially sends Jmol script commands over the "in" socket: {"type": "command", "command": command}\n where required command is some JSON-escaped string such as "rotate x 30" or "load $caffeine". For example: {"type": "command", "command": "var atoms = {_C or _H};select atoms"}\n For the rest of this discussion, we will use the Jmol command that communicates with another Jmol instance rather than this JSON context: SYNC 30000 "var atoms = {_C or _H};select atoms" in this case. 4) Jmol throughout this process is sending replies that come from the Jmol Statuslistener class. For example: {"type":"reply","reply":"SCRIPT:script 8 started"} {"type":"reply","reply":"SCRIPT:Script completed"} {"type":"reply","reply":"SCRIPT:Jmol script terminated"} Note that your client will be subscribed to many of the Jmol status callbacks (see org.openscience.jmol.app.jmolpanel.StatusListener), including: LOADSTRUCT ANIMFRAME SCRIPT ECHO PICK CLICK RESIZE ERROR MINIMIZATION STRUCTUREMODIFIED All scripts and callback messages run in order but asynchronously in Jmol. You do not need to wait for one script to be finished before issuing another; there is a queue that handles that. If you want to be sure that a particular script has been run, simply add a MESSAGE command as its last part: sync 30000 "background blue;message The background is blue now" and it will appear as a SCRIPT callback: {"type":"reply","reply":"SCRIPT:The background is blue now"} after which you can handle that event appropriately. The SCRIPT callback can be particularly useful to monitor: sync 30000 "backgrund blue" {"type":"reply","reply":"SCRIPT:script compiler ERROR: command expected\n----\n >>>> backgrund blue <<<<"} Note that the ERROR callback does not fire for compile errors, only for errors found while running a parsed script: {"type":"reply","reply":"ERROR:ScriptException"} Note that all of these messages are "thumbnails" in the sense that they are just a message string. You can subscribe to a full report for any of these callbacks using 'SYNC:ON' for the callback function: set XxxxxCallback SYNC:ON For example, issuing sync 30000 "load $caffeine" gives the simple reply: {"type":"reply","reply":"LOADSTRUCT:https://cactus.nci.nih.gov/chemical/structure/caffeine/file?format=sdf&get3d=true"} but after sync 30000 "set LoadStructCallback 'SYNC:ON' we get additional details, and array of data with nine elements: {"type":"reply","reply":["LOADSTRUCT", "https://cactus.nci.nih.gov/chemical/structure/caffeine/file?format=sdf&get3d=true", "file?format=sdf&get3d=true", "C8H10N4O2", null, 3, "1.1", "1.1", null]} Exact specifications for these callbacks are not well documented. See org.jmol.viewer.StatusManager code for details. Remove the callback listener using set XxxxxCallback SYNC:OFF Note that unlike Java, you get only one SYNC callback; this is not an array of listeners. 5) Shutdown can be requested by sending {"type": "quit"}\n or by issuing the command sync 30000 "exitjmol" Note that the Molecular Playgournd implemented an extensive set of gesture-handling methods that are also available via this interface. Many of these methods utilize the JmolViewer.syncScript() method, which directly manipulates the display as though someone were using a mouse. {"type" : "move", "style" : "rotate", "x" : deltaX, "y", deltaY } {"type" : "move", "style" : "translate", "x" : deltaX, "y", deltaY } {"type" : "move", "style" : "zoom", "scale" : scale } (1.0 = 100%) {"type" : "sync", "sync" : syncText } {"type" : "touch", "eventType" : eventType, "touchID" : touchID, "iData" : idata, "time" : time, "x" : x, "y" : y, "z" : z } For details on the "touch" type, see org.jmol.viewer.ActionManagerMT::processEvent Note that all of the move and sync commands utilize the Jmol sync functionality originally intended for applets. So any valid sync command may be used with the "sync" style. These include essentially all the actions that a user can make with a mouse, including the following, where the notation <....> represents a number of a given type. These events interrupt any currently running script, just as with typical mouse actions. "centerAt " -- set {ptx,pty,ptz} at screen (x,y) "rotateMolecule " "rotateXYBy " "rotateZBy " "rotateZBy " (with center reset) "rotateArcBall " "spinXYBy " -- a "flick" gesture "translateXYBy " "zoomBy " "zoomByFactor " "zoomByFactor " (with center reset) In addition, a Jmol client send "raw" JSON strings over the socket via the SYNC command: sync 30000 '{"type": "command", "command": "var atoms = {_C or _H};select atoms"}' and since JmolScript's associative array is equivalent to JSON, this message does not have to be a string; it can be an associative array: sync 30000 {"type":"command","command":"background orange"} Even simpler, Jmol's native associative array uses [...] instead of {...} and does not require quoting keys (unless they contain spaces): sync 30000 [type:"command", command:"background orange"] And, finally, the message can be in the form of a JmolScript variable: x = [type:"command", command:"background orange"] sync 30000 x
    • Constructor Summary

      Constructors 
      Constructor Description
      JsonNioService()  
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      private byte[] clean​(byte[] out)
      remove all new-line characters, and terminate this message with a single \n.
      void close()  
      static double getDouble​(java.util.Map<java.lang.String,​java.lang.Object> map, java.lang.String key)  
      static int getInt​(java.util.Map<java.lang.String,​java.lang.Object> map, java.lang.String key)  
      static long getLong​(java.util.Map<java.lang.String,​java.lang.Object> map, java.lang.String key)  
      int getPort()  
      static java.lang.String getString​(java.util.Map<java.lang.String,​java.lang.Object> map, java.lang.String key)  
      boolean hasOuputSocket()  
      protected void initialize​(java.lang.String role, naga.NIOSocket nioSocket)  
      protected void processMessage​(byte[] packet, naga.NIOSocket socket)  
      void reply​(int port, java.lang.Object data)  
      private void sendBytes​(byte[] bytes, naga.NIOSocket socket)  
      protected void sendMessage​(java.util.Map<java.lang.String,​java.lang.Object> map, java.lang.String msg, naga.NIOSocket socket)  
      void sendToJmol​(int port, java.lang.String msg)
      send the message - not for replies.
      private void startServerService()  
      void startService​(int port, JsonNioClient client, Viewer jmolViewer, java.lang.String name, int version)  
      static byte[] toJSONBytes​(java.util.Map<java.lang.String,​java.lang.Object> map)
      Guaranteed to create a clean no-whitespace JSON stream terminated by a single \n.
      static java.util.Map<java.lang.String,​java.lang.Object> toMap​(byte[] packet)  
      • Methods inherited from class naga.NIOService

        getBufferSize, getQueue, isOpen, notifyException, openServerSocket, openServerSocket, openServerSocket, openSocket, openSocket, openSSLServerSocket, openSSLServerSocket, openSSLServerSocket, openSSLSocket, openSSLSocket, queue, selectBlocking, selectBlocking, selectNonBlocking, setBufferSize, setExceptionObserver, wakeup
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • myName

        protected java.lang.String myName
      • halt

        protected boolean halt
      • port

        protected int port
      • clientThread

        private java.lang.Thread clientThread
      • serverThread

        private java.lang.Thread serverThread
      • inSocket

        private naga.NIOSocket inSocket
      • outSocket

        protected naga.NIOSocket outSocket
      • serverSocket

        private naga.NIOServerSocket serverSocket
      • version

        protected int version
    • Constructor Detail

      • JsonNioService

        public JsonNioService()
                       throws java.io.IOException
        Throws:
        java.io.IOException
    • Method Detail

      • startService

        public void startService​(int port,
                                 JsonNioClient client,
                                 Viewer jmolViewer,
                                 java.lang.String name,
                                 int version)
                          throws java.io.IOException
        Specified by:
        startService in interface JsonNioServer
        Throws:
        java.io.IOException
      • sendToJmol

        public void sendToJmol​(int port,
                               java.lang.String msg)
        send the message - not for replies.
        Specified by:
        sendToJmol in interface JsonNioServer
      • processMessage

        protected void processMessage​(byte[] packet,
                                      naga.NIOSocket socket)
      • sendMessage

        protected void sendMessage​(java.util.Map<java.lang.String,​java.lang.Object> map,
                                   java.lang.String msg,
                                   naga.NIOSocket socket)
      • reply

        public void reply​(int port,
                          java.lang.Object data)
        Specified by:
        reply in interface JsonNioServer
      • sendBytes

        private void sendBytes​(byte[] bytes,
                               naga.NIOSocket socket)
      • clean

        private byte[] clean​(byte[] out)
        remove all new-line characters, and terminate this message with a single \n.
        Parameters:
        out -
        Returns:
        cleaned bytes
      • close

        public void close()
        Specified by:
        close in interface JsonNioServer
        Overrides:
        close in class naga.NIOService
      • initialize

        protected void initialize​(java.lang.String role,
                                  naga.NIOSocket nioSocket)
      • startServerService

        private void startServerService()
      • toJSONBytes

        public static byte[] toJSONBytes​(java.util.Map<java.lang.String,​java.lang.Object> map)
        Guaranteed to create a clean no-whitespace JSON stream terminated by a single \n.
        Parameters:
        map -
        Returns:
        clean bytes
      • toMap

        public static java.util.Map<java.lang.String,​java.lang.Object> toMap​(byte[] packet)
      • getString

        public static java.lang.String getString​(java.util.Map<java.lang.String,​java.lang.Object> map,
                                                 java.lang.String key)
      • getLong

        public static long getLong​(java.util.Map<java.lang.String,​java.lang.Object> map,
                                   java.lang.String key)
                            throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getInt

        public static int getInt​(java.util.Map<java.lang.String,​java.lang.Object> map,
                                 java.lang.String key)
                          throws java.lang.Exception
        Throws:
        java.lang.Exception
      • getDouble

        public static double getDouble​(java.util.Map<java.lang.String,​java.lang.Object> map,
                                       java.lang.String key)
                                throws java.lang.Exception
        Throws:
        java.lang.Exception