From abfda0487a357f7042a7864070133ef285dbb36c Mon Sep 17 00:00:00 2001 From: erickson Date: Thu, 10 May 2007 19:56:08 +0000 Subject: [PATCH] added JSON reading ability, including serializable objects git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@882 9efc2488-bf62-4759-914b-345cdb29e865 --- src/java/org/opensrf/Message.java | 5 + src/java/org/opensrf/Method.java | 4 +- src/java/org/opensrf/Result.java | 2 +- src/java/org/opensrf/Session.java | 42 +++++- src/java/org/opensrf/net/xmpp/XMPPReader.java | 2 +- src/java/org/opensrf/net/xmpp/XMPPSession.java | 2 +- src/java/org/opensrf/test/TestJSON.java | 12 +- src/java/org/opensrf/util/JSONReader.java | 162 +++++++++++++++++++++ .../opensrf/util/{JSON.java => JSONWriter.java} | 52 ++++--- src/java/org/opensrf/util/OSRFObject.java | 15 +- 10 files changed, 258 insertions(+), 40 deletions(-) create mode 100644 src/java/org/opensrf/util/JSONReader.java rename src/java/org/opensrf/util/{JSON.java => JSONWriter.java} (73%) diff --git a/src/java/org/opensrf/Message.java b/src/java/org/opensrf/Message.java index 441172a..f1db9c7 100644 --- a/src/java/org/opensrf/Message.java +++ b/src/java/org/opensrf/Message.java @@ -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; diff --git a/src/java/org/opensrf/Method.java b/src/java/org/opensrf/Method.java index b6dc7b4..cd31a9d 100644 --- a/src/java/org/opensrf/Method.java +++ b/src/java/org/opensrf/Method.java @@ -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 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(8); diff --git a/src/java/org/opensrf/Result.java b/src/java/org/opensrf/Result.java index a784899..4fd065b 100644 --- a/src/java/org/opensrf/Result.java +++ b/src/java/org/opensrf/Result.java @@ -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; diff --git a/src/java/org/opensrf/Session.java b/src/java/org/opensrf/Session.java index 59eef49..fca03fa 100644 --- a/src/java/org/opensrf/Session.java +++ b/src/java/org/opensrf/Session.java @@ -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 + sessionCache = new HashMap(); + /** 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); + } } diff --git a/src/java/org/opensrf/net/xmpp/XMPPReader.java b/src/java/org/opensrf/net/xmpp/XMPPReader.java index 9a9c332..3c06b11 100644 --- a/src/java/org/opensrf/net/xmpp/XMPPReader.java +++ b/src/java/org/opensrf/net/xmpp/XMPPReader.java @@ -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; diff --git a/src/java/org/opensrf/net/xmpp/XMPPSession.java b/src/java/org/opensrf/net/xmpp/XMPPSession.java index ccc85d2..68147aa 100644 --- a/src/java/org/opensrf/net/xmpp/XMPPSession.java +++ b/src/java/org/opensrf/net/xmpp/XMPPSession.java @@ -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; diff --git a/src/java/org/opensrf/test/TestJSON.java b/src/java/org/opensrf/test/TestJSON.java index e57f01e..75cb7eb 100644 --- a/src/java/org/opensrf/test/TestJSON.java +++ b/src/java/org/opensrf/test/TestJSON.java @@ -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 map = new HashMap(); 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/JSONReader.java b/src/java/org/opensrf/util/JSONReader.java new file mode 100644 index 0000000..85ce58a --- /dev/null +++ b/src/java/org/opensrf/util/JSONReader.java @@ -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 map = new HashMap(); + + 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 list = new ArrayList(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/JSON.java b/src/java/org/opensrf/util/JSONWriter.java similarity index 73% rename from src/java/org/opensrf/util/JSON.java rename to src/java/org/opensrf/util/JSONWriter.java index 9e17b5d..c5a653d 100644 --- a/src/java/org/opensrf/util/JSON.java +++ b/src/java/org/opensrf/util/JSONWriter.java @@ -3,34 +3,41 @@ package org.opensrf.util; import java.io.*; import java.util.*; + /** - * JSON utilities. + * JSONWriter */ -public class JSON { +public class JSONWriter { - /** Special OpenSRF serializable object netClass key */ - public static final String JSON_CLASS_KEY = "__c"; + /** The object to serialize to JSON */ + private Object obj; - /** Special OpenSRF serializable object payload key */ - public static final String JSON_PAYLOAD_KEY = "__p"; + public JSONWriter(Object obj) { + this.obj = obj; + } /** - * @see toJSON(Object, StringBuffer) + * @see write(Object, StringBuffer) */ - public static String toJSON(Object obj) { + public String write() { StringBuffer sb = new StringBuffer(); - toJSON(obj, sb); + 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 static void toJSON(Object obj, StringBuffer sb) { + public void write(StringBuffer sb) { + write(obj, sb); + } + + public void write(Object obj, StringBuffer sb) { /** JSON null */ if(obj == null) { @@ -81,14 +88,14 @@ public class JSON { /** * Encodes a List as a JSON array */ - private static void encodeJSONArray(Iterable iterable, StringBuffer sb) { + private void encodeJSONArray(Iterable iterable, StringBuffer sb) { Iterator itr = iterable.iterator(); sb.append("["); boolean some = false; while(itr.hasNext()) { some = true; - toJSON(itr.next(), sb); + write(itr.next(), sb); sb.append(','); } @@ -100,18 +107,18 @@ public class JSON { /** - * Encodes a Map to a JSON object + * Encodes a Map as a JSON object */ - private static void encodeJSONObject(Map map, StringBuffer sb) { + private 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); + write(key, sb); sb.append(':'); - toJSON(map.get(key), sb); + write(map.get(key), sb); sb.append(','); } @@ -125,30 +132,31 @@ public class JSON { /** * Encodes a network-serializable OpenSRF object */ - private static void encodeOSRFSerializable(OSRFSerializable obj, StringBuffer sb) { + private void encodeOSRFSerializable(OSRFSerializable obj, StringBuffer sb) { OSRFRegistry reg = obj.getRegistry(); String[] fields = reg.getFields(); Map map = new HashMap(); - map.put(JSON_CLASS_KEY, reg.getNetClass()); + map.put(JSONReader.JSON_CLASS_KEY, reg.getNetClass()); if( reg.getWireProtocol() == OSRFRegistry.WireProtocol.ARRAY ) { List list = new ArrayList(fields.length); for(String s : fields) list.add(obj.get(s)); - map.put(JSON_PAYLOAD_KEY, list); + map.put(JSONReader.JSON_PAYLOAD_KEY, list); } else { - //map.put(JSON_PAYLOAD_KEY, new HashMap(obj)); + Map subMap = new HashMap(); for(String s : fields) - map.put(s, obj.get(s)); + subMap.put(s, obj.get(s)); + map.put(JSONReader.JSON_PAYLOAD_KEY, subMap); } /** now serialize the encoded object */ - toJSON(map, sb); + write(map, sb); } } diff --git a/src/java/org/opensrf/util/OSRFObject.java b/src/java/org/opensrf/util/OSRFObject.java index 0f2c7af..6ea0db3 100644 --- a/src/java/org/opensrf/util/OSRFObject.java +++ b/src/java/org/opensrf/util/OSRFObject.java @@ -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 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 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); -- 2.11.0