From: erickson Date: Thu, 10 May 2007 19:56:08 +0000 (+0000) Subject: added JSON reading ability, including serializable objects X-Git-Tag: osrf_rel_2_0_1~1026 X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=abfda0487a357f7042a7864070133ef285dbb36c;p=OpenSRF.git added JSON reading ability, including serializable objects git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@882 9efc2488-bf62-4759-914b-345cdb29e865 --- 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/JSON.java b/src/java/org/opensrf/util/JSON.java deleted file mode 100644 index 9e17b5d..0000000 --- a/src/java/org/opensrf/util/JSON.java +++ /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 map = new HashMap(); - map.put(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); - - } else { - //map.put(JSON_PAYLOAD_KEY, new HashMap(obj)); - Map subMap = new HashMap(); - 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 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/JSONWriter.java b/src/java/org/opensrf/util/JSONWriter.java new file mode 100644 index 0000000..c5a653d --- /dev/null +++ b/src/java/org/opensrf/util/JSONWriter.java @@ -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 map = new HashMap(); + 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(JSONReader.JSON_PAYLOAD_KEY, list); + + } else { + + Map subMap = new HashMap(); + 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); + } +} + + + 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);