added JSON reading ability, including serializable objects
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 10 May 2007 19:56:08 +0000 (19:56 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Thu, 10 May 2007 19:56:08 +0000 (19:56 +0000)
git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@882 9efc2488-bf62-4759-914b-345cdb29e865

src/java/org/opensrf/Message.java
src/java/org/opensrf/Method.java
src/java/org/opensrf/Result.java
src/java/org/opensrf/Session.java
src/java/org/opensrf/net/xmpp/XMPPReader.java
src/java/org/opensrf/net/xmpp/XMPPSession.java
src/java/org/opensrf/test/TestJSON.java
src/java/org/opensrf/util/JSON.java [deleted file]
src/java/org/opensrf/util/JSONReader.java [new file with mode: 0644]
src/java/org/opensrf/util/JSONWriter.java [new file with mode: 0644]
src/java/org/opensrf/util/OSRFObject.java

index 441172a..f1db9c7 100644 (file)
@@ -35,6 +35,11 @@ public class Message implements OSRFSerializable {
         setId(id);
         setType(type);
     }
+    public Message(int id, Type type, Object payload) {
+        this(id, type);
+        setPayload(payload);
+    }
+
 
     public int getId() {
         return id;
index b6dc7b4..cd31a9d 100644 (file)
@@ -4,19 +4,17 @@ import java.util.ArrayList;
 import org.opensrf.util.*;
 
 
-public class Method implements OSRFSerializable {
+public class Method extends OSRFObject {
 
     private String name;
     private List<Object> params;
 
-    /** Register this object */
     private static OSRFRegistry registry = 
         OSRFRegistry.registerObject(
             "osrfMethod", 
             OSRFRegistry.WireProtocol.HASH, 
             new String[] {"method", "params"});
 
-
     public Method(String name) {
         this.name = name;
         this.params = new ArrayList<Object>(8);
index a784899..4fd065b 100644 (file)
@@ -5,7 +5,7 @@ import org.opensrf.util.*;
 /**
  * Models a single result from a method request.
  */
-public class Result {
+public class Result implements OSRFSerializable {
 
     /** Method result content */
     private Object content;
index 59eef49..fca03fa 100644 (file)
@@ -1,6 +1,8 @@
 package org.opensrf;
-import org.opensrf.util.JSON;
+import org.opensrf.util.JSONWriter;
 import org.opensrf.net.xmpp.*;
+import java.util.Map;
+import java.util.HashMap;
 
 public abstract class Session {
 
@@ -11,6 +13,10 @@ public abstract class Session {
         CONNECTED
     };
 
+    /** local cache of existing sessions */
+    private static Map<String, Session> 
+        sessionCache = new HashMap<String, Session>();
+
     /** the current connection state */
     private ConnectState connectState;
 
@@ -37,14 +43,44 @@ public abstract class Session {
         XMPPMessage xmsg = new XMPPMessage();
         xmsg.setTo(remoteNode);
         xmsg.setThread(thread);
-        xmsg.setBody(JSON.toJSON(omsg));
+        xmsg.setBody(new JSONWriter(omsg).write());
         XMPPSession ses = XMPPSession.getGlobalSession();
 
         try {
             XMPPSession.getGlobalSession().send(xmsg);
         } catch(XMPPException e) {
-            /* XXX log */
+            /* XXX log.. what else? */
             connectState = ConnectState.DISCONNECTED;
         }
     }
+
+    /**
+     * Waits for a message to arrive over the network and passes
+     * all received messages to the stack for processing
+     * @param millis The number of milliseconds to wait for a message to arrive
+     */
+    public static void waitForMessage(long millis) {
+        try {
+            Stack.processXMPPMessage(
+                XMPPSession.getGlobalSession().recv(millis));
+        } catch(XMPPException e) {
+            /* XXX log.. what else? */
+        }
+    }
+
+    /**
+     * Removes this session from the session cache.
+     */
+    public void cleanup() {
+        sessionCache.remove(thread);
+    }
+
+    /**
+     * Searches for the cached session with the given thread.
+     * @param thread The session thread.
+     * @return The found session or null.
+     */
+    public static Session findCachedSession(String thread) {
+        return sessionCache.get(thread);
+    }
 }
index 9a9c332..3c06b11 100644 (file)
@@ -130,7 +130,7 @@ public class XMPPReader implements Runnable {
      * timeout is negative, waits potentially forever.
      * @return The number of milliseconds in wait
      */
-    public synchronized long waitCoreEvent(int timeout) {
+    public synchronized long waitCoreEvent(long timeout) {
 
         if(msgQueue.peek() != null || timeout == 0) return 0;
 
index ccc85d2..68147aa 100644 (file)
@@ -161,7 +161,7 @@ public class XMPPSession {
      * If timeout is 0, this method will not block at all, but will return a 
      * message if there is already a message available.
      */
-    public XMPPMessage recv(int timeout) throws XMPPException {
+    public XMPPMessage recv(long timeout) throws XMPPException {
 
         XMPPMessage msg;
 
index e57f01e..75cb7eb 100644 (file)
@@ -6,7 +6,7 @@ import java.util.*;
 
 public class TestJSON {
 
-    public static void main(String args[]) {
+    public static void main(String args[]) throws Exception {
         
         Map<String,Object> map = new HashMap<String,Object>();
         map.put("key1", "value1");
@@ -22,7 +22,7 @@ public class TestJSON {
         list.add(null);
         map.put("key6", list);
 
-        System.out.println(JSON.toJSON(map) + "\n");
+        System.out.println(new JSONWriter(map).write() + "\n");
 
         String[] fields = {"isnew", "name", "shortname", "ill_address"};
         OSRFRegistry.registerObject("aou", OSRFRegistry.WireProtocol.ARRAY, fields);
@@ -34,7 +34,7 @@ public class TestJSON {
 
         map.put("key7", obj);
         list.add(obj);
-        System.out.println(JSON.toJSON(map) + "\n");
+        System.out.println(new JSONWriter(map).write() + "\n");
 
 
         Message m = new Message(1, Message.Type.REQUEST);
@@ -42,6 +42,10 @@ public class TestJSON {
         method.addParam("app07.dev.gapines.org");
         m.setPayload(method);
 
-        System.out.println(JSON.toJSON(m) + "\n");
+        String s = new JSONWriter(m).write();
+        System.out.println(s + "\n");
+
+        Object o = new JSONReader(s).read();
+        System.out.println("Read+Wrote: " + new JSONWriter(o).write());
     }
 }
diff --git a/src/java/org/opensrf/util/JSON.java b/src/java/org/opensrf/util/JSON.java
deleted file mode 100644 (file)
index 9e17b5d..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-package org.opensrf.util;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * JSON utilities.
- */
-public class JSON {
-
-    /** Special OpenSRF serializable object netClass key */
-    public static final String JSON_CLASS_KEY = "__c";
-
-    /** Special OpenSRF serializable object payload key */
-    public static final String JSON_PAYLOAD_KEY = "__p";
-
-
-    /**
-     * @see toJSON(Object, StringBuffer)
-     */
-    public static String toJSON(Object obj) {
-        StringBuffer sb = new StringBuffer();
-        toJSON(obj, sb);
-        return sb.toString();
-    }
-
-
-    /**
-     * Encodes a java object to JSON.
-     * Maps (HashMaps, etc.) are encoded as JSON objects.  
-     * Iterable's (Lists, etc.) are encoded as JSON arrays
-     */
-    public static void toJSON(Object obj, StringBuffer sb) {
-
-        /** JSON null */
-        if(obj == null) {
-            sb.append("null");
-            return;
-        }
-
-        /** JSON string */
-        if(obj instanceof String) {
-            sb.append('"');
-            Utils.escape((String) obj, sb);
-            sb.append('"');
-            return;
-        }
-
-        /** JSON number */
-        if(obj instanceof Number) {
-            sb.append(obj.toString());
-            return;
-        }
-
-        /** JSON array */
-        if(obj instanceof Iterable) {
-            encodeJSONArray((Iterable) obj, sb);
-            return;
-        }
-
-        /** OpenSRF serializable objects */
-        if(obj instanceof OSRFSerializable) {
-            encodeOSRFSerializable((OSRFSerializable) obj, sb);
-            return;
-        }
-
-        /** JSON object */
-        if(obj instanceof Map) {
-            encodeJSONObject((Map) obj, sb);
-            return;
-        }
-
-        /** JSON boolean */
-        if(obj instanceof Boolean) {
-            sb.append((((Boolean) obj).booleanValue() ? "true" : "false"));
-            return;
-        }
-    }
-
-
-    /**
-     * Encodes a List as a JSON array
-     */
-    private static void encodeJSONArray(Iterable iterable, StringBuffer sb) {
-        Iterator itr = iterable.iterator();
-        sb.append("[");
-        boolean some = false;
-
-        while(itr.hasNext()) {
-            some = true;
-            toJSON(itr.next(), sb);
-            sb.append(',');
-        }
-
-        /* remove the trailing comma if the array has any items*/
-        if(some) 
-            sb.deleteCharAt(sb.length()-1); 
-        sb.append("]");
-    }
-
-
-    /**
-     * Encodes a Map to a JSON object
-     */
-    private static void encodeJSONObject(Map map, StringBuffer sb) {
-        Iterator itr = map.keySet().iterator();
-        sb.append("{");
-        Object key = null;
-
-        while(itr.hasNext()) {
-            key = itr.next();
-            toJSON(key, sb);
-            sb.append(':');
-            toJSON(map.get(key), sb);
-            sb.append(',');
-        }
-
-        /* remove the trailing comma if the object has any items*/
-        if(key != null) 
-            sb.deleteCharAt(sb.length()-1); 
-        sb.append("}");
-    }
-
-
-    /**
-     * Encodes a network-serializable OpenSRF object
-     */
-    private static void encodeOSRFSerializable(OSRFSerializable obj, StringBuffer sb) {
-
-        OSRFRegistry reg = obj.getRegistry();
-        String[] fields = reg.getFields();
-        Map<String, Object> map = new HashMap<String, Object>();
-        map.put(JSON_CLASS_KEY, reg.getNetClass());
-
-        if( reg.getWireProtocol() == OSRFRegistry.WireProtocol.ARRAY ) {
-
-            List<Object> list = new ArrayList<Object>(fields.length);
-            for(String s : fields)
-                list.add(obj.get(s));
-            map.put(JSON_PAYLOAD_KEY, list);
-
-        } else {
-            //map.put(JSON_PAYLOAD_KEY, new HashMap<String, Object>(obj));
-            Map<String, Object> subMap = new HashMap<String, Object>();
-            for(String s : fields)
-                map.put(s, obj.get(s));
-                
-        }
-
-        /** now serialize the encoded object */
-        toJSON(map, sb);
-    }
-}
-
-
-
diff --git a/src/java/org/opensrf/util/JSONReader.java b/src/java/org/opensrf/util/JSONReader.java
new file mode 100644 (file)
index 0000000..85ce58a
--- /dev/null
@@ -0,0 +1,162 @@
+package org.opensrf.util;
+
+import java.io.*;
+import java.util.*;
+
+import org.json.JSONTokener;
+import org.json.JSONObject;
+import org.json.JSONArray;
+
+
+/**
+ * JSON utilities.
+ */
+public class JSONReader {
+
+    /** Special OpenSRF serializable object netClass key */
+    public static final String JSON_CLASS_KEY = "__c";
+
+    /** Special OpenSRF serializable object payload key */
+    public static final String JSON_PAYLOAD_KEY = "__p";
+
+    private String json;
+
+    public JSONReader(String json) {
+        this.json = json;
+    }
+
+    /**
+     * Parses JSON and creates an object.
+     * @param json The JSON string to parse
+     * @return The resulting object which may be a List, 
+     * Map, Number, String, Boolean, or null
+     */
+    public Object read() throws JSONException {
+        JSONTokener tk = new JSONTokener(json);
+        try {
+            return readSubObject(tk.nextValue());
+        } catch(org.json.JSONException e) {
+            throw new JSONException(e.toString());
+        }
+    }
+
+    public List<?> readArray() throws JSONException {
+        Object o = read();
+        try {
+            return (List<?>) o;
+        } catch(Exception e) {
+            throw new JSONException("readArray(): JSON cast exception");
+        }
+    }
+
+    public Map<?,?> readObject() throws JSONException {
+        Object o = read();
+        try {
+            return (Map<?,?>) o;
+        } catch(Exception e) {
+            throw new JSONException("readObject(): JSON cast exception");
+        }
+    }
+
+
+    private Object readSubObject(Object obj) throws JSONException {
+
+        if( obj == null || 
+            obj instanceof String || 
+            obj instanceof Number ||
+            obj instanceof Boolean)
+                return obj;
+
+        try {
+
+            if( obj instanceof JSONObject ) {
+
+                /* read objects */
+                String key;
+                JSONObject jobj = (JSONObject) obj;
+                Map<String, Object> map = new HashMap<String, Object>();
+
+                for( Iterator e = jobj.keys(); e.hasNext(); ) {
+                    key = (String) e.next();
+
+                    /* we encoutered the special class key */
+                    if( JSON_CLASS_KEY.equals(key) ) 
+                        return buildRegisteredObject(
+                            (String) jobj.get(key), jobj.get(JSON_PAYLOAD_KEY));
+
+                    /* we encountered the data key */
+                    if( JSON_PAYLOAD_KEY.equals(key) ) 
+                        return buildRegisteredObject(
+                            (String) jobj.get(JSON_CLASS_KEY), jobj.get(key));
+
+                    map.put(key, readSubObject(jobj.get(key)));
+                }
+                return map;
+            } 
+            
+            if ( obj instanceof JSONArray ) {
+
+                JSONArray jarr = (JSONArray) obj;
+                int length = jarr.length();
+                List<Object> list = new ArrayList<Object>(length);
+
+                for( int i = 0; i < length; i++ ) 
+                    list.add(readSubObject(jarr.get(i)));   
+                return list;
+                
+            }
+
+        } catch(org.json.JSONException e) {
+
+            throw new JSONException(e.toString());
+        }
+
+        return null;
+    }
+
+
+
+    /**
+     * Builds an OSRFObject map registered OSRFHash object based on the JSON object data.
+     * @param netClass The network class hint for this object.
+     * @param paylaod The actual object on the wire.
+     */
+    private OSRFObject buildRegisteredObject(
+        String netClass, Object payload) throws JSONException {
+
+        OSRFRegistry registry = OSRFRegistry.getRegistry(netClass);
+        OSRFObject obj = new OSRFObject(registry);
+
+        try {
+            if( payload instanceof JSONArray ) {
+                JSONArray jarr = (JSONArray) payload;
+
+                /* for each array item, instert the item into the hash.  the hash 
+                 * key is found by extracting the fields array from the registered 
+                 * object at the current array index */
+                String fields[] = registry.getFields();
+                for( int i = 0; i < jarr.length(); i++ ) {
+                    obj.put(fields[i], readSubObject(jarr.get(i)));   
+                }
+
+            } else if( payload instanceof JSONObject ) {
+
+                /* since this is a hash, simply copy the data over */
+                JSONObject jobj = (JSONObject) payload;
+                String key;
+                for( Iterator e = jobj.keys(); e.hasNext(); ) {
+                    key = (String) e.next();
+                    obj.put(key, readSubObject(jobj.get(key)));
+                }
+            }
+
+        } catch(org.json.JSONException e) {
+            throw new JSONException(e.toString());
+        }
+
+        return obj;
+    }
+}
+
+
+
diff --git a/src/java/org/opensrf/util/JSONWriter.java b/src/java/org/opensrf/util/JSONWriter.java
new file mode 100644 (file)
index 0000000..c5a653d
--- /dev/null
@@ -0,0 +1,164 @@
+package org.opensrf.util;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * JSONWriter
+ */
+public class JSONWriter {
+
+    /** The object to serialize to JSON */
+    private Object obj;
+
+    public JSONWriter(Object obj) {
+        this.obj = obj;
+    }
+
+
+    /**
+     * @see write(Object, StringBuffer)
+     */
+    public String write() {
+        StringBuffer sb = new StringBuffer();
+        write(sb);
+        return sb.toString();
+    }
+
+
+
+    /**
+     * Encodes a java object to JSON.
+     * Maps (HashMaps, etc.) are encoded as JSON objects.  
+     * Iterable's (Lists, etc.) are encoded as JSON arrays
+     */
+    public void write(StringBuffer sb) {
+        write(obj, sb);
+    }
+
+    public void write(Object obj, StringBuffer sb) {
+
+        /** JSON null */
+        if(obj == null) {
+            sb.append("null");
+            return;
+        }
+
+        /** JSON string */
+        if(obj instanceof String) {
+            sb.append('"');
+            Utils.escape((String) obj, sb);
+            sb.append('"');
+            return;
+        }
+
+        /** JSON number */
+        if(obj instanceof Number) {
+            sb.append(obj.toString());
+            return;
+        }
+
+        /** JSON array */
+        if(obj instanceof Iterable) {
+            encodeJSONArray((Iterable) obj, sb);
+            return;
+        }
+
+        /** OpenSRF serializable objects */
+        if(obj instanceof OSRFSerializable) {
+            encodeOSRFSerializable((OSRFSerializable) obj, sb);
+            return;
+        }
+
+        /** JSON object */
+        if(obj instanceof Map) {
+            encodeJSONObject((Map) obj, sb);
+            return;
+        }
+
+        /** JSON boolean */
+        if(obj instanceof Boolean) {
+            sb.append((((Boolean) obj).booleanValue() ? "true" : "false"));
+            return;
+        }
+    }
+
+
+    /**
+     * Encodes a List as a JSON array
+     */
+    private void encodeJSONArray(Iterable iterable, StringBuffer sb) {
+        Iterator itr = iterable.iterator();
+        sb.append("[");
+        boolean some = false;
+
+        while(itr.hasNext()) {
+            some = true;
+            write(itr.next(), sb);
+            sb.append(',');
+        }
+
+        /* remove the trailing comma if the array has any items*/
+        if(some) 
+            sb.deleteCharAt(sb.length()-1); 
+        sb.append("]");
+    }
+
+
+    /**
+     * Encodes a Map as a JSON object
+     */
+    private void encodeJSONObject(Map map, StringBuffer sb) {
+        Iterator itr = map.keySet().iterator();
+        sb.append("{");
+        Object key = null;
+
+        while(itr.hasNext()) {
+            key = itr.next();
+            write(key, sb);
+            sb.append(':');
+            write(map.get(key), sb);
+            sb.append(',');
+        }
+
+        /* remove the trailing comma if the object has any items*/
+        if(key != null) 
+            sb.deleteCharAt(sb.length()-1); 
+        sb.append("}");
+    }
+
+
+    /**
+     * Encodes a network-serializable OpenSRF object
+     */
+    private void encodeOSRFSerializable(OSRFSerializable obj, StringBuffer sb) {
+
+        OSRFRegistry reg = obj.getRegistry();
+        String[] fields = reg.getFields();
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put(JSONReader.JSON_CLASS_KEY, reg.getNetClass());
+
+        if( reg.getWireProtocol() == OSRFRegistry.WireProtocol.ARRAY ) {
+
+            List<Object> list = new ArrayList<Object>(fields.length);
+            for(String s : fields)
+                list.add(obj.get(s));
+            map.put(JSONReader.JSON_PAYLOAD_KEY, list);
+
+        } else {
+
+            Map<String, Object> subMap = new HashMap<String, Object>();
+            for(String s : fields)
+                subMap.put(s, obj.get(s));
+            map.put(JSONReader.JSON_PAYLOAD_KEY, subMap);
+                
+        }
+
+        /** now serialize the encoded object */
+        write(map, sb);
+    }
+}
+
+
+
index 0f2c7af..6ea0db3 100644 (file)
@@ -1,5 +1,6 @@
 package org.opensrf.util;
 
+import java.util.Map;
 import java.util.HashMap;
 
 
@@ -15,6 +16,14 @@ public class OSRFObject extends HashMap<String, Object> implements OSRFSerializa
     public OSRFObject() {
     }
 
+
+    /*
+    public OSRFObject(String netClass, Map map) {
+        super(map);
+        registry = OSRFRegistry.getRegistry(netClass);
+    }
+    */
+
     /**
      * Creates a new object with the provided registry
      */
@@ -31,12 +40,8 @@ public class OSRFObject extends HashMap<String, Object> implements OSRFSerializa
         return registry;
     }
 
-
     /**
-     * Gets the object at the given fields.  We override this here
-     * as part of the contract with OSRFSerializable
-     * @param field the field name to get.
-     * @return The object contained at the given field.
+     * Implement get() to fulfill our contract with OSRFSerializable
      */
     public Object get(String field) {
         return super.get(field);