From d4167cf01d12034a0da5ec2e64909e995d606311 Mon Sep 17 00:00:00 2001 From: erickson Date: Thu, 10 May 2007 02:51:51 +0000 Subject: [PATCH] added initial opensrf stack layer objects. more tuning/testing of the jabber layer git-svn-id: svn://svn.open-ils.org/OpenSRF/trunk@880 9efc2488-bf62-4759-914b-345cdb29e865 --- src/java/org/opensrf/Message.java | 50 ++++++++ src/java/org/opensrf/Method.java | 36 ++++++ src/java/org/opensrf/MethodException.java | 11 ++ src/java/org/opensrf/Result.java | 70 +++++++++++ src/java/org/opensrf/Session.java | 50 ++++++++ .../org/opensrf/net/xmpp/XMPPException.java | 10 +- src/java/org/opensrf/net/xmpp/XMPPReader.java | 5 +- .../org/opensrf/net/xmpp/XMPPSession.java | 16 ++- src/java/org/opensrf/test/TestJSON.java | 26 ++++ src/java/org/opensrf/util/JSON.java | 117 ++++++++++++++++++ src/java/org/opensrf/util/Utils.java | 86 +++++++++++++ 11 files changed, 465 insertions(+), 12 deletions(-) create mode 100644 src/java/org/opensrf/Message.java create mode 100644 src/java/org/opensrf/Method.java create mode 100644 src/java/org/opensrf/MethodException.java create mode 100644 src/java/org/opensrf/Result.java create mode 100644 src/java/org/opensrf/Session.java create mode 100644 src/java/org/opensrf/test/TestJSON.java create mode 100644 src/java/org/opensrf/util/JSON.java create mode 100644 src/java/org/opensrf/util/Utils.java diff --git a/src/java/org/opensrf/Message.java b/src/java/org/opensrf/Message.java new file mode 100644 index 0000000..798839a --- /dev/null +++ b/src/java/org/opensrf/Message.java @@ -0,0 +1,50 @@ +package org.opensrf; + + +public class Message { + + /** Message types */ + public enum Type { + REQUEST, + STATUS, + RESULT, + CONNECT, + DISCONNECT, + }; + + /** Message ID. This number is used to relate requests to responses */ + private int id; + /** Type of message. */ + private Type type; + /** message payload */ + private Object payload; + + /** + * @param id This message's ID + * @param type The type of message + */ + public Message(int id, Type type) { + setId(id); + setType(type); + } + public int getId() { + return id; + } + public Type getType() { + return type; + } + public Object getPayload() { + return payload; + } + public void setId(int id) { + this.id = id; + } + public void setType(Type type) { + this.type = type; + } + public void setPayload(Object p) { + payload = p; + } +} + + diff --git a/src/java/org/opensrf/Method.java b/src/java/org/opensrf/Method.java new file mode 100644 index 0000000..97a5ba3 --- /dev/null +++ b/src/java/org/opensrf/Method.java @@ -0,0 +1,36 @@ +package org.opensrf; +import java.util.List; +import java.util.ArrayList; + + +public class Method { + + private String name; + private List params; + + public Method(String name) { + this.name = name; + this.params = new ArrayList(8); + } + + public Method(String name, List params) { + this.name = name; + this.params = params; + } + + public String getName() { + return name; + } + public List getParams() { + return params; + } + + /** + * Pushes a new param object onto the set of params + * @param p The new param to add to the method. + */ + public void pushParam(Object p) { + this.params.add(p); + } +} + diff --git a/src/java/org/opensrf/MethodException.java b/src/java/org/opensrf/MethodException.java new file mode 100644 index 0000000..bf47313 --- /dev/null +++ b/src/java/org/opensrf/MethodException.java @@ -0,0 +1,11 @@ +package org.opensrf; + +/** + * Thrown when the server responds with a method exception. + */ +public class MethodException extends Exception { + public MethodException(String info) { + super(info); + } +} + diff --git a/src/java/org/opensrf/Result.java b/src/java/org/opensrf/Result.java new file mode 100644 index 0000000..f295a1a --- /dev/null +++ b/src/java/org/opensrf/Result.java @@ -0,0 +1,70 @@ +package org.opensrf; + + +/** + * Models a single result from a method request. + */ +public class Result { + + /** Method result content */ + private Object content; + /** Name of the status */ + private String status; + /** Status code number */ + private int statusCode; + + public Result(String status, int statusCode, Object content) { + this.status = status; + this.statusCode = statusCode; + this.content = content; + } + + /** + * Get status. + * @return status as String. + */ + public String getStatus() { + return status; + } + + /** + * Set status. + * @param status the value to set. + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * Get statusCode. + * @return statusCode as int. + */ + public int getStatusCode() { + return statusCode; + } + + /** + * Set statusCode. + * @param statusCode the value to set. + */ + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + /** + * Get content. + * @return content as Object. + */ + public Object getContent() { + return content; + } + + /** + * Set content. + * @param content the value to set. + */ + public void setContent(Object content) { + this.content = content; + } +} + diff --git a/src/java/org/opensrf/Session.java b/src/java/org/opensrf/Session.java new file mode 100644 index 0000000..59eef49 --- /dev/null +++ b/src/java/org/opensrf/Session.java @@ -0,0 +1,50 @@ +package org.opensrf; +import org.opensrf.util.JSON; +import org.opensrf.net.xmpp.*; + +public abstract class Session { + + /** Represents the different connection states for a session */ + public enum ConnectState { + DISCONNECTED, + CONNECTING, + CONNECTED + }; + + /** the current connection state */ + private ConnectState connectState; + + /** The (jabber) address of the remote party we are communicating with */ + private String remoteNode; + + /** + * The thread is used to link messages to a given session. + * In other words, each session has a unique thread, and all messages + * in that session will carry this thread around as an indicator. + */ + private String thread; + + public Session() { + connectState = ConnectState.DISCONNECTED; + } + + /** + * Sends a Message to our remoteNode. + */ + public void send(Message omsg) throws XMPPException { + + /** construct the XMPP message */ + XMPPMessage xmsg = new XMPPMessage(); + xmsg.setTo(remoteNode); + xmsg.setThread(thread); + xmsg.setBody(JSON.toJSON(omsg)); + XMPPSession ses = XMPPSession.getGlobalSession(); + + try { + XMPPSession.getGlobalSession().send(xmsg); + } catch(XMPPException e) { + /* XXX log */ + connectState = ConnectState.DISCONNECTED; + } + } +} diff --git a/src/java/org/opensrf/net/xmpp/XMPPException.java b/src/java/org/opensrf/net/xmpp/XMPPException.java index 29ee0e7..8c20ab7 100644 --- a/src/java/org/opensrf/net/xmpp/XMPPException.java +++ b/src/java/org/opensrf/net/xmpp/XMPPException.java @@ -4,15 +4,7 @@ package org.opensrf.net.xmpp; * Used for XMPP stream/authentication errors */ public class XMPPException extends Exception { - private String info; - - /** - * @param info Runtime exception information. - */ public XMPPException(String info) { - this.info = info; - } - public String toString() { - return this.info; + super(info); } } diff --git a/src/java/org/opensrf/net/xmpp/XMPPReader.java b/src/java/org/opensrf/net/xmpp/XMPPReader.java index 9f5bdb4..9a9c332 100644 --- a/src/java/org/opensrf/net/xmpp/XMPPReader.java +++ b/src/java/org/opensrf/net/xmpp/XMPPReader.java @@ -217,7 +217,10 @@ public class XMPPReader implements Runnable { } } catch(javax.xml.stream.XMLStreamException se) { - /* XXX log an error, set a state, and notify */ + /* XXX log an error */ + xmlState = XMLState.IN_NOTHING; + streamState = XMPPStreamState.DISCONNECTED; + notifyCoreEvent(); } } diff --git a/src/java/org/opensrf/net/xmpp/XMPPSession.java b/src/java/org/opensrf/net/xmpp/XMPPSession.java index 98b555f..ccc85d2 100644 --- a/src/java/org/opensrf/net/xmpp/XMPPSession.java +++ b/src/java/org/opensrf/net/xmpp/XMPPSession.java @@ -37,6 +37,9 @@ public class XMPPSession { /** Raw socket output stream */ OutputStream outStream; + /** The process-wide session. All communication occurs + * accross this single connection */ + private static XMPPSession globalSession; /** @@ -49,12 +52,21 @@ public class XMPPSession { this.port = port; } + public static XMPPSession getGlobalSession() { + return globalSession; + } + + public static void setGlobalSession(XMPPSession ses) { + globalSession = ses; + } + /** true if this session is connected to the server */ public boolean connected() { return ( reader != null && - reader.getXMPPStreamState() == XMPPReader.XMPPStreamState.CONNECTED); + reader.getXMPPStreamState() == + XMPPReader.XMPPStreamState.CONNECTED); } @@ -123,7 +135,7 @@ public class XMPPSession { * Sends an XMPPMessage. * @param msg The message to send. */ - public void send(XMPPMessage msg) throws XMPPException { + public synchronized void send(XMPPMessage msg) throws XMPPException { checkConnected(); try { outStream.write(msg.toXML().getBytes()); diff --git a/src/java/org/opensrf/test/TestJSON.java b/src/java/org/opensrf/test/TestJSON.java new file mode 100644 index 0000000..31a1550 --- /dev/null +++ b/src/java/org/opensrf/test/TestJSON.java @@ -0,0 +1,26 @@ +package org.opensrf.test; + +import org.opensrf.util.JSON; +import java.util.*; + +public class TestJSON { + + public static void main(String args[]) { + + Map map = new HashMap(); + map.put("key1", "value1"); + map.put("key2", "value2"); + map.put("key3", "value3"); + map.put("key4", "athe\u0301s"); + map.put("key5", null); + + List list = new ArrayList(16); + list.add(new Integer(1)); + list.add(new Boolean(true)); + list.add("WATER"); + list.add(null); + map.put("key6", list); + + System.out.println(JSON.toJSON(map)); + } +} diff --git a/src/java/org/opensrf/util/JSON.java b/src/java/org/opensrf/util/JSON.java new file mode 100644 index 0000000..4ba7066 --- /dev/null +++ b/src/java/org/opensrf/util/JSON.java @@ -0,0 +1,117 @@ +package org.opensrf.util; + +import java.io.*; +import java.util.*; + +/** + * JSON utilities. + */ +public class JSON { + + public static final String JSON_CLASS_KEY = "__c"; + 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; + } + + /** 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("}"); + } +} + + + diff --git a/src/java/org/opensrf/util/Utils.java b/src/java/org/opensrf/util/Utils.java new file mode 100644 index 0000000..60c476f --- /dev/null +++ b/src/java/org/opensrf/util/Utils.java @@ -0,0 +1,86 @@ +package org.opensrf.util; + +import java.io.*; +import java.util.*; + +/** + * Collection of general, static utility methods + */ +public class Utils { + + /** + * Returns the string representation of a given file. + * @param filename The file to turn into a string + */ + public static String fileToString(String filename) + throws FileNotFoundException, IOException { + + StringBuffer sb = new StringBuffer(); + BufferedReader in = new BufferedReader(new FileReader(filename)); + String str; + while ((str = in.readLine()) != null) + sb.append(str); + in.close(); + return sb.toString(); + } + + + /** + * @see escape(String, StringBuffer) + */ + public static String escape(String string) { + StringBuffer sb = new StringBuffer(); + escape(string, sb); + return sb.toString(); + } + + /** + * Escapes a string. Turns bare newlines into \n, etc. + * Escapes \n, \r, \t, ", \f + * Encodes non-ascii characters as UTF-8: \u0000 + * @param string The string to escape + * @param sb The string buffer to write the escaped string into + */ + public static void escape(String string, StringBuffer sb) { + int len = string.length(); + String utf; + char c; + for( int i = 0; i < len; i++ ) { + c = string.charAt(i); + switch (c) { + case '\\': + sb.append("\\\\"); + break; + case '"': + sb.append("\\\""); + break; + case '\b': + sb.append("\\b"); + break; + case '\t': + sb.append("\\t"); + break; + case '\n': + sb.append("\\n"); + break; + case '\f': + sb.append("\\f"); + break; + case '\r': + sb.append("\\r"); + break; + default: + if (c < 32 || c > 126 ) { + /* escape all non-ascii or control characters as UTF-8 */ + utf = "000" + Integer.toHexString(c); + sb.append("\\u" + utf.substring(utf.length() - 4)); + } else { + sb.append(c); + } + } + } + } +} + + + -- 2.43.2