+++ /dev/null
-package org.opensrf;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Random;
-import java.util.Arrays;
-
-import org.opensrf.util.*;
-import org.opensrf.net.xmpp.*;
-
-
-/**
- * Models an OpenSRF client session.
- */
-public class ClientSession extends Session {
-
- /** The remote service to communicate with */
- private String service;
- /** OpenSRF domain */
- private String domain;
- /** Router name */
- private String router;
-
- /**
- * original remote node. The current remote node will change based
- * on server responses. This is used to reset the remote node to
- * its original state.
- */
- private String origRemoteNode;
- /** The next request id */
- private int nextId;
- /** The requests this session has sent */
- private Map<Integer, Request> requests;
-
- /**
- * Creates a new client session. Initializes the
- * @param service The remote service.
- */
- public ClientSession(String service) throws ConfigException {
- this(service, null);
- }
-
- /**
- * Creates a new client session. Initializes the
- * @param service The remote service.
- * @param locale The locale for this session.
- */
- public ClientSession(String service, String locale) throws ConfigException {
- this.service = service;
- if(locale != null)
- setLocale(locale);
-
- /** generate the remote node string */
- domain = (String) Config.global().getFirst("/domain");
- router = Config.global().getString("/router_name");
- setRemoteNode(router + "@" + domain + "/" + service);
- origRemoteNode = getRemoteNode();
-
-
- /** create a random thread */
- long time = new Date().getTime();
- Random rand = new Random(time);
- setThread(rand.nextInt()+""+rand.nextInt()+""+time+Thread.currentThread().getId());
-
- nextId = 0;
- requests = new HashMap<Integer, Request>();
- cacheSession();
- }
-
- /**
- * Creates a new request to send to our remote service.
- * @param method The method API name
- * @param params The list of method parameters
- * @return The request object.
- */
- public Request request(String method, List<Object> params) throws SessionException {
- return request(new Request(this, nextId++, method, params));
- }
-
- /**
- * Creates a new request to send to our remote service.
- * @param method The method API name
- * @param params The list of method parameters
- * @return The request object.
- */
- public Request request(String method, Object[] params) throws SessionException {
- return request(new Request(this, nextId++, method, Arrays.asList(params)));
- }
-
-
- /**
- * Creates a new request to send to our remote service.
- * @param method The method API name
- * @return The request object.
- */
- public Request request(String method) throws SessionException {
- return request(new Request(this, nextId++, method));
- }
-
-
- private Request request(Request req) throws SessionException {
- if(getConnectState() != ConnectState.CONNECTED)
- resetRemoteId();
- requests.put(new Integer(req.getId()), req);
- req.send();
- return req;
- }
-
-
- /**
- * Resets the remoteNode to its original state.
- */
- public void resetRemoteId() {
- setRemoteNode(origRemoteNode);
- }
-
-
- /**
- * Pushes a response onto the result queue of the appropriate request.
- * @param msg The received RESULT Message
- */
- public void pushResponse(Message msg) {
-
- Request req = findRequest(msg.getId());
- if(req == null) {
- /** LOG that we've received a result to a non-existant request */
- System.err.println(msg.getId() +" has no corresponding request");
- return;
- }
- OSRFObject payload = (OSRFObject) msg.get("payload");
-
- /** build a result and push it onto the request's result queue */
- req.pushResponse(
- new Result(
- payload.getString("status"),
- payload.getInt("statusCode"),
- payload.get("content")
- )
- );
- }
-
- public Request findRequest(int reqId) {
- return requests.get(new Integer(reqId));
- }
-
- /**
- * Removes a request for this session's request set
- */
- public void cleanupRequest(int reqId) {
- requests.remove(new Integer(reqId));
- }
-
- public void setRequestComplete(int reqId) {
- Request req = findRequest(reqId);
- if(req == null) return;
- req.setComplete();
- }
-
- public static Object atomicRequest(String service, String method, Object[] params) throws MethodException {
- try {
- ClientSession session = new ClientSession(service);
- Request osrfRequest = session.request(method, params);
- Result result = osrfRequest.recv(600000);
- if(result.getStatusCode() != 200)
- throw new MethodException(
- "Request "+service+":"+method+":"+" failed with status code " + result.getStatusCode());
- return result.getContent();
- } catch(Exception e) {
- throw new MethodException(e);
- }
- }
-}
-
+++ /dev/null
-package org.opensrf;
-import org.opensrf.util.*;
-
-
-public class Message implements OSRFSerializable {
-
- /** Message types */
- public static final String REQUEST = "REQUEST";
- public static final String STATUS = "STATUS";
- public static final String RESULT = "RESULT";
- public static final String CONNECT = "CONNECT";
- public static final String DISCONNECT = "DISCONNECT";
-
- /** Message ID. This number is used to relate requests to responses */
- private int id;
- /** type of message. */
- private String type;
- /** message payload */
- private Object payload;
- /** message locale */
- private String locale;
-
- /** Create a registry for the osrfMessage object */
- private static OSRFRegistry registry =
- OSRFRegistry.registerObject(
- "osrfMessage",
- OSRFRegistry.WireProtocol.HASH,
- new String[] {"threadTrace", "type", "payload", "locale"});
-
- /**
- * @param id This message's ID
- * @param type The type of message
- */
- public Message(int id, String type) {
- setId(id);
- setString(type);
- }
-
- /**
- * @param id This message's ID
- * @param type The type of message
- * @param payload The message payload
- */
- public Message(int id, String type, Object payload) {
- this(id, type);
- setPayload(payload);
- }
-
- /**
- * @param id This message's ID
- * @param type The type of message
- * @param payload The message payload
- * @param locale The message locale
- */
- public Message(int id, String type, Object payload, String locale) {
- this(id, type, payload);
- setPayload(payload);
- setLocale(locale);
- }
-
-
- public int getId() {
- return id;
- }
- public String getType() {
- return type;
- }
- public Object getPayload() {
- return payload;
- }
- public String getLocale() {
- return locale;
- }
- public void setId(int id) {
- this.id = id;
- }
- public void setString(String type) {
- this.type = type;
- }
- public void setPayload(Object p) {
- payload = p;
- }
- public void setLocale(String l) {
- locale = l;
- }
-
- /**
- * Implements the generic get() API required by OSRFSerializable
- */
- public Object get(String field) {
- if("threadTrace".equals(field))
- return getId();
- if("type".equals(field))
- return getType().toString();
- if("payload".equals(field))
- return getPayload();
- if("locale".equals(field))
- return getLocale();
- return null;
- }
-
- /**
- * @return The osrfMessage registry.
- */
- public OSRFRegistry getRegistry() {
- return registry;
- }
-}
-
-
+++ /dev/null
-package org.opensrf;
-import java.util.List;
-import java.util.ArrayList;
-import org.opensrf.util.*;
-
-
-public class Method extends OSRFObject {
-
- /** The method API name */
- private String name;
- /** The ordered list of method params */
- private List<Object> params;
-
- /** Create a registry for the osrfMethod object */
- private static OSRFRegistry registry =
- OSRFRegistry.registerObject(
- "osrfMethod",
- OSRFRegistry.WireProtocol.HASH,
- new String[] {"method", "params"});
-
- /**
- * @param name The method API name
- */
- public Method(String name) {
- this.name = name;
- this.params = new ArrayList<Object>(8);
- }
-
- /**
- * @param name The method API name
- * @param params The ordered list of params
- */
- public Method(String name, List<Object> params) {
- this.name = name;
- this.params = params;
- }
-
- /**
- * @return The method API name
- */
- public String getName() {
- return name;
- }
- /**
- * @return The ordered list of params
- */
- public List<Object> 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 addParam(Object p) {
- this.params.add(p);
- }
-
- /**
- * Implements the generic get() API required by OSRFSerializable
- */
- public Object get(String field) {
- if("method".equals(field))
- return getName();
- if("params".equals(field))
- return getParams();
- return null;
- }
-
- /**
- * @return The osrfMethod registry.
- */
- public OSRFRegistry getRegistry() {
- return registry;
- }
-
-}
-
+++ /dev/null
-package org.opensrf;
-
-/**
- * Thrown when the server responds with a method exception.
- */
-public class MethodException extends Exception {
- public MethodException(String info) {
- super(info);
- }
- public MethodException(Throwable cause) {
- super(cause);
- }
-}
-
+++ /dev/null
-package org.opensrf;
-import java.util.List;
-import java.util.ArrayList;
-import org.opensrf.util.ConfigException;
-
-public class MultiSession {
-
- class RequestContainer {
- Request request;
- int id;
- RequestContainer(Request r) {
- request = r;
- }
- }
-
- private boolean complete;
- private List<RequestContainer> requests;
- private int lastId;
-
- public MultiSession() {
- requests = new ArrayList<RequestContainer>();
- }
-
- public boolean isComplete() {
- return complete;
- }
-
- public int lastId() {
- return lastId;
- }
-
- /**
- * Adds a new request to the set of requests.
- * @param service The OpenSRF service
- * @param method The OpenSRF method
- * @param params The array of method params
- * @return The request ID, which is used to map results from recv() to the original request.
- */
- public int request(String service, String method, Object[] params) throws SessionException, ConfigException {
- ClientSession ses = new ClientSession(service);
- return request(ses.request(method, params));
- }
-
-
- public int request(String service, String method) throws SessionException, ConfigException {
- ClientSession ses = new ClientSession(service);
- return request(ses.request(method));
- }
-
- private int request(Request req) {
- RequestContainer c = new RequestContainer(req);
- c.id = requests.size();
- requests.add(c);
- return c.id;
- }
-
-
- /**
- * Calls recv on all pending requests until there is data to return. The ID which
- * maps the received object to the request can be retrieved by calling lastId().
- * @param millis Number of milliseconds to wait for some data to arrive.
- * @return The object result or null if all requests are complete
- * @throws MethodException Thrown if no response is received within
- * the given timeout or the method fails.
- */
- public Object recv(int millis) throws MethodException {
- if(complete) return null;
-
- Request req = null;
- Result res = null;
- RequestContainer cont = null;
-
- long duration = 0;
- long blockTime = 100;
-
- /* if there is only 1 outstanding request, don't poll */
- if(requests.size() == 1)
- blockTime = millis;
-
- while(true) {
- for(int i = 0; i < requests.size(); i++) {
-
- cont = requests.get(i);
- req = cont.request;
-
- try {
- if(i == 0) {
- res = req.recv(blockTime);
- } else {
- res = req.recv(0);
- }
- } catch(SessionException e) {
- throw new MethodException(e);
- }
-
- if(res != null) break;
- }
-
- if(res != null) break;
- duration += blockTime;
-
- if(duration >= millis) {
- System.out.println("duration = " + duration + " millis = " + millis);
- throw new MethodException("No request received within " + millis + " milliseconds");
- }
- }
-
- if(res.getStatusCode() != 200) {
- throw new MethodException("Request " + cont.id + " failed with status code " +
- res.getStatusCode() + " and status message " + res.getStatus());
- }
-
- if(req.isComplete())
- requests.remove(requests.indexOf(cont));
-
- if(requests.size() == 0)
- complete = true;
-
- lastId = cont.id;
- return res.getContent();
- }
-}
-
+++ /dev/null
-package org.opensrf;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.List;
-import java.util.Date;
-import org.opensrf.net.xmpp.XMPPException;
-import org.opensrf.util.Logger;
-
-public class Request {
-
- /** This request's controlling session */
- private ClientSession session;
- /** The method */
- private Method method;
- /** The ID of this request */
- private int id;
- /** Queue of Results */
- private Queue<Result> resultQueue;
- /** If true, the receive timeout for this request should be reset */
- private boolean resetTimeout;
-
- /** If true, the server has indicated that this request is complete. */
- private boolean complete;
-
- /**
- * @param ses The controlling session for this request.
- * @param id This request's ID.
- * @param method The requested method.
- */
- public Request(ClientSession ses, int id, Method method) {
- this.session = ses;
- this.id = id;
- this.method = method;
- resultQueue = new ConcurrentLinkedQueue<Result>();
- complete = false;
- resetTimeout = false;
- }
-
- /**
- * @param ses The controlling session for this request.
- * @param id This request's ID.
- * @param methodName The requested method's API name.
- */
- public Request(ClientSession ses, int id, String methodName) {
- this(ses, id, new Method(methodName));
- }
-
- /**
- * @param ses The controlling session for this request.
- * @param id This request's ID.
- * @param methodName The requested method's API name.
- * @param params The list of request params
- */
- public Request(ClientSession ses, int id, String methodName, List<Object> params) {
- this(ses, id, new Method(methodName, params));
- }
-
- /**
- * Sends the request to the server.
- */
- public void send() throws SessionException {
- session.send(new Message(id, Message.REQUEST, method, session.getLocale()));
- }
-
- /**
- * Receives the next result for this request. This method
- * will wait up to the specified number of milliseconds for
- * a response.
- * @param millis Number of milliseconds to wait for a result. If
- * negative, this method will wait indefinitely.
- * @return The result or null if none arrives in time
- */
- public Result recv(long millis) throws SessionException, MethodException {
-
- Result result = null;
-
- if((result = resultQueue.poll()) != null)
- return result;
-
- if(millis < 0 && !complete) {
- /** wait potentially forever for a result to arrive */
- while(!complete) {
- session.waitForMessage(millis);
- if((result = resultQueue.poll()) != null)
- return result;
- }
-
- } else {
-
- while(millis >= 0 && !complete) {
-
- /** wait up to millis milliseconds for a result. waitForMessage()
- * will return if a response to any request arrives, so we keep track
- * of how long we've been waiting in total for a response to
- * this request */
-
- long start = new Date().getTime();
- session.waitForMessage(millis);
- millis -= new Date().getTime() - start;
- if((result = resultQueue.poll()) != null)
- return result;
- }
- }
-
- return null;
- }
-
- /**
- * Pushes a result onto the result queue
- * @param result The result to push
- */
- public void pushResponse(Result result) {
- resultQueue.offer(result);
- }
-
- /**
- * @return This request's ID
- */
- public int getId() {
- return id;
- }
-
- /**
- * Removes this request from the controlling session's request set
- */
- public void cleanup() {
- session.cleanupRequest(id);
- }
-
- /** Sets this request as complete */
- public void setComplete() {
- complete = true;
- }
-
- public boolean isComplete() {
- return complete;
- }
-}
+++ /dev/null
-package org.opensrf;
-import org.opensrf.util.*;
-
-
-/**
- * Models a single result from a method request.
- */
-public class Result implements OSRFSerializable {
-
- /** Method result content */
- private Object content;
- /** Name of the status */
- private String status;
- /** Status code number */
- private int statusCode;
-
-
- /** Register this object */
- private static OSRFRegistry registry =
- OSRFRegistry.registerObject(
- "osrfResult",
- OSRFRegistry.WireProtocol.HASH,
- new String[] {"status", "statusCode", "content"});
-
-
- /**
- * @param status The status message for this result
- * @param statusCode The status code
- * @param content The content of the result
- */
- 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;
- }
-
- /**
- * Implements the generic get() API required by OSRFSerializable
- */
- public Object get(String field) {
- if("status".equals(field))
- return getStatus();
- if("statusCode".equals(field))
- return getStatusCode();
- if("content".equals(field))
- return getContent();
- return null;
- }
-
- /**
- * @return The osrfMethod registry.
- */
- public OSRFRegistry getRegistry() {
- return registry;
- }
-
-}
-
+++ /dev/null
-package org.opensrf;
-
-/**
- * Models an OpenSRF server session.
- */
-public class ServerSession extends Session {
-}
-
+++ /dev/null
-package org.opensrf;
-import org.opensrf.util.JSONWriter;
-import org.opensrf.net.xmpp.*;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Arrays;
-
-public abstract class Session {
-
- /** Represents the different connection states for a session */
- public enum ConnectState {
- DISCONNECTED,
- CONNECTING,
- CONNECTED
- };
-
- /** local cache of existing sessions */
- private static Map<String, Session>
- sessionCache = new HashMap<String, Session>();
-
- /** the current connection state */
- private ConnectState connectState;
-
- /** The address of the remote party we are communicating with */
- private String remoteNode;
-
- /** Session locale */
- protected String locale;
- /** Default session locale */
- protected static String defaultLocale = "en-US";
-
- /**
- * 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 SessionException {
-
- /** construct the XMPP message */
- XMPPMessage xmsg = new XMPPMessage();
- xmsg.setTo(remoteNode);
- xmsg.setThread(thread);
- xmsg.setBody(new JSONWriter(Arrays.asList(new Message[] {omsg})).write());
-
- try {
- XMPPSession.getThreadSession().send(xmsg);
- } catch(XMPPException e) {
- connectState = ConnectState.DISCONNECTED;
- throw new SessionException("Error sending message to " + remoteNode, e);
- }
- }
-
- /**
- * 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) throws SessionException, MethodException {
- try {
- Stack.processXMPPMessage(
- XMPPSession.getThreadSession().recv(millis));
- } catch(XMPPException e) {
- throw new SessionException("Error waiting for message", e);
- }
- }
-
- /**
- * 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);
- }
-
- /**
- * Puts this session into session cache.
- */
- protected void cacheSession() {
- sessionCache.put(thread, this);
- }
-
- /**
- * Sets the remote address
- * @param nodeName The name of the remote node.
- */
- public void setRemoteNode(String nodeName) {
- remoteNode = nodeName;
- }
- /**
- * @return The remote node
- */
- public String getRemoteNode() {
- return remoteNode;
- }
-
-
- /**
- * Get thread.
- * @return thread as String.
- */
- public String getThread() {
- return thread;
- }
-
- /**
- * Set thread.
- * @param thread the value to set.
- */
- public void setThread(String thread) {
- this.thread = thread;
- }
-
- /**
- * Get locale.
- * @return locale as String.
- */
- public String getLocale() {
- if(locale == null)
- return defaultLocale;
- return locale;
- }
-
- /**
- * Set locale.
- * @param locale the value to set.
- */
- public void setLocale(String locale) {
- this.locale = locale;
- }
-
- /**
- * Get defaultLocale.
- * @return defaultLocale as String.
- */
- public String getDefaultLocale() {
- return defaultLocale;
- }
-
- /**
- * Set defaultLocale.
- * @param defaultLocale the value to set.
- */
- public void setDefaultLocale(String defaultLocale) {
- this.defaultLocale = defaultLocale;
- }
-
-
- /**
- * Get connectState.
- * @return connectState as ConnectState.
- */
- public ConnectState getConnectState() {
- return connectState;
- }
-
- /**
- * Set connectState.
- * @param connectState the value to set.
- */
- public void setConnectState(ConnectState connectState) {
- this.connectState = connectState;
- }
-}
+++ /dev/null
-package org.opensrf;
-/**
- * Used by sessions to indicate communication errors
- */
-public class SessionException extends Exception {
- public SessionException(String info) {
- super(info);
- }
- public SessionException(String info, Throwable cause) {
- super(info, cause);
- }
-}
-
+++ /dev/null
-package org.opensrf;
-import org.opensrf.net.xmpp.XMPPMessage;
-import org.opensrf.util.*;
-import java.util.Date;
-import java.util.List;
-import java.util.Iterator;
-
-
-public class Stack {
-
- public static void processXMPPMessage(XMPPMessage msg) throws MethodException {
-
- if(msg == null) return;
-
- //System.out.println(msg.getBody());
-
- /** fetch this session from the cache */
- Session ses = Session.findCachedSession(msg.getThread());
-
- if(ses == null) {
- /** inbound client request, create a new server session */
- return;
- }
-
- /** parse the JSON message body, which should result in a list of OpenSRF messages */
- List msgList;
-
- try {
- msgList = new JSONReader(msg.getBody()).readArray();
- } catch(JSONException e) {
- /** XXX LOG error */
- e.printStackTrace();
- return;
- }
-
- Iterator itr = msgList.iterator();
-
- OSRFObject obj = null;
- long start = new Date().getTime();
-
- /** cycle through the messages and push them up the stack */
- while(itr.hasNext()) {
-
- /** Construct a Message object from the parsed generic OSRFObject */
- obj = (OSRFObject) itr.next();
-
- processOSRFMessage(
- ses,
- new Message(
- obj.getInt("threadTrace"),
- obj.getString("type"),
- obj.get("payload")
- )
- );
- }
-
- /** LOG the duration */
- }
-
- private static void processOSRFMessage(Session ses, Message msg) throws MethodException {
-
- Logger.debug("received id=" + msg.getId() +
- " type=" + msg.getType() + " payload=" + msg.getPayload());
-
- if( ses instanceof ClientSession )
- processResponse((ClientSession) ses, msg);
- else
- processRequest((ServerSession) ses, msg);
- }
-
- /**
- * Process a server response
- */
- private static void processResponse(ClientSession session, Message msg) throws MethodException {
- String type = msg.getType();
-
- if(msg.RESULT.equals(type)) {
- session.pushResponse(msg);
- return;
- }
-
- if(msg.STATUS.equals(type)) {
-
- OSRFObject obj = (OSRFObject) msg.getPayload();
- Status stat = new Status(obj.getString("status"), obj.getInt("statusCode"));
- int statusCode = stat.getStatusCode();
- String status = stat.getStatus();
-
- switch(statusCode) {
- case Status.COMPLETE:
- session.setRequestComplete(msg.getId());
- break;
- case Status.NOTFOUND:
- session.setRequestComplete(msg.getId());
- throw new MethodException(status);
- }
- }
- }
-
- /**
- * Process a client request
- */
- private static void processRequest(ServerSession session, Message msg) {
- }
-}
+++ /dev/null
-package org.opensrf;
-import org.opensrf.util.*;
-
-public class Status {
-
- public static final int CONTINUE = 100;
- public static final int OK = 200;
- public static final int ACCEPTED = 202;
- public static final int COMPLETE = 205;
- public static final int REDIRECTED = 307;
- public static final int EST = 400;
- public static final int STATUS_UNAUTHORIZED = 401;
- public static final int FORBIDDEN = 403;
- public static final int NOTFOUND = 404;
- public static final int NOTALLOWED = 405;
- public static final int TIMEOUT = 408;
- public static final int EXPFAILED = 417;
- public static final int INTERNALSERVERERROR = 500;
- public static final int NOTIMPLEMENTED = 501;
- public static final int VERSIONNOTSUPPORTED = 505;
-
- private OSRFRegistry registry = OSRFRegistry.registerObject(
- "osrfConnectStatus",
- OSRFRegistry.WireProtocol.HASH,
- new String[] {"status", "statusCode"});
-
- /** The name of the status */
- String status;
- /** The status code */
- int statusCode;
-
- public Status(String status, int statusCode) {
- this.status = status;
- this.statusCode = statusCode;
- }
-
- public int getStatusCode() {
- return statusCode;
- }
- public String getStatus() {
- return status;
- }
-
- /**
- * Implements the generic get() API required by OSRFSerializable
- */
- public Object get(String field) {
- if("status".equals(field))
- return getStatus();
- if("statusCode".equals(field))
- return new Integer(getStatusCode());
- return null;
- }
-
- /**
- * @return The osrfMessage registry.
- */
- public OSRFRegistry getRegistry() {
- return registry;
- }
-}
-
-
+++ /dev/null
-package org.opensrf;
-
-import org.opensrf.util.*;
-import org.opensrf.net.xmpp.*;
-import java.util.Random;
-import java.util.Date;
-import java.net.InetAddress;
-
-
-public class Sys {
-
- private static void initLogger(Config config) {
- if(Logger.instance() == null) {
- try {
- String logFile = config.getString("/logfile");
- int logLevel = config.getInt("/loglevel");
- Logger.init( (short) config.getInt("/loglevel"), new FileLogger(logFile));
- /** add syslog support... */
- } catch(Exception e) {
- /* by default, log to stderr at WARN level */
- Logger.init(Logger.WARN, new Logger());
- }
- }
- }
-
- /**
- * Connects to the OpenSRF network so that client sessions may communicate.
- * @param configFile The OpenSRF config file
- * @param configContext Where in the XML document the config chunk lives. This
- * allows an OpenSRF client config chunk to live in XML files where other config
- * information lives.
- */
- public static void bootstrapClient(String configFile, String configContext)
- throws ConfigException, SessionException {
-
-
- /** see if the current thread already has a connection */
- XMPPSession existing = XMPPSession.getThreadSession();
- if(existing != null && existing.connected())
- return;
-
- /** create the config parser */
- Config config = new Config(configContext);
- config.parse(configFile);
- Config.setConfig(config); /* set this as the global config */
-
- initLogger(config);
-
- /** Collect the network connection info from the config */
- String username = config.getString("/username");
- String passwd = config.getString("/passwd");
- String host = (String) config.getFirst("/domain");
- int port = config.getInt("/port");
-
-
- /** Create a random login resource string */
- String res = "java_";
- try {
- res += InetAddress.getLocalHost().getHostAddress();
- } catch(java.net.UnknownHostException e) {}
- res += "_"+Math.abs(new Random(new Date().getTime()).nextInt())
- + "_t"+ Thread.currentThread().getId();
-
-
-
- try {
-
- /** Connect to the Jabber network */
- Logger.info("attempting to create XMPP session "+username+"@"+host+"/"+res);
- XMPPSession xses = new XMPPSession(host, port);
- xses.connect(username, passwd, res);
- XMPPSession.setThreadSession(xses);
-
- } catch(XMPPException e) {
- throw new SessionException("Unable to bootstrap client", e);
- }
- }
-
- /**
- * Shuts down the connection to the opensrf network
- */
- public static void shutdown() {
- XMPPSession.getThreadSession().disconnect();
- }
-}
-
+++ /dev/null
-package org.opensrf.net.http;
-
-import android.util.Log;
-import org.json.JSONObject;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-import java.io.IOException;
-import java.io.BufferedInputStream;
-import java.io.OutputStreamWriter;
-import java.io.InputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URI;
-import java.net.HttpURLConnection;
-import java.lang.StringBuffer;
-import java.util.List;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-public class GatewayRequest extends HttpRequest {
-
- private boolean readComplete;
- private String TAG = GatewayRequest.class.getName();
-
- public GatewayRequest(HttpConnection conn, String service, Method method) {
- super(conn, service, method);
- readComplete = false;
- }
-
- public GatewayRequest send() {
- try {
-
- String postData = compilePostData(service, method);
-
- urlConn = (HttpURLConnection) httpConn.url.openConnection();
- urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- urlConn.setDoInput(true);
- urlConn.setDoOutput(true);
-
-
-
- OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
- wr.write(postData);
- wr.flush();
- wr.close();
-
- } catch (java.io.IOException ex) {
- failed = true;
- failure = ex;
- }
-
- return this;
- }
-
- public Object recv() {
-
- if (readComplete)
- return nextResponse();
-
- try {
-
- InputStream netStream = new BufferedInputStream(urlConn.getInputStream());
- StringBuffer readBuf = new StringBuffer();
-
- int bytesRead = 0;
- byte[] buffer = new byte[1024];
-
- while ((bytesRead = netStream.read(buffer)) != -1) {
- readBuf.append(new String(buffer, 0, bytesRead));
- }
-
- netStream.close();
- urlConn = null;
-
- Map<String,?> result = null;
-
- //System.out.println("osrf: Received " + readBuf.toString());
- //Log.d(TAG, "received:" + readBuf.toString());
- try {
- result = (Map<String, ?>) new JSONReader(readBuf.toString()).readObject();
- } catch (org.opensrf.util.JSONException ex) {
- ex.printStackTrace();
- return null;
- }
- //System.out.println("osrf: Converted object " + result);
- Log.d(TAG, "service:" + this.service
- + " method:" + this.method.getName()
- + " result:" + new JSONObject(result).toString());
- String status = result.get("status").toString();
- if (!"200".equals(status)) {
- failed = true;
- // failure = <some new exception>
- }
-
- // gateway always returns a wrapper array with the full results set
- responseList = (List) result.get("payload");
-
- // System.out.println("Response list : " + responseList);
- } catch (java.io.IOException ex) {
- failed = true;
- failure = ex;
- }
-
- readComplete = true;
- return nextResponse();
- }
-
- private String compilePostData(String service, Method method) {
- URI uri = null;
- StringBuffer postData = new StringBuffer();
-
- postData.append("service=");
- postData.append(service);
- postData.append("&method=");
- postData.append(method.getName());
-
- List params = method.getParams();
- Iterator itr = params.iterator();
-
- while (itr.hasNext()) {
- postData.append("¶m=");
- postData.append(new JSONWriter(itr.next()).write());
- }
-
- try {
- // not using URLEncoder because it replaces ' ' with '+'.
- uri = new URI("http", "", null, postData.toString(), null);
- } catch (java.net.URISyntaxException ex) {
- ex.printStackTrace();
- }
-
- return uri.getRawQuery();
- }
-}
-
-
+++ /dev/null
-package org.opensrf.net.http;
-
-import java.net.URL;
-import java.net.MalformedURLException;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-
-/**
- * Manages connection parameters and thread limiting for opensrf json gateway connections.
- */
-
-public class HttpConnection {
-
- /** Compiled URL object */
- protected URL url;
- /** Number of threads currently communicating with the server */
- protected int activeThreads;
- /** Queue of pending async requests */
- protected Queue<HttpRequest> pendingThreadQueue;
- /** maximum number of actively communicating threads allowed */
- protected int maxThreads = 10;
-
- public HttpConnection(String fullUrl) throws java.net.MalformedURLException {
- activeThreads = 0;
- pendingThreadQueue = new ConcurrentLinkedQueue();
- url = new URL(fullUrl);
- }
-
- public int getMaxThreads() {
- return maxThreads;
- }
-
- /**
- * Set the maximum number of actively communicating threads allowed
- */
- public void setMaxThreads(int max) {
- maxThreads = max;
- }
-
- /**
- * Launches or queues an asynchronous request.
- *
- * If the maximum active thread count has not been reached,
- * start a new thread and use it to send and receive the request.
- * The response is passed to the request's HttpRequestHandler
- * onComplete(). After complete, if the number of active threads
- * is still lower than the max, one request will be pulled (if
- * present) from the async queue and fired.
- *
- * If there are too many active threads, the main request is
- * pushed onto the async queue for later processing
- */
- protected void manageAsyncRequest(final HttpRequest request) {
-
- if (activeThreads >= maxThreads) {
- pendingThreadQueue.offer(request);
- return;
- }
-
- activeThreads++;
-
- //Send the request receive the response, fire off the next
- //thread if necessary, then pass the result to the handler
- Runnable r = new Runnable() {
- public void run() {
-
- Object response;
- request.send();
-
- while ((response = request.recv()) != null) {
- if (request.handler != null)
- request.handler.onResponse(request, response);
- }
-
- if (request.handler != null)
- request.handler.onComplete(request);
-
- activeThreads--;
-
- if (activeThreads < maxThreads) {
- try {
- manageAsyncRequest(pendingThreadQueue.remove());
- } catch (java.util.NoSuchElementException ex) {
- // may have been gobbled by another thread
- }
- }
- }
- };
-
- new Thread(r).start();
- }
-}
-
-
+++ /dev/null
-package org.opensrf.net.http;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-import java.util.List;
-import java.util.LinkedList;
-import java.net.HttpURLConnection;
-
-public abstract class HttpRequest {
-
- protected String service;
- protected Method method;
- protected HttpURLConnection urlConn;
- protected HttpConnection httpConn;
- protected HttpRequestHandler handler;
- protected List<Object> responseList;
- protected Exception failure;
- protected boolean failed;
- protected boolean complete;
-
- public HttpRequest() {
- failed = false;
- complete = false;
- handler = null;
- urlConn = null;
- }
-
- public HttpRequest(HttpConnection conn, String service, Method method) {
- this();
- this.httpConn = conn;
- this.service = service;
- this.method = method;
- }
-
- public void sendAsync(final HttpRequestHandler handler) {
- this.handler = handler;
- httpConn.manageAsyncRequest(this);
- }
-
- protected void pushResponse(Object response) {
- if (responseList == null)
- responseList = new LinkedList<Object>();
- responseList.add(response);
- }
-
- protected List responses() {
- return responseList;
- }
-
- protected Object nextResponse() {
- if (complete || failed) return null;
- if (responseList.size() > 0)
- return responseList.remove(0);
- return null;
- }
-
- public Exception getFailure() {
- return failure;
- }
-
- public abstract HttpRequest send();
-
- public abstract Object recv();
-
- public boolean failed(){
- return failed;
- }
-}
-
-
+++ /dev/null
-package org.opensrf.net.http;
-
-import java.util.List;
-
-/*
- * Handler for async gateway responses.
- */
-public abstract class HttpRequestHandler {
-
- /**
- * Called when all responses have been received.
- *
- * If discardResponses() returns true, will be passed null.
- */
- public void onComplete(HttpRequest request) {
- }
-
- /**
- * Called with each response received from the server.
- *
- * @param payload the value returned from the server.
- */
- public void onResponse(HttpRequest request, Object response) {
- }
-}
+++ /dev/null
-package org.opensrf.net.xmpp;
-
-/**
- * Used for XMPP stream/authentication errors
- */
-public class XMPPException extends Exception {
- public XMPPException(String info) {
- super(info);
- }
-}
+++ /dev/null
-package org.opensrf.net.xmpp;
-
-import java.io.*;
-
-/**
- * Models a single XMPP message.
- */
-public class XMPPMessage {
-
- /** Message body */
- private String body;
- /** Message recipient */
- private String to;
- /** Message sender */
- private String from;
- /** Message thread */
- private String thread;
- /** Message xid */
- private String xid;
-
- public XMPPMessage() {
- }
-
- public String getBody() {
- return body;
- }
- public String getTo() {
- return to;
- }
- public String getFrom() {
- return from;
- }
- public String getThread() {
- return thread;
- }
- public String getXid() {
- return xid;
- }
- public void setBody(String body) {
- this.body = body;
- }
- public void setTo(String to) {
- this.to = to;
- }
- public void setFrom(String from) {
- this.from = from;
- }
- public void setThread(String thread) {
- this.thread = thread;
- }
- public void setXid(String xid) {
- this.xid = xid;
- }
-
-
- /**
- * Generates the XML representation of this message.
- */
- public String toXML() {
- StringBuffer sb = new StringBuffer("<message to='");
- escapeXML(to, sb);
- sb.append("' osrf_xid='");
- escapeXML(xid, sb);
- sb.append("'><thread>");
- escapeXML(thread, sb);
- sb.append("</thread><body>");
- escapeXML(body, sb);
- sb.append("</body></message>");
- return sb.toString();
- }
-
-
- /**
- * Escapes non-valid XML characters.
- * @param s The string to escape.
- * @param sb The StringBuffer to append new data to.
- */
- private void escapeXML(String s, StringBuffer sb) {
- if( s == null ) return;
- char c;
- int l = s.length();
- for( int i = 0; i < l; i++ ) {
- c = s.charAt(i);
- switch(c) {
- case '<':
- sb.append("<");
- break;
- case '>':
- sb.append(">");
- break;
- case '&':
- sb.append("&");
- break;
- default:
- sb.append(c);
- }
- }
- }
-}
-
-
+++ /dev/null
-package org.opensrf.net.xmpp;
-
-import javax.xml.stream.*;
-import javax.xml.stream.events.* ;
-import javax.xml.namespace.QName;
-import java.util.Queue;
-import java.io.InputStream;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.Date;
-import org.opensrf.util.Logger;
-
-/**
- * Slim XMPP Stream reader. This reader only understands enough XMPP
- * to handle logins and recv messages. It's implemented as a StAX parser.
- * @author Bill Erickson, Georgia Public Library Systems
- */
-public class XMPPReader implements Runnable {
-
- /** Queue of received messages. */
- private Queue<XMPPMessage> msgQueue;
- /** Incoming XMPP XML stream */
- private InputStream inStream;
- /** Current message body */
- private StringBuffer msgBody;
- /** Current message thread */
- private StringBuffer msgThread;
- /** Current message status */
- private StringBuffer msgStatus;
- /** Current message error type */
- private StringBuffer msgErrType;
- /** Current message sender */
- private String msgFrom;
- /** Current message recipient */
- private String msgTo;
- /** Current message error code */
- private int msgErrCode;
-
- /** Where this reader currently is in the document */
- private XMLState xmlState;
-
- /** The current connect state to the XMPP server */
- private XMPPStreamState streamState;
-
-
- /** Used to represent out connection state to the XMPP server */
- public static enum XMPPStreamState {
- DISCONNECTED, /* not connected to the server */
- CONNECT_SENT, /* we've sent the initial connect message */
- CONNECT_RECV, /* we've received a response to our connect message */
- AUTH_SENT, /* we've sent an authentication request */
- CONNECTED /* authentication is complete */
- };
-
-
- /** Used to represents where we are in the XML document stream. */
- public static enum XMLState {
- IN_NOTHING,
- IN_BODY,
- IN_THREAD,
- IN_STATUS
- };
-
-
- /**
- * Creates a new reader. Initializes the message queue.
- * Sets the stream state to disconnected, and the xml
- * state to in_nothing.
- * @param inStream the inbound XML stream
- */
- public XMPPReader(InputStream inStream) {
- msgQueue = new ConcurrentLinkedQueue<XMPPMessage>();
- this.inStream = inStream;
- resetBuffers();
- xmlState = XMLState.IN_NOTHING;
- streamState = XMPPStreamState.DISCONNECTED;
- }
-
- /**
- * Change the connect state and notify that a core
- * event has occurred.
- */
- protected void setXMPPStreamState(XMPPStreamState state) {
- streamState = state;
- notifyCoreEvent();
- }
-
- /**
- * @return The current stream state of the reader
- */
- public XMPPStreamState getXMPPStreamState() {
- return streamState;
- }
-
-
- /**
- * @return The next message in the queue, or null
- */
- public XMPPMessage popMessageQueue() {
- return (XMPPMessage) msgQueue.poll();
- }
-
-
- /**
- * Initializes the message buffers
- */
- private void resetBuffers() {
- msgBody = new StringBuffer();
- msgThread = new StringBuffer();
- msgStatus = new StringBuffer();
- msgErrType = new StringBuffer();
- msgFrom = "";
- msgTo = "";
- }
-
-
- /**
- * Notifies the waiting thread that a core event has occurred.
- * Each reader should have exactly one dependent session thread.
- */
- private synchronized void notifyCoreEvent() {
- notifyAll();
- }
-
-
- /**
- * Waits up to timeout milliseconds for a core event to occur.
- * Also, having a message already waiting in the queue
- * constitutes a core event.
- * @param timeout The number of milliseconds to wait. If
- * timeout is negative, waits potentially forever.
- * @return The number of milliseconds in wait
- */
- public synchronized long waitCoreEvent(long timeout) {
-
- if(msgQueue.peek() != null || timeout == 0) return 0;
- long start = new Date().getTime();
-
- try{
- if(timeout < 0)
- wait();
- else
- wait(timeout);
- } catch(InterruptedException ie) {}
-
- return new Date().getTime() - start;
- }
-
-
-
- /** Kickoff the thread */
- public void run() {
- read();
- }
-
-
- /**
- * Parses XML data from the provided XMPP stream.
- */
- public void read() {
-
- try {
-
- XMLInputFactory factory = XMLInputFactory.newInstance();
-
- /** disable as many unused features as possible to speed up the parsing */
- factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
-
- /** create the stream reader */
- XMLStreamReader reader = factory.createXMLStreamReader(inStream);
- int eventType;
-
- while(reader.hasNext()) {
- /** cycle through the XML events */
-
- eventType = reader.next();
-
- switch(eventType) {
-
- case XMLEvent.START_ELEMENT:
- handleStartElement(reader);
- break;
-
- case XMLEvent.CHARACTERS:
- switch(xmlState) {
- case IN_BODY:
- msgBody.append(reader.getText());
- break;
- case IN_THREAD:
- msgThread.append(reader.getText());
- break;
- case IN_STATUS:
- msgStatus.append(reader.getText());
- break;
- }
- break;
-
- case XMLEvent.END_ELEMENT:
- xmlState = XMLState.IN_NOTHING;
- if("message".equals(reader.getName().toString())) {
-
- /** build a message and add it to the message queue */
- XMPPMessage msg = new XMPPMessage();
- msg.setFrom(msgFrom);
- msg.setTo(msgTo);
- msg.setBody(msgBody.toString());
- msg.setThread(msgThread.toString());
-
- Logger.internal("xmpp message from="+msgFrom+" " + msg.getBody());
-
- msgQueue.offer(msg);
- resetBuffers();
- notifyCoreEvent();
- }
- break;
- }
- }
-
- } catch(javax.xml.stream.XMLStreamException se) {
- /* XXX log an error */
- xmlState = XMLState.IN_NOTHING;
- streamState = XMPPStreamState.DISCONNECTED;
- notifyCoreEvent();
- }
- }
-
-
- /**
- * Handles the start_element event.
- */
- private void handleStartElement(XMLStreamReader reader) {
-
- String name = reader.getName().toString();
-
- if("message".equals(name)) {
- xmlState = XMLState.IN_BODY;
-
- /** add a special case for the opensrf "router_from" attribute */
- String rf = reader.getAttributeValue(null, "router_from");
- if( rf != null )
- msgFrom = rf;
- else
- msgFrom = reader.getAttributeValue(null, "from");
- msgTo = reader.getAttributeValue(null, "to");
- return;
- }
-
- if("body".equals(name)) {
- xmlState = XMLState.IN_BODY;
- return;
- }
-
- if("thread".equals(name)) {
- xmlState = XMLState.IN_THREAD;
- return;
- }
-
- if("stream:stream".equals(name)) {
- setXMPPStreamState(XMPPStreamState.CONNECT_RECV);
- return;
- }
-
- if("iq".equals(name)) {
- if("result".equals(reader.getAttributeValue(null, "type")))
- setXMPPStreamState(XMPPStreamState.CONNECTED);
- return;
- }
-
- if("status".equals(name)) {
- xmlState = XMLState.IN_STATUS;
- return;
- }
-
- if("stream:error".equals(name)) {
- setXMPPStreamState(XMPPStreamState.DISCONNECTED);
- return;
- }
-
- if("error".equals(name)) {
- msgErrType.append(reader.getAttributeValue(null, "type"));
- msgErrCode = Integer.parseInt(reader.getAttributeValue(null, "code"));
- setXMPPStreamState(XMPPStreamState.DISCONNECTED);
- return;
- }
- }
-}
-
-
-
-
+++ /dev/null
-package org.opensrf.net.xmpp;
-
-import java.io.*;
-import java.net.Socket;
-import java.util.Map;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-
-
-/**
- * Represents a single XMPP session. Sessions are responsible for writing to
- * the stream and for managing a stream reader.
- */
-public class XMPPSession {
-
- /** Initial jabber message */
- public static final String JABBER_CONNECT =
- "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
-
- /** Basic auth message */
- public static final String JABBER_BASIC_AUTH =
- "<iq id='123' type='set'><query xmlns='jabber:iq:auth'>" +
- "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>";
-
- public static final String JABBER_DISCONNECT = "</stream:stream>";
-
- private static Map threadConnections = new ConcurrentHashMap();
-
- /** jabber domain */
- private String host;
- /** jabber port */
- private int port;
- /** jabber username */
- private String username;
- /** jabber password */
- private String password;
- /** jabber resource */
- private String resource;
-
- /** XMPP stream reader */
- XMPPReader reader;
- /** Fprint-capable socket writer */
- PrintWriter writer;
- /** Raw socket output stream */
- OutputStream outStream;
- /** The raw socket */
- Socket socket;
-
- /** The process-wide session. All communication occurs
- * accross this single connection */
- private static XMPPSession globalSession;
-
-
- /**
- * Creates a new session.
- * @param host The jabber domain
- * @param port The jabber port
- */
- public XMPPSession( String host, int port ) {
- this.host = host;
- this.port = port;
- }
-
- /**
- * Returns the global, process-wide session
- */
- /*
- public static XMPPSession getGlobalSession() {
- return globalSession;
- }
- */
-
- public static XMPPSession getThreadSession() {
- return (XMPPSession) threadConnections.get(new Long(Thread.currentThread().getId()));
- }
-
- /**
- * Sets the given session as the global session for the current thread
- * @param ses The session
- */
- public static void setThreadSession(XMPPSession ses) {
- /* every time we create a new connection, clean up any dead threads.
- * this is cheaper than cleaning up the dead threads at every access. */
- cleanupThreadSessions();
- threadConnections.put(new Long(Thread.currentThread().getId()), ses);
- }
-
- /**
- * Analyzes the threadSession data to see if there are any sessions
- * whose controlling thread has gone away.
- */
- private static void cleanupThreadSessions() {
- Thread threads[] = new Thread[Thread.activeCount()];
- Thread.enumerate(threads);
- for(Iterator i = threadConnections.keySet().iterator(); i.hasNext(); ) {
- boolean found = false;
- Long id = (Long) i.next();
- for(Thread t : threads) {
- if(t.getId() == id.longValue()) {
- found = true;
- break;
- }
- }
- if(!found)
- threadConnections.remove(id);
- }
- }
-
- /**
- * Sets the global, process-wide section
- */
- /*
- 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 &&
- !socket.isClosed()
- );
- }
-
-
- /**
- * Connects to the network.
- * @param username The jabber username
- * @param password The jabber password
- * @param resource The Jabber resource
- */
- public void connect(String username, String password, String resource) throws XMPPException {
-
- this.username = username;
- this.password = password;
- this.resource = resource;
-
- try {
- /* open the socket and associated streams */
- socket = new Socket(host, port);
-
- /** the session maintains control over the output stream */
- outStream = socket.getOutputStream();
- writer = new PrintWriter(outStream, true);
-
- /** pass the input stream to the reader */
- reader = new XMPPReader(socket.getInputStream());
-
- } catch(IOException ioe) {
- throw new
- XMPPException("unable to communicate with host " + host + " on port " + port);
- }
-
- /* build the reader thread */
- Thread thread = new Thread(reader);
- thread.setDaemon(true);
- thread.start();
-
- synchronized(reader) {
- /* send the initial jabber message */
- sendConnect();
- reader.waitCoreEvent(10000);
- }
- if( reader.getXMPPStreamState() != XMPPReader.XMPPStreamState.CONNECT_RECV )
- throw new XMPPException("unable to connect to jabber server");
-
- synchronized(reader) {
- /* send the basic auth message */
- sendBasicAuth();
- reader.waitCoreEvent(10000);
- }
- if(!connected())
- throw new XMPPException("Authentication failed");
- }
-
- /** Sends the initial jabber message */
- private void sendConnect() {
- reader.setXMPPStreamState(XMPPReader.XMPPStreamState.CONNECT_SENT);
- writer.printf(JABBER_CONNECT, host);
- }
-
- /** Send the basic auth message */
- private void sendBasicAuth() {
- reader.setXMPPStreamState(XMPPReader.XMPPStreamState.AUTH_SENT);
- writer.printf(JABBER_BASIC_AUTH, username, password, resource);
- }
-
-
- /**
- * Sends an XMPPMessage.
- * @param msg The message to send.
- */
- public synchronized void send(XMPPMessage msg) throws XMPPException {
- checkConnected();
- try {
- String xml = msg.toXML();
- outStream.write(xml.getBytes());
- } catch (Exception e) {
- throw new XMPPException(e.toString());
- }
- }
-
-
- /**
- * @throws XMPPException if we are no longer connected.
- */
- private void checkConnected() throws XMPPException {
- if(!connected())
- throw new XMPPException("Disconnected stream");
- }
-
-
- /**
- * Receives messages from the network.
- * @param timeout Maximum number of milliseconds to wait for a message to arrive.
- * If timeout is negative, this method will wait indefinitely.
- * 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(long timeout) throws XMPPException {
-
- XMPPMessage msg;
-
- if(timeout < 0) {
-
- while(true) { /* wait indefinitely for a message to arrive */
- reader.waitCoreEvent(timeout);
- msg = reader.popMessageQueue();
- if( msg != null ) return msg;
- checkConnected();
- }
-
- } else {
-
- while(timeout >= 0) { /* wait at most 'timeout' milleseconds for a message to arrive */
- msg = reader.popMessageQueue();
- if( msg != null ) return msg;
- timeout -= reader.waitCoreEvent(timeout);
- msg = reader.popMessageQueue();
- if( msg != null ) return msg;
- checkConnected();
- if(timeout == 0) break;
- }
- }
-
- return reader.popMessageQueue();
- }
-
-
- /**
- * Disconnects from the jabber server and closes the socket
- */
- public void disconnect() {
- try {
- outStream.write(JABBER_DISCONNECT.getBytes());
- socket.close();
- } catch(Exception e) {}
- }
-}
-
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.io.PrintStream;
-
-
-public class MathBench {
-
- public static void main(String args[]) throws Exception {
-
- PrintStream out = System.out;
-
- if(args.length < 2) {
- out.println("usage: java org.opensrf.test.MathBench <osrfConfig> <numIterations>");
- return;
- }
-
- /** connect to the opensrf network */
- Sys.bootstrapClient(args[0], "/config/opensrf");
-
- /** how many iterations */
- int count = Integer.parseInt(args[1]);
-
- /** create the client session */
- ClientSession session = new ClientSession("opensrf.math");
-
- /** params are 1,2 */
- List<Object> params = new ArrayList<Object>();
- params.add(new Integer(1));
- params.add(new Integer(2));
-
- Request request;
- Result result;
- long start;
- double total = 0;
-
- for(int i = 0; i < count; i++) {
-
- start = new Date().getTime();
-
- /** create (and send) the request */
- request = session.request("add", params);
-
- /** wait up to 3 seconds for a response */
- result = request.recv(3000);
-
- /** collect the round-trip time */
- total += new Date().getTime() - start;
-
- if(result.getStatusCode() == Status.OK) {
- out.print("+");
- } else {
- out.println("\nrequest failed");
- out.println("status = " + result.getStatus());
- out.println("status code = " + result.getStatusCode());
- }
-
- /** remove this request from the session's request set */
- request.cleanup();
-
- if((i+1) % 100 == 0) /** print 100 responses per line */
- out.println(" [" + (i+1) + "]");
- }
-
- out.println("\nAverage request time is " + (total/count) + " ms");
-
- /** remove this session from the global session cache */
- session.cleanup();
-
- /** disconnect from the opensrf network */
- Sys.shutdown();
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.util.Cache;
-
-public class TestCache {
- public static void main(String args[]) throws Exception {
-
- /**
- * args is a list of string like so: server:port server2:port server3:port ...
- */
-
- Cache.initCache(args);
- Cache cache = new Cache();
-
- cache.set("key1", "HI, MA!");
- cache.set("key2", "HI, MA! 2");
- cache.set("key3", "HI, MA! 3");
-
- System.out.println("got key1 = " + (String) cache.get("key1"));
- System.out.println("got key2 = " + (String) cache.get("key2"));
- System.out.println("got key3 = " + (String) cache.get("key3"));
- }
-}
-
-
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-import java.util.Map;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.io.PrintStream;
-
-public class TestClient {
-
- public static void main(String args[]) throws Exception {
-
- /** which opensrf service are we sending our request to */
- String service;
- /** which opensrf method we're calling */
- String method;
- /** method params, captures from command-line args */
- List<Object> params;
- /** knows how to read JSON */
- JSONReader reader;
- /** opensrf request */
- Request request;
- /** request result */
- Result result;
- /** start time for the request */
- long start;
- /** for brevity */
- PrintStream out = System.out;
-
- if(args.length < 3) {
- out.println( "usage: org.opensrf.test.TestClient "+
- "<osrfConfigFile> <service> <method> [<JSONparam1>, <JSONparam2>]");
- return;
- }
-
- /** connect to the opensrf network, default config context
- * for opensrf_core.xml is /config/opensrf */
- Sys.bootstrapClient(args[0], "/config/opensrf");
-
- /* grab the server, method, and any params from the command line */
- service = args[1];
- method = args[2];
- params = new ArrayList<Object>();
- for(int i = 3; i < args.length; i++)
- params.add(new JSONReader(args[i]).read());
-
-
- /** build the client session */
- ClientSession session = new ClientSession(service);
-
- /** kick off the timer */
- start = new Date().getTime();
-
- /** Create the request object from the session, method and params */
- request = session.request(method, params);
-
- while( (result = request.recv(60000)) != null ) {
- /** loop over the results and print the JSON version of the content */
-
- if(result.getStatusCode() != 200) {
- /** make sure the request succeeded */
- out.println("status = " + result.getStatus());
- out.println("status code = " + result.getStatusCode());
- continue;
- }
-
- /** JSON-ify the resulting object and print it */
- out.println("\nresult JSON: " + new JSONWriter(result.getContent()).write());
- }
-
- /** How long did the request take? */
- out.println("Request round trip took: " + (new Date().getTime() - start) + " ms.");
-
- Sys.shutdown();
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-public class TestConfig {
- public static void main(String args[]) throws Exception {
- Config config = new Config("");
- config.parse(args[0]);
- Config.setConfig(config);
- System.out.println(config);
- System.out.println("");
-
- for(int i = 1; i < args.length; i++)
- System.out.println("Found config value: " + args[i] + ": " + Config.global().get(args[i]));
- }
-}
+++ /dev/null
-package org.opensrf.test;
-
-import org.opensrf.*;
-import org.opensrf.util.*;
-import java.util.*;
-
-public class TestJSON {
-
- public static void main(String args[]) throws Exception {
-
- Map<String,Object> map = new HashMap<String,Object>();
- map.put("key1", "value1");
- map.put("key2", "value2");
- map.put("key3", "value3");
- map.put("key4", "athe\u0301s");
- map.put("key5", null);
-
- List<Object> list = new ArrayList<Object>(16);
- list.add(new Integer(1));
- list.add(new Boolean(true));
- list.add("WATER");
- list.add(null);
- map.put("key6", list);
-
- System.out.println(new JSONWriter(map).write() + "\n");
-
- String[] fields = {"isnew", "name", "shortname", "ill_address"};
- OSRFRegistry.registerObject("aou", OSRFRegistry.WireProtocol.ARRAY, fields);
-
- OSRFObject obj = new OSRFObject(OSRFRegistry.getRegistry("aou"));
- obj.put("name", "athens clarke county");
- obj.put("ill_address", new Integer(1));
- obj.put("shortname", "ARL-ATH");
-
- map.put("key7", obj);
- list.add(obj);
- System.out.println(new JSONWriter(map).write() + "\n");
-
-
- Message m = new Message(1, Message.REQUEST);
- Method method = new Method("opensrf.settings.host_config.get");
- method.addParam("app07.dev.gapines.org");
- m.setPayload(method);
-
- 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());
- }
-}
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.util.Logger;
-import org.opensrf.util.FileLogger;
-
-
-/** Simple test class for tesing the logging functionality */
-public class TestLog {
- public static void main(String args[]) {
- Logger.init(Logger.DEBUG, new FileLogger("test.log"));
- Logger.error("Hello, world");
- Logger.warn("Hello, world");
- Logger.info("Hello, world");
- Logger.debug("Hello, world");
- }
-}
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-public class TestMultiSession {
- public static void main(String[] args) {
- try {
- String config = args[0];
-
- Sys.bootstrapClient(config, "/config/opensrf");
- MultiSession ses = new MultiSession();
-
- for(int i = 0; i < 40; i++) {
- ses.request("opensrf.settings", "opensrf.system.time");
- }
-
- while(!ses.isComplete())
- System.out.println("result = " + ses.recv(5000) + " and id = " + ses.lastId());
-
- System.out.println("done");
- Sys.shutdown();
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
-}
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-
-public class TestSettings {
- public static void main(String args[]) throws Exception {
- Sys.bootstrapClient(args[0], "/config/opensrf");
- SettingsClient client = SettingsClient.instance();
- String lang = client.getString("/apps/opensrf.settings/language");
- String impl = client.getString("/apps/opensrf.settings/implementation");
- System.out.println("opensrf.settings language = " + lang);
- System.out.println("opensrf.settings implementation = " + impl);
- }
-}
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.*;
-import org.opensrf.util.*;
-import java.util.Map;
-import java.util.Date;
-import java.util.List;
-import java.util.ArrayList;
-import java.io.PrintStream;
-
-/**
- * Connects to the opensrf network once per thread and runs
- * and runs a series of request acccross all launched threads.
- * The purpose is to verify that the java threaded client api
- * is functioning as expected
- */
-public class TestThread implements Runnable {
-
- String args[];
-
- public TestThread(String args[]) {
- this.args = args;
- }
-
- public void run() {
-
- try {
-
- Sys.bootstrapClient(args[0], "/config/opensrf");
- ClientSession session = new ClientSession(args[3]);
-
- List params = new ArrayList<Object>();
- for(int i = 5; i < args.length; i++)
- params.add(new JSONReader(args[3]).read());
-
- for(int i = 0; i < Integer.parseInt(args[2]); i++) {
- System.out.println("thread " + Thread.currentThread().getId()+" sending request " + i);
- Request request = session.request(args[4], params);
- Result result = request.recv(3000);
- if(result != null) {
- System.out.println("thread " + Thread.currentThread().getId()+
- " got result JSON: " + new JSONWriter(result.getContent()).write());
- } else {
- System.out.println("* thread " + Thread.currentThread().getId()+ " got NO result");
- }
- }
-
- Sys.shutdown();
- } catch(Exception e) {
- System.err.println(e);
- }
- }
-
- public static void main(String args[]) throws Exception {
-
- if(args.length < 5) {
- System.out.println( "usage: org.opensrf.test.TestClient "+
- "<osrfConfigFile> <numthreads> <numiter> <service> <method> [<JSONparam1>, <JSONparam2>]");
- return;
- }
-
- int numThreads = Integer.parseInt(args[1]);
- for(int i = 0; i < numThreads; i++)
- new Thread(new TestThread(args)).start();
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.util.XMLFlattener;
-import java.io.FileInputStream;
-
-public class TestXMLFlattener {
- public static void main(String args[]) throws Exception {
- FileInputStream fis = new FileInputStream(args[0]);
- XMLFlattener f = new XMLFlattener(fis);
- System.out.println(f.read());
- }
-}
+++ /dev/null
-package org.opensrf.test;
-import org.opensrf.util.XMLTransformer;
-import java.io.File;
-
-public class TestXMLTransformer {
- /**
- * arg[0] path to an XML file
- * arg[1] path to the XSL file to apply
- */
- public static void main(String[] args) {
- try {
- File xmlFile = new File(args[0]);
- File xslFile = new File(args[1]);
- XMLTransformer t = new XMLTransformer(xmlFile, xslFile);
- System.out.println(t.apply());
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
-}
-
-
+++ /dev/null
-package org.opensrf.test;
-
-import org.opensrf.net.xmpp.XMPPReader;
-import org.opensrf.net.xmpp.XMPPMessage;
-import org.opensrf.net.xmpp.XMPPSession;
-
-public class TestXMPP {
-
- /**
- * Connects to the jabber server and waits for inbound messages.
- * If a recipient is provided, a small message is sent to the recipient.
- */
- public static void main(String args[]) throws Exception {
-
- String host;
- int port;
- String username;
- String password;
- String resource;
- String recipient;
-
- try {
- host = args[0];
- port = Integer.parseInt(args[1]);
- username = args[2];
- password = args[3];
- resource = args[4];
-
- } catch(ArrayIndexOutOfBoundsException e) {
- System.err.println("usage: org.opensrf.test.TestXMPP <host> <port> <username> <password> <resource> [<recipient>]");
- return;
- }
-
- XMPPSession session = new XMPPSession(host, port);
- session.connect(username, password, resource);
-
- XMPPMessage msg;
-
- if( args.length == 6 ) {
-
- /** they specified a recipient */
-
- recipient = args[5];
- msg = new XMPPMessage();
- msg.setTo(recipient);
- msg.setThread("test-thread");
- msg.setBody("Hello, from java-xmpp");
- System.out.println("Sending message to " + recipient);
- session.send(msg);
- }
-
- while(true) {
- System.out.println("waiting for message...");
- msg = session.recv(-1); /* wait forever for a message to arrive */
- System.out.println("got message: " + msg.toXML());
- }
- }
-}
-
-
-
-
-
+++ /dev/null
-package org.opensrf.util;
-import com.danga.MemCached.*;
-import java.util.List;
-
-/**
- * Memcache client
- */
-public class Cache extends MemCachedClient {
-
- public Cache() {
- super();
- setCompressThreshold(4096); /* ?? */
- }
-
- /**
- * Initializes the cache client
- * @param serverList Array of server:port strings specifying the
- * set of memcache servers this client will talk to
- */
- public static void initCache(String[] serverList) {
- SockIOPool pool = SockIOPool.getInstance();
- pool.setServers(serverList);
- pool.initialize();
- com.danga.MemCached.Logger logger =
- com.danga.MemCached.Logger.getLogger(MemCachedClient.class.getName());
- logger.setLevel(logger.LEVEL_ERROR);
- }
-
- /**
- * Initializes the cache client
- * @param serverList List of server:port strings specifying the
- * set of memcache servers this client will talk to
- */
- public static void initCache(List<String> serverList) {
- initCache(serverList.toArray(new String[]{}));
- }
-}
-
+++ /dev/null
-package org.opensrf.util;
-
-import org.json.*;
-import java.util.Map;
-import java.util.List;
-
-
-/**
- * Config reader and accesor module. This module reads an XML config file,
- * then loads the file into an internal config, whose values may be accessed
- * by xpath-style lookup paths.
- */
-public class Config {
-
- /** The globl config instance */
- private static Config config;
- /** The object form of the parsed config */
- private Map configObject;
- /**
- * The log parsing context. This is used as a prefix to the
- * config item search path. This allows config XML chunks to
- * be inserted into arbitrary XML files.
- */
- private String context;
-
- public static Config global() {
- return config;
- }
-
-
- /**
- * @param context The config context
- */
- public Config(String context) {
- this.context = context;
- }
-
- /**
- * Sets the global config object.
- * @param c The config object to use.
- */
- public static void setGlobalConfig(Config c) {
- config = c;
- }
-
- /**
- * Parses an XML config file.
- * @param filename The path to the file to parse.
- */
- public void parse(String filename) throws ConfigException {
- try {
- String xml = Utils.fileToString(filename);
- JSONObject jobj = XML.toJSONObject(xml);
- configObject = (Map) new JSONReader(jobj.toString()).readObject();
- } catch(Exception e) {
- throw new ConfigException("Error parsing config", e);
- }
- }
-
- public static void setConfig(Config conf) {
- config = conf;
- }
-
- public void setConfigObject(Map config) {
- this.configObject = config;
- }
-
- protected Map getConfigObject() {
- return this.configObject;
- }
-
-
- /**
- * Returns the configuration value found at the requested path.
- * @param path The search path
- * @return The config value, or null if no value exists at the given path.
- * @throws ConfigException thrown if nothing is found at the path
- */
- public String getString(String path) throws ConfigException {
- try {
- return (String) get(path);
- } catch(Exception e) {
- throw new
- ConfigException("No config string found at " + path);
- }
- }
-
- /**
- * Gets the int value at the given path
- * @param path The search path
- */
- public int getInt(String path) throws ConfigException {
- try {
- return Integer.parseInt(getString(path));
- } catch(Exception e) {
- throw new
- ConfigException("No config int found at " + path);
- }
- }
-
- /**
- * Returns the configuration object found at the requested path.
- * @param path The search path
- * @return The config value
- * @throws ConfigException thrown if nothing is found at the path
- */
- public Object get(String path) throws ConfigException {
- try {
- Object obj = Utils.findPath(configObject, context + path);
- if(obj == null)
- throw new ConfigException("");
- return obj;
- } catch(Exception e) {
- e.printStackTrace();
- throw new ConfigException("No config object found at " + path);
- }
- }
-
- /**
- * Returns the first item in the list found at the given path. If
- * no list is found, ConfigException is thrown.
- * @param path The search path
- */
- public Object getFirst(String path) throws ConfigException {
- Object obj = get(path);
- if(obj instanceof List)
- return ((List) obj).get(0);
- return obj;
- }
-
-
- /**
- * Returns the config as a JSON string
- */
- public String toString() {
- return new JSONWriter(configObject).write();
- }
-}
-
+++ /dev/null
-package org.opensrf.util;
-
-/**
- * Thrown by the Config module when a user requests a configuration
- * item that does not exist
- */
-public class ConfigException extends Exception {
- public ConfigException(String info) {
- super(info);
- }
- public ConfigException(String info, Throwable t) {
- super(info, t);
- }
-}
+++ /dev/null
-package org.opensrf.util;
-import java.io.BufferedWriter;
-import java.io.FileWriter;
-
-
-public class FileLogger extends Logger {
-
- /** File to log to */
- private String filename;
-
- /**
- * FileLogger constructor
- * @param filename The path to the log file
- */
- public FileLogger(String filename) {
- this.filename = filename;
- }
-
- /**
- * Logs the mesage to a file.
- * @param level The log level
- * @param msg The mesage to log
- */
- protected synchronized void log(short level, String msg) {
- if(level > logLevel) return;
-
- BufferedWriter out = null;
- try {
- out = new BufferedWriter(new FileWriter(this.filename, true));
- out.write(formatMessage(level, msg) + "\n");
-
- } catch(Exception e) {
- /** If we are unable to write our log message, go ahead and
- * fall back to the default (stdout) logger */
- Logger.init(logLevel, new Logger());
- Logger.logByLevel(ERROR, "Unable to write to log file " + this.filename);
- Logger.logByLevel(level, msg);
- }
-
- try {
- out.close();
- } catch(Exception e) {}
- }
-}
+++ /dev/null
-package org.opensrf.util;
-/**
- * Used to indicate JSON parsing errors
- */
-public class JSONException extends Exception {
- public JSONException(String s) {
- super(s);
- }
-}
+++ /dev/null
-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";
-
- /** The JSON string to parser */
- private String json;
-
- /**
- * @param json The JSON to parse
- */
- public JSONReader(String json) {
- this.json = json;
- }
-
- /**
- * Parses JSON and creates an object.
- * @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());
- }
- }
-
- /**
- * Assumes that a JSON array will be read. Returns
- * the resulting array as a list.
- */
- public List<?> readArray() throws JSONException {
- Object o = read();
- try {
- return (List<?>) o;
- } catch(Exception e) {
- throw new JSONException("readArray(): JSON cast exception");
- }
- }
-
- /**
- * Assumes that a JSON object will be read. Returns
- * the resulting object as a map.
- */
- public Map<?,?> readObject() throws JSONException {
- Object o = read();
- try {
- return (Map<?,?>) o;
- } catch(Exception e) {
- throw new JSONException("readObject(): JSON cast exception");
- }
- }
-
-
- /**
- * Recurse through the object and turn items into maps, lists, etc.
- */
- 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;
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.util;
-
-import java.io.*;
-import java.util.*;
-
-
-/**
- * JSONWriter
- */
-public class JSONWriter {
-
- /** The object to serialize to JSON */
- private Object obj;
-
- /**
- * @param obj The object to encode
- */
- public JSONWriter(Object obj) {
- this.obj = obj;
- }
-
-
- /**
- * Encodes a java object to JSON.
- */
- 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);
- }
-
- /**
- * Encodes the object as JSON into the provided buffer
- */
- 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 ) {
-
- /** encode arrays as lists */
- 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 {
-
- /** encode hashes as maps */
- 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);
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.util;
-import java.text.SimpleDateFormat;
-import java.text.FieldPosition;
-import java.util.Date;
-
-/**
- * Basic OpenSRF logging API. This default implementation
- * logs to stderr.
- */
-public class Logger {
-
- /** Log levels */
- public static final short ERROR = 1;
- public static final short WARN = 2;
- public static final short INFO = 3;
- public static final short DEBUG = 4;
- public static final short INTERNAL = 5;
-
- /** The global log instance */
- private static Logger instance;
- /** The global log level */
- protected static short logLevel;
-
- public Logger() {}
-
- /** Sets the global Logger instance
- * @param level The global log level.
- * @param l The Logger instance to use
- */
- public static void init(short level, Logger l) {
- instance = l;
- logLevel = level;
- }
-
- /**
- * @return The global Logger instance
- */
- public static Logger instance() {
- return instance;
- }
-
- /**
- * Logs an error message
- * @param msg The message to log
- */
- public static void error(String msg) {
- instance.log(ERROR, msg);
- }
-
- /**
- * Logs an warning message
- * @param msg The message to log
- */
- public static void warn(String msg) {
- instance.log(WARN, msg);
- }
-
- /**
- * Logs an info message
- * @param msg The message to log
- */
- public static void info(String msg) {
- instance.log(INFO, msg);
- }
-
- /**
- * Logs an debug message
- * @param msg The message to log
- */
- public static void debug(String msg) {
- instance.log(DEBUG, msg);
- }
-
- /**
- * Logs an internal message
- * @param msg The message to log
- */
- public static void internal(String msg) {
- instance.log(INTERNAL, msg);
- }
-
-
- /**
- * Appends the text representation of the log level
- * @param sb The stringbuffer to append to
- * @param level The log level
- */
- protected static void appendLevelString(StringBuffer sb, short level) {
- switch(level) {
- case DEBUG:
- sb.append("DEBG"); break;
- case INFO:
- sb.append("INFO"); break;
- case INTERNAL:
- sb.append("INT "); break;
- case WARN:
- sb.append("WARN"); break;
- case ERROR:
- sb.append("ERR "); break;
- }
- }
-
- /**
- * Formats a message for logging. Appends the current date+time
- * and the log level string.
- * @param level The log level
- * @param msg The message to log
- */
- protected static String formatMessage(short level, String msg) {
-
- StringBuffer sb = new StringBuffer();
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(
- new Date(), sb, new FieldPosition(0));
-
- sb.append(" [");
- appendLevelString(sb, level);
- sb.append(":");
- sb.append(Thread.currentThread().getId());
- sb.append("] ");
- sb.append(msg);
- return sb.toString();
- }
-
- /**
- * Logs a message by passing the log level explicitly
- * @param level The log level
- * @param msg The message to log
- */
- public static void logByLevel(short level, String msg) {
- instance.log(level, msg);
- }
-
- /**
- * Performs the actual logging. Subclasses should override
- * this method.
- * @param level The log level
- * @param msg The message to log
- */
- protected synchronized void log(short level, String msg) {
- if(level > logLevel) return;
- System.err.println(formatMessage(level, msg));
- }
-}
-
+++ /dev/null
-package org.opensrf.util;
-
-import java.util.Map;
-import java.util.HashMap;
-
-
-/**
- * Generic OpenSRF network-serializable object. This allows
- * access to object fields.
- */
-public class OSRFObject extends HashMap<String, Object> implements OSRFSerializable {
-
- /** This objects registry */
- private OSRFRegistry registry;
-
- public OSRFObject() {
- }
-
-
- /**
- * Creates a new object with the provided registry
- */
- public OSRFObject(OSRFRegistry reg) {
- this();
- registry = reg;
- }
-
-
- /**
- * Creates a new OpenSRF object based on the net class string
- * */
- public OSRFObject(String netClass) {
- this(OSRFRegistry.getRegistry(netClass));
- }
-
-
- /**
- * @return This object's registry
- */
- public OSRFRegistry getRegistry() {
- return registry;
- }
-
- /**
- * Implement get() to fulfill our contract with OSRFSerializable
- */
- public Object get(String field) {
- return super.get(field);
- }
-
- /** Returns the string value found at the given field */
- public String getString(String field) {
- return (String) get(field);
- }
-
- /** Returns the int value found at the given field */
- public int getInt(String field) {
- Object o = get(field);
- if(o instanceof String)
- return Integer.parseInt((String) o);
- return ((Integer) get(field)).intValue();
- }
-}
+++ /dev/null
-package org.opensrf.util;
-
-import java.io.Serializable;
-import java.util.Map;
-import java.util.HashMap;
-
-
-/**
- * Manages the registration of OpenSRF network-serializable objects.
- * A serializable object has a class "hint" (called netClass within) which
- * describes the type of object. Each object also has a set of field names
- * for accessing/mutating object properties. Finally, objects have a
- * serialization wire protocol. Currently supported protocols are HASH
- * and ARRAY.
- */
-public class OSRFRegistry implements Serializable{
-
-
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- /**
- * Global collection of registered net objects.
- * Maps netClass names to registries.
- */
- private static HashMap<String, OSRFRegistry>
- registry = new HashMap<String, OSRFRegistry>();
-
-
- /** Serialization types for registered objects */
- public enum WireProtocol {
- ARRAY, HASH
- };
-
-
- /** Array of field names for this registered object */
- String fields[];
- /** The wire protocol for this object */
- WireProtocol wireProtocol;
- /** The network class for this object */
- String netClass;
-
- /**
- * Returns the array of field names
- */
- public String[] getFields() {
- return this.fields;
- }
-
-
- /**
- * Registers a new object.
- * @param netClass The net class for this object
- * @param wireProtocol The object's wire protocol
- * @param fields An array of field names. For objects whose
- * wire protocol is ARRAY, the positions of the field names
- * will be used as the array indices for the fields at serialization time
- */
- public static OSRFRegistry registerObject(String netClass, WireProtocol wireProtocol, String fields[]) {
- OSRFRegistry r = new OSRFRegistry(netClass, wireProtocol, fields);
- registry.put(netClass, r);
- return r;
- }
-
- /**
- * Returns the registry for the given netclass
- * @param netClass The network class to lookup
- */
- public static OSRFRegistry getRegistry(String netClass) {
- if( netClass == null ) return null;
- return (OSRFRegistry) registry.get(netClass);
- }
-
-
- /**
- * @param field The name of the field to lookup
- * @return the index into the fields array of the given field name.
- */
- public int getFieldIndex(String field) {
- for( int i = 0; i < fields.length; i++ )
- if( fields[i].equals(field) )
- return i;
- return -1;
- }
-
- /** Returns the wire protocol of this object */
- public WireProtocol getWireProtocol() {
- return this.wireProtocol;
- }
-
- /** Returns the netClass ("hint") of this object */
- public String getNetClass() {
- return this.netClass;
- }
-
- /**
- * Creates a new registry object.
- * @param netClass The network class/hint
- * @param wireProtocol The wire protocol
- * @param fields The array of field names. For array-based objects,
- * the fields array must be sorted in accordance with the sorting
- * of the objects in the array.
- */
- public OSRFRegistry(String netClass, WireProtocol wireProtocol, String fields[]) {
- this.netClass = netClass;
- this.wireProtocol = wireProtocol;
- this.fields = fields;
- }
-}
-
-
+++ /dev/null
-package org.opensrf.util;
-
-/**
- * All network-serializable OpenSRF object must implement this interface.
- */
-public interface OSRFSerializable {
-
- /**
- * Returns the object registry object for the implementing class.
- */
- public abstract OSRFRegistry getRegistry();
-
- /**
- * Returns the object found at the given field
- */
- public abstract Object get(String field);
-}
-
-
+++ /dev/null
-package org.opensrf.util;
-import org.opensrf.*;
-import java.util.Map;
-
-/**
- * Connects to the OpenSRF Settings server to fetch the settings config.
- * Provides a Config interface for fetching settings via path
- */
-public class SettingsClient extends Config {
-
- /** Singleton SettingsClient instance */
- private static SettingsClient client = new SettingsClient();
-
- public SettingsClient() {
- super("");
- }
-
- /**
- * @return The global settings client instance
- */
- public static SettingsClient instance() throws ConfigException {
- if(client.getConfigObject() == null)
- client.fetchConfig();
- return client;
- }
-
- /**
- * Fetches the settings object from the settings server
- */
- private void fetchConfig() throws ConfigException {
-
- ClientSession ses = new ClientSession("opensrf.settings");
- try {
-
- Request req = ses.request(
- "opensrf.settings.host_config.get",
- new String[]{(String)Config.global().getFirst("/domain")});
-
- Result res = req.recv(12000);
- if(res == null) {
- /** throw exception */
- }
- setConfigObject((Map) res.getContent());
-
- } catch(Exception e) {
- throw new ConfigException("Error fetching settings config", e);
-
- } finally {
- ses.cleanup();
- }
- }
-}
-
+++ /dev/null
-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();
- }
-
-
- /**
- * Escapes a string.
- */
- 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);
- }
- }
- }
- }
-
-
- /**
- * Descends into the map along the given XPATH-style path
- * and returns the object found there.
- * @param path The XPATH-style path to search. Path
- * components are separated by '/' characters.
- * Example: /opensrf/loglevel
- * @return The found object.
- */
-
- public static Object findPath(Map map, String path) {
- String keys[] = path.split("/", -1);
- int i = 0;
- if(path.charAt(0) == '/') i++;
- for(; i < keys.length - 1; i++ )
- map = (Map) map.get(keys[i]);
-
- return map.get(keys[i]);
- }
-}
-
-
-
+++ /dev/null
-package org.opensrf.util;
-
-import javax.xml.stream.*;
-import javax.xml.stream.events.* ;
-import javax.xml.namespace.QName;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.ListIterator;
-import java.io.InputStream;
-import org.opensrf.util.JSONWriter;
-import org.opensrf.util.JSONReader;
-
-/**
- * Flattens an XML file into a properties map. Values are stored as JSON strings or arrays.
- * An array is created if more than one value resides at the same key.
- * e.g. html.head.script = "alert('hello');"
- */
-public class XMLFlattener {
-
- /** Flattened properties map */
- private Map<String, String> props;
- /** Incoming XML stream */
- private InputStream inStream;
- /** Runtime list of encountered elements */
- private List<String> elementList;
-
- /**
- * Creates a new reader. Initializes the message queue.
- * Sets the stream state to disconnected, and the xml
- * state to in_nothing.
- * @param inStream the inbound XML stream
- */
- public XMLFlattener(InputStream inStream) {
- props = new HashMap<String, String>();
- this.inStream = inStream;
- elementList = new ArrayList<String>();
- }
-
- /** Turns an array of strings into a dot-separated key string */
- private String listToString() {
- ListIterator itr = elementList.listIterator();
- StringBuffer sb = new StringBuffer();
- while(itr.hasNext()) {
- sb.append(itr.next());
- if(itr.hasNext())
- sb.append(".");
- }
- return sb.toString();
- }
-
- /**
- * Parses XML data from the provided stream.
- */
- public Map read() throws javax.xml.stream.XMLStreamException {
-
- XMLInputFactory factory = XMLInputFactory.newInstance();
-
- /** disable as many unused features as possible to speed up the parsing */
- factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
- factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
- factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
-
- /** create the stream reader */
- XMLStreamReader reader = factory.createXMLStreamReader(inStream);
- int eventType;
-
- while(reader.hasNext()) {
- /** cycle through the XML events */
-
- eventType = reader.next();
- if(reader.isWhiteSpace()) continue;
-
- switch(eventType) {
-
- case XMLEvent.START_ELEMENT:
- elementList.add(reader.getName().toString());
- break;
-
- case XMLEvent.CHARACTERS:
- String text = reader.getText();
- String key = listToString();
-
- if(props.containsKey(key)) {
-
- /* something in the map already has this key */
-
- Object o = null;
- try {
- o = new JSONReader(props.get(key)).read();
- } catch(org.opensrf.util.JSONException e){}
-
- if(o instanceof List) {
- /* if the map contains a list, append to the list and re-encode */
- ((List) o).add(text);
-
- } else {
- /* if the map just contains a string, start building a new list
- * with the old string and append the new string */
- List<String> arr = new ArrayList<String>();
- arr.add((String) o);
- arr.add(text);
- o = arr;
- }
-
- props.put(key, new JSONWriter(o).write());
-
- } else {
- props.put(key, new JSONWriter(text).write());
- }
- break;
-
- case XMLEvent.END_ELEMENT:
- elementList.remove(elementList.size()-1);
- break;
- }
- }
-
- return props;
- }
-}
-
-
-
-
+++ /dev/null
-package org.opensrf.util;
-import javax.xml.transform.*;
-import javax.xml.transform.stream.*;
-import javax.xml.parsers.*;
-import java.io.File;
-import java.io.ByteArrayInputStream;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-
-
-/**
- * Performs XSL transformations.
- * TODO: Add ability to pass in XSL variables
- */
-public class XMLTransformer {
-
- /** The XML to transform */
- private Source xmlSource;
- /** The stylesheet to apply */
- private Source xslSource;
-
- public XMLTransformer(Source xmlSource, Source xslSource) {
- this.xmlSource = xmlSource;
- this.xslSource = xslSource;
- }
-
- public XMLTransformer(String xmlString, File xslFile) {
- this(
- new StreamSource(new ByteArrayInputStream(xmlString.getBytes())),
- new StreamSource(xslFile));
- }
-
- public XMLTransformer(File xmlFile, File xslFile) {
- this(
- new StreamSource(xmlFile),
- new StreamSource(xslFile));
- }
-
- /**
- * Applies the transformation and puts the result into the provided output stream
- */
- public void apply(OutputStream outStream) throws TransformerException, TransformerConfigurationException {
- Result result = new StreamResult(outStream);
- Transformer trans = TransformerFactory.newInstance().newTransformer(xslSource);
- trans.transform(xmlSource, result);
- }
-
- /**
- * Applies the transformation and return the resulting string
- * @return The String created by the XSL transformation
- */
- public String apply() throws TransformerException, TransformerConfigurationException {
- OutputStream outStream = new ByteArrayOutputStream();
- this.apply(outStream);
- return outStream.toString();
- }
-}
-
-
--- /dev/null
+package org.opensrf;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.Arrays;
+
+import org.opensrf.util.*;
+import org.opensrf.net.xmpp.*;
+
+
+/**
+ * Models an OpenSRF client session.
+ */
+public class ClientSession extends Session {
+
+ /** The remote service to communicate with */
+ private String service;
+ /** OpenSRF domain */
+ private String domain;
+ /** Router name */
+ private String router;
+
+ /**
+ * original remote node. The current remote node will change based
+ * on server responses. This is used to reset the remote node to
+ * its original state.
+ */
+ private String origRemoteNode;
+ /** The next request id */
+ private int nextId;
+ /** The requests this session has sent */
+ private Map<Integer, Request> requests;
+
+ /**
+ * Creates a new client session. Initializes the
+ * @param service The remote service.
+ */
+ public ClientSession(String service) throws ConfigException {
+ this(service, null);
+ }
+
+ /**
+ * Creates a new client session. Initializes the
+ * @param service The remote service.
+ * @param locale The locale for this session.
+ */
+ public ClientSession(String service, String locale) throws ConfigException {
+ this.service = service;
+ if(locale != null)
+ setLocale(locale);
+
+ /** generate the remote node string */
+ domain = (String) Config.global().getFirst("/domain");
+ router = Config.global().getString("/router_name");
+ setRemoteNode(router + "@" + domain + "/" + service);
+ origRemoteNode = getRemoteNode();
+
+
+ /** create a random thread */
+ long time = new Date().getTime();
+ Random rand = new Random(time);
+ setThread(rand.nextInt()+""+rand.nextInt()+""+time+Thread.currentThread().getId());
+
+ nextId = 0;
+ requests = new HashMap<Integer, Request>();
+ cacheSession();
+ }
+
+ /**
+ * Creates a new request to send to our remote service.
+ * @param method The method API name
+ * @param params The list of method parameters
+ * @return The request object.
+ */
+ public Request request(String method, List<Object> params) throws SessionException {
+ return request(new Request(this, nextId++, method, params));
+ }
+
+ /**
+ * Creates a new request to send to our remote service.
+ * @param method The method API name
+ * @param params The list of method parameters
+ * @return The request object.
+ */
+ public Request request(String method, Object[] params) throws SessionException {
+ return request(new Request(this, nextId++, method, Arrays.asList(params)));
+ }
+
+
+ /**
+ * Creates a new request to send to our remote service.
+ * @param method The method API name
+ * @return The request object.
+ */
+ public Request request(String method) throws SessionException {
+ return request(new Request(this, nextId++, method));
+ }
+
+
+ private Request request(Request req) throws SessionException {
+ if(getConnectState() != ConnectState.CONNECTED)
+ resetRemoteId();
+ requests.put(new Integer(req.getId()), req);
+ req.send();
+ return req;
+ }
+
+
+ /**
+ * Resets the remoteNode to its original state.
+ */
+ public void resetRemoteId() {
+ setRemoteNode(origRemoteNode);
+ }
+
+
+ /**
+ * Pushes a response onto the result queue of the appropriate request.
+ * @param msg The received RESULT Message
+ */
+ public void pushResponse(Message msg) {
+
+ Request req = findRequest(msg.getId());
+ if(req == null) {
+ /** LOG that we've received a result to a non-existant request */
+ System.err.println(msg.getId() +" has no corresponding request");
+ return;
+ }
+ OSRFObject payload = (OSRFObject) msg.get("payload");
+
+ /** build a result and push it onto the request's result queue */
+ req.pushResponse(
+ new Result(
+ payload.getString("status"),
+ payload.getInt("statusCode"),
+ payload.get("content")
+ )
+ );
+ }
+
+ public Request findRequest(int reqId) {
+ return requests.get(new Integer(reqId));
+ }
+
+ /**
+ * Removes a request for this session's request set
+ */
+ public void cleanupRequest(int reqId) {
+ requests.remove(new Integer(reqId));
+ }
+
+ public void setRequestComplete(int reqId) {
+ Request req = findRequest(reqId);
+ if(req == null) return;
+ req.setComplete();
+ }
+
+ public static Object atomicRequest(String service, String method, Object[] params) throws MethodException {
+ try {
+ ClientSession session = new ClientSession(service);
+ Request osrfRequest = session.request(method, params);
+ Result result = osrfRequest.recv(600000);
+ if(result.getStatusCode() != 200)
+ throw new MethodException(
+ "Request "+service+":"+method+":"+" failed with status code " + result.getStatusCode());
+ return result.getContent();
+ } catch(Exception e) {
+ throw new MethodException(e);
+ }
+ }
+}
+
--- /dev/null
+package org.opensrf;
+import org.opensrf.util.*;
+
+
+public class Message implements OSRFSerializable {
+
+ /** Message types */
+ public static final String REQUEST = "REQUEST";
+ public static final String STATUS = "STATUS";
+ public static final String RESULT = "RESULT";
+ public static final String CONNECT = "CONNECT";
+ public static final String DISCONNECT = "DISCONNECT";
+
+ /** Message ID. This number is used to relate requests to responses */
+ private int id;
+ /** type of message. */
+ private String type;
+ /** message payload */
+ private Object payload;
+ /** message locale */
+ private String locale;
+
+ /** Create a registry for the osrfMessage object */
+ private static OSRFRegistry registry =
+ OSRFRegistry.registerObject(
+ "osrfMessage",
+ OSRFRegistry.WireProtocol.HASH,
+ new String[] {"threadTrace", "type", "payload", "locale"});
+
+ /**
+ * @param id This message's ID
+ * @param type The type of message
+ */
+ public Message(int id, String type) {
+ setId(id);
+ setString(type);
+ }
+
+ /**
+ * @param id This message's ID
+ * @param type The type of message
+ * @param payload The message payload
+ */
+ public Message(int id, String type, Object payload) {
+ this(id, type);
+ setPayload(payload);
+ }
+
+ /**
+ * @param id This message's ID
+ * @param type The type of message
+ * @param payload The message payload
+ * @param locale The message locale
+ */
+ public Message(int id, String type, Object payload, String locale) {
+ this(id, type, payload);
+ setPayload(payload);
+ setLocale(locale);
+ }
+
+
+ public int getId() {
+ return id;
+ }
+ public String getType() {
+ return type;
+ }
+ public Object getPayload() {
+ return payload;
+ }
+ public String getLocale() {
+ return locale;
+ }
+ public void setId(int id) {
+ this.id = id;
+ }
+ public void setString(String type) {
+ this.type = type;
+ }
+ public void setPayload(Object p) {
+ payload = p;
+ }
+ public void setLocale(String l) {
+ locale = l;
+ }
+
+ /**
+ * Implements the generic get() API required by OSRFSerializable
+ */
+ public Object get(String field) {
+ if("threadTrace".equals(field))
+ return getId();
+ if("type".equals(field))
+ return getType().toString();
+ if("payload".equals(field))
+ return getPayload();
+ if("locale".equals(field))
+ return getLocale();
+ return null;
+ }
+
+ /**
+ * @return The osrfMessage registry.
+ */
+ public OSRFRegistry getRegistry() {
+ return registry;
+ }
+}
+
+
--- /dev/null
+package org.opensrf;
+import java.util.List;
+import java.util.ArrayList;
+import org.opensrf.util.*;
+
+
+public class Method extends OSRFObject {
+
+ /** The method API name */
+ private String name;
+ /** The ordered list of method params */
+ private List<Object> params;
+
+ /** Create a registry for the osrfMethod object */
+ private static OSRFRegistry registry =
+ OSRFRegistry.registerObject(
+ "osrfMethod",
+ OSRFRegistry.WireProtocol.HASH,
+ new String[] {"method", "params"});
+
+ /**
+ * @param name The method API name
+ */
+ public Method(String name) {
+ this.name = name;
+ this.params = new ArrayList<Object>(8);
+ }
+
+ /**
+ * @param name The method API name
+ * @param params The ordered list of params
+ */
+ public Method(String name, List<Object> params) {
+ this.name = name;
+ this.params = params;
+ }
+
+ /**
+ * @return The method API name
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * @return The ordered list of params
+ */
+ public List<Object> 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 addParam(Object p) {
+ this.params.add(p);
+ }
+
+ /**
+ * Implements the generic get() API required by OSRFSerializable
+ */
+ public Object get(String field) {
+ if("method".equals(field))
+ return getName();
+ if("params".equals(field))
+ return getParams();
+ return null;
+ }
+
+ /**
+ * @return The osrfMethod registry.
+ */
+ public OSRFRegistry getRegistry() {
+ return registry;
+ }
+
+}
+
--- /dev/null
+package org.opensrf;
+
+/**
+ * Thrown when the server responds with a method exception.
+ */
+public class MethodException extends Exception {
+ public MethodException(String info) {
+ super(info);
+ }
+ public MethodException(Throwable cause) {
+ super(cause);
+ }
+}
+
--- /dev/null
+package org.opensrf;
+import java.util.List;
+import java.util.ArrayList;
+import org.opensrf.util.ConfigException;
+
+public class MultiSession {
+
+ class RequestContainer {
+ Request request;
+ int id;
+ RequestContainer(Request r) {
+ request = r;
+ }
+ }
+
+ private boolean complete;
+ private List<RequestContainer> requests;
+ private int lastId;
+
+ public MultiSession() {
+ requests = new ArrayList<RequestContainer>();
+ }
+
+ public boolean isComplete() {
+ return complete;
+ }
+
+ public int lastId() {
+ return lastId;
+ }
+
+ /**
+ * Adds a new request to the set of requests.
+ * @param service The OpenSRF service
+ * @param method The OpenSRF method
+ * @param params The array of method params
+ * @return The request ID, which is used to map results from recv() to the original request.
+ */
+ public int request(String service, String method, Object[] params) throws SessionException, ConfigException {
+ ClientSession ses = new ClientSession(service);
+ return request(ses.request(method, params));
+ }
+
+
+ public int request(String service, String method) throws SessionException, ConfigException {
+ ClientSession ses = new ClientSession(service);
+ return request(ses.request(method));
+ }
+
+ private int request(Request req) {
+ RequestContainer c = new RequestContainer(req);
+ c.id = requests.size();
+ requests.add(c);
+ return c.id;
+ }
+
+
+ /**
+ * Calls recv on all pending requests until there is data to return. The ID which
+ * maps the received object to the request can be retrieved by calling lastId().
+ * @param millis Number of milliseconds to wait for some data to arrive.
+ * @return The object result or null if all requests are complete
+ * @throws MethodException Thrown if no response is received within
+ * the given timeout or the method fails.
+ */
+ public Object recv(int millis) throws MethodException {
+ if(complete) return null;
+
+ Request req = null;
+ Result res = null;
+ RequestContainer cont = null;
+
+ long duration = 0;
+ long blockTime = 100;
+
+ /* if there is only 1 outstanding request, don't poll */
+ if(requests.size() == 1)
+ blockTime = millis;
+
+ while(true) {
+ for(int i = 0; i < requests.size(); i++) {
+
+ cont = requests.get(i);
+ req = cont.request;
+
+ try {
+ if(i == 0) {
+ res = req.recv(blockTime);
+ } else {
+ res = req.recv(0);
+ }
+ } catch(SessionException e) {
+ throw new MethodException(e);
+ }
+
+ if(res != null) break;
+ }
+
+ if(res != null) break;
+ duration += blockTime;
+
+ if(duration >= millis) {
+ System.out.println("duration = " + duration + " millis = " + millis);
+ throw new MethodException("No request received within " + millis + " milliseconds");
+ }
+ }
+
+ if(res.getStatusCode() != 200) {
+ throw new MethodException("Request " + cont.id + " failed with status code " +
+ res.getStatusCode() + " and status message " + res.getStatus());
+ }
+
+ if(req.isComplete())
+ requests.remove(requests.indexOf(cont));
+
+ if(requests.size() == 0)
+ complete = true;
+
+ lastId = cont.id;
+ return res.getContent();
+ }
+}
+
--- /dev/null
+package org.opensrf;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.List;
+import java.util.Date;
+import org.opensrf.net.xmpp.XMPPException;
+import org.opensrf.util.Logger;
+
+public class Request {
+
+ /** This request's controlling session */
+ private ClientSession session;
+ /** The method */
+ private Method method;
+ /** The ID of this request */
+ private int id;
+ /** Queue of Results */
+ private Queue<Result> resultQueue;
+ /** If true, the receive timeout for this request should be reset */
+ private boolean resetTimeout;
+
+ /** If true, the server has indicated that this request is complete. */
+ private boolean complete;
+
+ /**
+ * @param ses The controlling session for this request.
+ * @param id This request's ID.
+ * @param method The requested method.
+ */
+ public Request(ClientSession ses, int id, Method method) {
+ this.session = ses;
+ this.id = id;
+ this.method = method;
+ resultQueue = new ConcurrentLinkedQueue<Result>();
+ complete = false;
+ resetTimeout = false;
+ }
+
+ /**
+ * @param ses The controlling session for this request.
+ * @param id This request's ID.
+ * @param methodName The requested method's API name.
+ */
+ public Request(ClientSession ses, int id, String methodName) {
+ this(ses, id, new Method(methodName));
+ }
+
+ /**
+ * @param ses The controlling session for this request.
+ * @param id This request's ID.
+ * @param methodName The requested method's API name.
+ * @param params The list of request params
+ */
+ public Request(ClientSession ses, int id, String methodName, List<Object> params) {
+ this(ses, id, new Method(methodName, params));
+ }
+
+ /**
+ * Sends the request to the server.
+ */
+ public void send() throws SessionException {
+ session.send(new Message(id, Message.REQUEST, method, session.getLocale()));
+ }
+
+ /**
+ * Receives the next result for this request. This method
+ * will wait up to the specified number of milliseconds for
+ * a response.
+ * @param millis Number of milliseconds to wait for a result. If
+ * negative, this method will wait indefinitely.
+ * @return The result or null if none arrives in time
+ */
+ public Result recv(long millis) throws SessionException, MethodException {
+
+ Result result = null;
+
+ if((result = resultQueue.poll()) != null)
+ return result;
+
+ if(millis < 0 && !complete) {
+ /** wait potentially forever for a result to arrive */
+ while(!complete) {
+ session.waitForMessage(millis);
+ if((result = resultQueue.poll()) != null)
+ return result;
+ }
+
+ } else {
+
+ while(millis >= 0 && !complete) {
+
+ /** wait up to millis milliseconds for a result. waitForMessage()
+ * will return if a response to any request arrives, so we keep track
+ * of how long we've been waiting in total for a response to
+ * this request */
+
+ long start = new Date().getTime();
+ session.waitForMessage(millis);
+ millis -= new Date().getTime() - start;
+ if((result = resultQueue.poll()) != null)
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Pushes a result onto the result queue
+ * @param result The result to push
+ */
+ public void pushResponse(Result result) {
+ resultQueue.offer(result);
+ }
+
+ /**
+ * @return This request's ID
+ */
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Removes this request from the controlling session's request set
+ */
+ public void cleanup() {
+ session.cleanupRequest(id);
+ }
+
+ /** Sets this request as complete */
+ public void setComplete() {
+ complete = true;
+ }
+
+ public boolean isComplete() {
+ return complete;
+ }
+}
--- /dev/null
+package org.opensrf;
+import org.opensrf.util.*;
+
+
+/**
+ * Models a single result from a method request.
+ */
+public class Result implements OSRFSerializable {
+
+ /** Method result content */
+ private Object content;
+ /** Name of the status */
+ private String status;
+ /** Status code number */
+ private int statusCode;
+
+
+ /** Register this object */
+ private static OSRFRegistry registry =
+ OSRFRegistry.registerObject(
+ "osrfResult",
+ OSRFRegistry.WireProtocol.HASH,
+ new String[] {"status", "statusCode", "content"});
+
+
+ /**
+ * @param status The status message for this result
+ * @param statusCode The status code
+ * @param content The content of the result
+ */
+ 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;
+ }
+
+ /**
+ * Implements the generic get() API required by OSRFSerializable
+ */
+ public Object get(String field) {
+ if("status".equals(field))
+ return getStatus();
+ if("statusCode".equals(field))
+ return getStatusCode();
+ if("content".equals(field))
+ return getContent();
+ return null;
+ }
+
+ /**
+ * @return The osrfMethod registry.
+ */
+ public OSRFRegistry getRegistry() {
+ return registry;
+ }
+
+}
+
--- /dev/null
+package org.opensrf;
+
+/**
+ * Models an OpenSRF server session.
+ */
+public class ServerSession extends Session {
+}
+
--- /dev/null
+package org.opensrf;
+import org.opensrf.util.JSONWriter;
+import org.opensrf.net.xmpp.*;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Arrays;
+
+public abstract class Session {
+
+ /** Represents the different connection states for a session */
+ public enum ConnectState {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED
+ };
+
+ /** local cache of existing sessions */
+ private static Map<String, Session>
+ sessionCache = new HashMap<String, Session>();
+
+ /** the current connection state */
+ private ConnectState connectState;
+
+ /** The address of the remote party we are communicating with */
+ private String remoteNode;
+
+ /** Session locale */
+ protected String locale;
+ /** Default session locale */
+ protected static String defaultLocale = "en-US";
+
+ /**
+ * 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 SessionException {
+
+ /** construct the XMPP message */
+ XMPPMessage xmsg = new XMPPMessage();
+ xmsg.setTo(remoteNode);
+ xmsg.setThread(thread);
+ xmsg.setBody(new JSONWriter(Arrays.asList(new Message[] {omsg})).write());
+
+ try {
+ XMPPSession.getThreadSession().send(xmsg);
+ } catch(XMPPException e) {
+ connectState = ConnectState.DISCONNECTED;
+ throw new SessionException("Error sending message to " + remoteNode, e);
+ }
+ }
+
+ /**
+ * 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) throws SessionException, MethodException {
+ try {
+ Stack.processXMPPMessage(
+ XMPPSession.getThreadSession().recv(millis));
+ } catch(XMPPException e) {
+ throw new SessionException("Error waiting for message", e);
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Puts this session into session cache.
+ */
+ protected void cacheSession() {
+ sessionCache.put(thread, this);
+ }
+
+ /**
+ * Sets the remote address
+ * @param nodeName The name of the remote node.
+ */
+ public void setRemoteNode(String nodeName) {
+ remoteNode = nodeName;
+ }
+ /**
+ * @return The remote node
+ */
+ public String getRemoteNode() {
+ return remoteNode;
+ }
+
+
+ /**
+ * Get thread.
+ * @return thread as String.
+ */
+ public String getThread() {
+ return thread;
+ }
+
+ /**
+ * Set thread.
+ * @param thread the value to set.
+ */
+ public void setThread(String thread) {
+ this.thread = thread;
+ }
+
+ /**
+ * Get locale.
+ * @return locale as String.
+ */
+ public String getLocale() {
+ if(locale == null)
+ return defaultLocale;
+ return locale;
+ }
+
+ /**
+ * Set locale.
+ * @param locale the value to set.
+ */
+ public void setLocale(String locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Get defaultLocale.
+ * @return defaultLocale as String.
+ */
+ public String getDefaultLocale() {
+ return defaultLocale;
+ }
+
+ /**
+ * Set defaultLocale.
+ * @param defaultLocale the value to set.
+ */
+ public void setDefaultLocale(String defaultLocale) {
+ this.defaultLocale = defaultLocale;
+ }
+
+
+ /**
+ * Get connectState.
+ * @return connectState as ConnectState.
+ */
+ public ConnectState getConnectState() {
+ return connectState;
+ }
+
+ /**
+ * Set connectState.
+ * @param connectState the value to set.
+ */
+ public void setConnectState(ConnectState connectState) {
+ this.connectState = connectState;
+ }
+}
--- /dev/null
+package org.opensrf;
+/**
+ * Used by sessions to indicate communication errors
+ */
+public class SessionException extends Exception {
+ public SessionException(String info) {
+ super(info);
+ }
+ public SessionException(String info, Throwable cause) {
+ super(info, cause);
+ }
+}
+
--- /dev/null
+package org.opensrf;
+import org.opensrf.net.xmpp.XMPPMessage;
+import org.opensrf.util.*;
+import java.util.Date;
+import java.util.List;
+import java.util.Iterator;
+
+
+public class Stack {
+
+ public static void processXMPPMessage(XMPPMessage msg) throws MethodException {
+
+ if(msg == null) return;
+
+ //System.out.println(msg.getBody());
+
+ /** fetch this session from the cache */
+ Session ses = Session.findCachedSession(msg.getThread());
+
+ if(ses == null) {
+ /** inbound client request, create a new server session */
+ return;
+ }
+
+ /** parse the JSON message body, which should result in a list of OpenSRF messages */
+ List msgList;
+
+ try {
+ msgList = new JSONReader(msg.getBody()).readArray();
+ } catch(JSONException e) {
+ /** XXX LOG error */
+ e.printStackTrace();
+ return;
+ }
+
+ Iterator itr = msgList.iterator();
+
+ OSRFObject obj = null;
+ long start = new Date().getTime();
+
+ /** cycle through the messages and push them up the stack */
+ while(itr.hasNext()) {
+
+ /** Construct a Message object from the parsed generic OSRFObject */
+ obj = (OSRFObject) itr.next();
+
+ processOSRFMessage(
+ ses,
+ new Message(
+ obj.getInt("threadTrace"),
+ obj.getString("type"),
+ obj.get("payload")
+ )
+ );
+ }
+
+ /** LOG the duration */
+ }
+
+ private static void processOSRFMessage(Session ses, Message msg) throws MethodException {
+
+ Logger.debug("received id=" + msg.getId() +
+ " type=" + msg.getType() + " payload=" + msg.getPayload());
+
+ if( ses instanceof ClientSession )
+ processResponse((ClientSession) ses, msg);
+ else
+ processRequest((ServerSession) ses, msg);
+ }
+
+ /**
+ * Process a server response
+ */
+ private static void processResponse(ClientSession session, Message msg) throws MethodException {
+ String type = msg.getType();
+
+ if(msg.RESULT.equals(type)) {
+ session.pushResponse(msg);
+ return;
+ }
+
+ if(msg.STATUS.equals(type)) {
+
+ OSRFObject obj = (OSRFObject) msg.getPayload();
+ Status stat = new Status(obj.getString("status"), obj.getInt("statusCode"));
+ int statusCode = stat.getStatusCode();
+ String status = stat.getStatus();
+
+ switch(statusCode) {
+ case Status.COMPLETE:
+ session.setRequestComplete(msg.getId());
+ break;
+ case Status.NOTFOUND:
+ session.setRequestComplete(msg.getId());
+ throw new MethodException(status);
+ }
+ }
+ }
+
+ /**
+ * Process a client request
+ */
+ private static void processRequest(ServerSession session, Message msg) {
+ }
+}
--- /dev/null
+package org.opensrf;
+import org.opensrf.util.*;
+
+public class Status {
+
+ public static final int CONTINUE = 100;
+ public static final int OK = 200;
+ public static final int ACCEPTED = 202;
+ public static final int COMPLETE = 205;
+ public static final int REDIRECTED = 307;
+ public static final int EST = 400;
+ public static final int STATUS_UNAUTHORIZED = 401;
+ public static final int FORBIDDEN = 403;
+ public static final int NOTFOUND = 404;
+ public static final int NOTALLOWED = 405;
+ public static final int TIMEOUT = 408;
+ public static final int EXPFAILED = 417;
+ public static final int INTERNALSERVERERROR = 500;
+ public static final int NOTIMPLEMENTED = 501;
+ public static final int VERSIONNOTSUPPORTED = 505;
+
+ private OSRFRegistry registry = OSRFRegistry.registerObject(
+ "osrfConnectStatus",
+ OSRFRegistry.WireProtocol.HASH,
+ new String[] {"status", "statusCode"});
+
+ /** The name of the status */
+ String status;
+ /** The status code */
+ int statusCode;
+
+ public Status(String status, int statusCode) {
+ this.status = status;
+ this.statusCode = statusCode;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * Implements the generic get() API required by OSRFSerializable
+ */
+ public Object get(String field) {
+ if("status".equals(field))
+ return getStatus();
+ if("statusCode".equals(field))
+ return new Integer(getStatusCode());
+ return null;
+ }
+
+ /**
+ * @return The osrfMessage registry.
+ */
+ public OSRFRegistry getRegistry() {
+ return registry;
+ }
+}
+
+
--- /dev/null
+package org.opensrf;
+
+import org.opensrf.util.*;
+import org.opensrf.net.xmpp.*;
+import java.util.Random;
+import java.util.Date;
+import java.net.InetAddress;
+
+
+public class Sys {
+
+ private static void initLogger(Config config) {
+ if(Logger.instance() == null) {
+ try {
+ String logFile = config.getString("/logfile");
+ int logLevel = config.getInt("/loglevel");
+ Logger.init( (short) config.getInt("/loglevel"), new FileLogger(logFile));
+ /** add syslog support... */
+ } catch(Exception e) {
+ /* by default, log to stderr at WARN level */
+ Logger.init(Logger.WARN, new Logger());
+ }
+ }
+ }
+
+ /**
+ * Connects to the OpenSRF network so that client sessions may communicate.
+ * @param configFile The OpenSRF config file
+ * @param configContext Where in the XML document the config chunk lives. This
+ * allows an OpenSRF client config chunk to live in XML files where other config
+ * information lives.
+ */
+ public static void bootstrapClient(String configFile, String configContext)
+ throws ConfigException, SessionException {
+
+
+ /** see if the current thread already has a connection */
+ XMPPSession existing = XMPPSession.getThreadSession();
+ if(existing != null && existing.connected())
+ return;
+
+ /** create the config parser */
+ Config config = new Config(configContext);
+ config.parse(configFile);
+ Config.setConfig(config); /* set this as the global config */
+
+ initLogger(config);
+
+ /** Collect the network connection info from the config */
+ String username = config.getString("/username");
+ String passwd = config.getString("/passwd");
+ String host = (String) config.getFirst("/domain");
+ int port = config.getInt("/port");
+
+
+ /** Create a random login resource string */
+ String res = "java_";
+ try {
+ res += InetAddress.getLocalHost().getHostAddress();
+ } catch(java.net.UnknownHostException e) {}
+ res += "_"+Math.abs(new Random(new Date().getTime()).nextInt())
+ + "_t"+ Thread.currentThread().getId();
+
+
+
+ try {
+
+ /** Connect to the Jabber network */
+ Logger.info("attempting to create XMPP session "+username+"@"+host+"/"+res);
+ XMPPSession xses = new XMPPSession(host, port);
+ xses.connect(username, passwd, res);
+ XMPPSession.setThreadSession(xses);
+
+ } catch(XMPPException e) {
+ throw new SessionException("Unable to bootstrap client", e);
+ }
+ }
+
+ /**
+ * Shuts down the connection to the opensrf network
+ */
+ public static void shutdown() {
+ XMPPSession.getThreadSession().disconnect();
+ }
+}
+
--- /dev/null
+package org.opensrf.net.http;
+
+import android.util.Log;
+import org.json.JSONObject;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+import java.io.IOException;
+import java.io.BufferedInputStream;
+import java.io.OutputStreamWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URI;
+import java.net.HttpURLConnection;
+import java.lang.StringBuffer;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class GatewayRequest extends HttpRequest {
+
+ private boolean readComplete;
+ private String TAG = GatewayRequest.class.getName();
+
+ public GatewayRequest(HttpConnection conn, String service, Method method) {
+ super(conn, service, method);
+ readComplete = false;
+ }
+
+ public GatewayRequest send() {
+ try {
+
+ String postData = compilePostData(service, method);
+
+ urlConn = (HttpURLConnection) httpConn.url.openConnection();
+ urlConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ urlConn.setDoInput(true);
+ urlConn.setDoOutput(true);
+
+
+
+ OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
+ wr.write(postData);
+ wr.flush();
+ wr.close();
+
+ } catch (java.io.IOException ex) {
+ failed = true;
+ failure = ex;
+ }
+
+ return this;
+ }
+
+ public Object recv() {
+
+ if (readComplete)
+ return nextResponse();
+
+ try {
+
+ InputStream netStream = new BufferedInputStream(urlConn.getInputStream());
+ StringBuffer readBuf = new StringBuffer();
+
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+
+ while ((bytesRead = netStream.read(buffer)) != -1) {
+ readBuf.append(new String(buffer, 0, bytesRead));
+ }
+
+ netStream.close();
+ urlConn = null;
+
+ Map<String,?> result = null;
+
+ //System.out.println("osrf: Received " + readBuf.toString());
+ //Log.d(TAG, "received:" + readBuf.toString());
+ try {
+ result = (Map<String, ?>) new JSONReader(readBuf.toString()).readObject();
+ } catch (org.opensrf.util.JSONException ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ //System.out.println("osrf: Converted object " + result);
+ Log.d(TAG, "service:" + this.service
+ + " method:" + this.method.getName()
+ + " result:" + new JSONObject(result).toString());
+ String status = result.get("status").toString();
+ if (!"200".equals(status)) {
+ failed = true;
+ // failure = <some new exception>
+ }
+
+ // gateway always returns a wrapper array with the full results set
+ responseList = (List) result.get("payload");
+
+ // System.out.println("Response list : " + responseList);
+ } catch (java.io.IOException ex) {
+ failed = true;
+ failure = ex;
+ }
+
+ readComplete = true;
+ return nextResponse();
+ }
+
+ private String compilePostData(String service, Method method) {
+ URI uri = null;
+ StringBuffer postData = new StringBuffer();
+
+ postData.append("service=");
+ postData.append(service);
+ postData.append("&method=");
+ postData.append(method.getName());
+
+ List params = method.getParams();
+ Iterator itr = params.iterator();
+
+ while (itr.hasNext()) {
+ postData.append("¶m=");
+ postData.append(new JSONWriter(itr.next()).write());
+ }
+
+ try {
+ // not using URLEncoder because it replaces ' ' with '+'.
+ uri = new URI("http", "", null, postData.toString(), null);
+ } catch (java.net.URISyntaxException ex) {
+ ex.printStackTrace();
+ }
+
+ return uri.getRawQuery();
+ }
+}
+
+
--- /dev/null
+package org.opensrf.net.http;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+
+/**
+ * Manages connection parameters and thread limiting for opensrf json gateway connections.
+ */
+
+public class HttpConnection {
+
+ /** Compiled URL object */
+ protected URL url;
+ /** Number of threads currently communicating with the server */
+ protected int activeThreads;
+ /** Queue of pending async requests */
+ protected Queue<HttpRequest> pendingThreadQueue;
+ /** maximum number of actively communicating threads allowed */
+ protected int maxThreads = 10;
+
+ public HttpConnection(String fullUrl) throws java.net.MalformedURLException {
+ activeThreads = 0;
+ pendingThreadQueue = new ConcurrentLinkedQueue();
+ url = new URL(fullUrl);
+ }
+
+ public int getMaxThreads() {
+ return maxThreads;
+ }
+
+ /**
+ * Set the maximum number of actively communicating threads allowed
+ */
+ public void setMaxThreads(int max) {
+ maxThreads = max;
+ }
+
+ /**
+ * Launches or queues an asynchronous request.
+ *
+ * If the maximum active thread count has not been reached,
+ * start a new thread and use it to send and receive the request.
+ * The response is passed to the request's HttpRequestHandler
+ * onComplete(). After complete, if the number of active threads
+ * is still lower than the max, one request will be pulled (if
+ * present) from the async queue and fired.
+ *
+ * If there are too many active threads, the main request is
+ * pushed onto the async queue for later processing
+ */
+ protected void manageAsyncRequest(final HttpRequest request) {
+
+ if (activeThreads >= maxThreads) {
+ pendingThreadQueue.offer(request);
+ return;
+ }
+
+ activeThreads++;
+
+ //Send the request receive the response, fire off the next
+ //thread if necessary, then pass the result to the handler
+ Runnable r = new Runnable() {
+ public void run() {
+
+ Object response;
+ request.send();
+
+ while ((response = request.recv()) != null) {
+ if (request.handler != null)
+ request.handler.onResponse(request, response);
+ }
+
+ if (request.handler != null)
+ request.handler.onComplete(request);
+
+ activeThreads--;
+
+ if (activeThreads < maxThreads) {
+ try {
+ manageAsyncRequest(pendingThreadQueue.remove());
+ } catch (java.util.NoSuchElementException ex) {
+ // may have been gobbled by another thread
+ }
+ }
+ }
+ };
+
+ new Thread(r).start();
+ }
+}
+
+
--- /dev/null
+package org.opensrf.net.http;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+import java.util.List;
+import java.util.LinkedList;
+import java.net.HttpURLConnection;
+
+public abstract class HttpRequest {
+
+ protected String service;
+ protected Method method;
+ protected HttpURLConnection urlConn;
+ protected HttpConnection httpConn;
+ protected HttpRequestHandler handler;
+ protected List<Object> responseList;
+ protected Exception failure;
+ protected boolean failed;
+ protected boolean complete;
+
+ public HttpRequest() {
+ failed = false;
+ complete = false;
+ handler = null;
+ urlConn = null;
+ }
+
+ public HttpRequest(HttpConnection conn, String service, Method method) {
+ this();
+ this.httpConn = conn;
+ this.service = service;
+ this.method = method;
+ }
+
+ public void sendAsync(final HttpRequestHandler handler) {
+ this.handler = handler;
+ httpConn.manageAsyncRequest(this);
+ }
+
+ protected void pushResponse(Object response) {
+ if (responseList == null)
+ responseList = new LinkedList<Object>();
+ responseList.add(response);
+ }
+
+ protected List responses() {
+ return responseList;
+ }
+
+ protected Object nextResponse() {
+ if (complete || failed) return null;
+ if (responseList.size() > 0)
+ return responseList.remove(0);
+ return null;
+ }
+
+ public Exception getFailure() {
+ return failure;
+ }
+
+ public abstract HttpRequest send();
+
+ public abstract Object recv();
+
+ public boolean failed(){
+ return failed;
+ }
+}
+
+
--- /dev/null
+package org.opensrf.net.http;
+
+import java.util.List;
+
+/*
+ * Handler for async gateway responses.
+ */
+public abstract class HttpRequestHandler {
+
+ /**
+ * Called when all responses have been received.
+ *
+ * If discardResponses() returns true, will be passed null.
+ */
+ public void onComplete(HttpRequest request) {
+ }
+
+ /**
+ * Called with each response received from the server.
+ *
+ * @param payload the value returned from the server.
+ */
+ public void onResponse(HttpRequest request, Object response) {
+ }
+}
--- /dev/null
+package org.opensrf.net.xmpp;
+
+/**
+ * Used for XMPP stream/authentication errors
+ */
+public class XMPPException extends Exception {
+ public XMPPException(String info) {
+ super(info);
+ }
+}
--- /dev/null
+package org.opensrf.net.xmpp;
+
+import java.io.*;
+
+/**
+ * Models a single XMPP message.
+ */
+public class XMPPMessage {
+
+ /** Message body */
+ private String body;
+ /** Message recipient */
+ private String to;
+ /** Message sender */
+ private String from;
+ /** Message thread */
+ private String thread;
+ /** Message xid */
+ private String xid;
+
+ public XMPPMessage() {
+ }
+
+ public String getBody() {
+ return body;
+ }
+ public String getTo() {
+ return to;
+ }
+ public String getFrom() {
+ return from;
+ }
+ public String getThread() {
+ return thread;
+ }
+ public String getXid() {
+ return xid;
+ }
+ public void setBody(String body) {
+ this.body = body;
+ }
+ public void setTo(String to) {
+ this.to = to;
+ }
+ public void setFrom(String from) {
+ this.from = from;
+ }
+ public void setThread(String thread) {
+ this.thread = thread;
+ }
+ public void setXid(String xid) {
+ this.xid = xid;
+ }
+
+
+ /**
+ * Generates the XML representation of this message.
+ */
+ public String toXML() {
+ StringBuffer sb = new StringBuffer("<message to='");
+ escapeXML(to, sb);
+ sb.append("' osrf_xid='");
+ escapeXML(xid, sb);
+ sb.append("'><thread>");
+ escapeXML(thread, sb);
+ sb.append("</thread><body>");
+ escapeXML(body, sb);
+ sb.append("</body></message>");
+ return sb.toString();
+ }
+
+
+ /**
+ * Escapes non-valid XML characters.
+ * @param s The string to escape.
+ * @param sb The StringBuffer to append new data to.
+ */
+ private void escapeXML(String s, StringBuffer sb) {
+ if( s == null ) return;
+ char c;
+ int l = s.length();
+ for( int i = 0; i < l; i++ ) {
+ c = s.charAt(i);
+ switch(c) {
+ case '<':
+ sb.append("<");
+ break;
+ case '>':
+ sb.append(">");
+ break;
+ case '&':
+ sb.append("&");
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ }
+}
+
+
--- /dev/null
+package org.opensrf.net.xmpp;
+
+import javax.xml.stream.*;
+import javax.xml.stream.events.* ;
+import javax.xml.namespace.QName;
+import java.util.Queue;
+import java.io.InputStream;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.Date;
+import org.opensrf.util.Logger;
+
+/**
+ * Slim XMPP Stream reader. This reader only understands enough XMPP
+ * to handle logins and recv messages. It's implemented as a StAX parser.
+ * @author Bill Erickson, Georgia Public Library Systems
+ */
+public class XMPPReader implements Runnable {
+
+ /** Queue of received messages. */
+ private Queue<XMPPMessage> msgQueue;
+ /** Incoming XMPP XML stream */
+ private InputStream inStream;
+ /** Current message body */
+ private StringBuffer msgBody;
+ /** Current message thread */
+ private StringBuffer msgThread;
+ /** Current message status */
+ private StringBuffer msgStatus;
+ /** Current message error type */
+ private StringBuffer msgErrType;
+ /** Current message sender */
+ private String msgFrom;
+ /** Current message recipient */
+ private String msgTo;
+ /** Current message error code */
+ private int msgErrCode;
+
+ /** Where this reader currently is in the document */
+ private XMLState xmlState;
+
+ /** The current connect state to the XMPP server */
+ private XMPPStreamState streamState;
+
+
+ /** Used to represent out connection state to the XMPP server */
+ public static enum XMPPStreamState {
+ DISCONNECTED, /* not connected to the server */
+ CONNECT_SENT, /* we've sent the initial connect message */
+ CONNECT_RECV, /* we've received a response to our connect message */
+ AUTH_SENT, /* we've sent an authentication request */
+ CONNECTED /* authentication is complete */
+ };
+
+
+ /** Used to represents where we are in the XML document stream. */
+ public static enum XMLState {
+ IN_NOTHING,
+ IN_BODY,
+ IN_THREAD,
+ IN_STATUS
+ };
+
+
+ /**
+ * Creates a new reader. Initializes the message queue.
+ * Sets the stream state to disconnected, and the xml
+ * state to in_nothing.
+ * @param inStream the inbound XML stream
+ */
+ public XMPPReader(InputStream inStream) {
+ msgQueue = new ConcurrentLinkedQueue<XMPPMessage>();
+ this.inStream = inStream;
+ resetBuffers();
+ xmlState = XMLState.IN_NOTHING;
+ streamState = XMPPStreamState.DISCONNECTED;
+ }
+
+ /**
+ * Change the connect state and notify that a core
+ * event has occurred.
+ */
+ protected void setXMPPStreamState(XMPPStreamState state) {
+ streamState = state;
+ notifyCoreEvent();
+ }
+
+ /**
+ * @return The current stream state of the reader
+ */
+ public XMPPStreamState getXMPPStreamState() {
+ return streamState;
+ }
+
+
+ /**
+ * @return The next message in the queue, or null
+ */
+ public XMPPMessage popMessageQueue() {
+ return (XMPPMessage) msgQueue.poll();
+ }
+
+
+ /**
+ * Initializes the message buffers
+ */
+ private void resetBuffers() {
+ msgBody = new StringBuffer();
+ msgThread = new StringBuffer();
+ msgStatus = new StringBuffer();
+ msgErrType = new StringBuffer();
+ msgFrom = "";
+ msgTo = "";
+ }
+
+
+ /**
+ * Notifies the waiting thread that a core event has occurred.
+ * Each reader should have exactly one dependent session thread.
+ */
+ private synchronized void notifyCoreEvent() {
+ notifyAll();
+ }
+
+
+ /**
+ * Waits up to timeout milliseconds for a core event to occur.
+ * Also, having a message already waiting in the queue
+ * constitutes a core event.
+ * @param timeout The number of milliseconds to wait. If
+ * timeout is negative, waits potentially forever.
+ * @return The number of milliseconds in wait
+ */
+ public synchronized long waitCoreEvent(long timeout) {
+
+ if(msgQueue.peek() != null || timeout == 0) return 0;
+ long start = new Date().getTime();
+
+ try{
+ if(timeout < 0)
+ wait();
+ else
+ wait(timeout);
+ } catch(InterruptedException ie) {}
+
+ return new Date().getTime() - start;
+ }
+
+
+
+ /** Kickoff the thread */
+ public void run() {
+ read();
+ }
+
+
+ /**
+ * Parses XML data from the provided XMPP stream.
+ */
+ public void read() {
+
+ try {
+
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+
+ /** disable as many unused features as possible to speed up the parsing */
+ factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+
+ /** create the stream reader */
+ XMLStreamReader reader = factory.createXMLStreamReader(inStream);
+ int eventType;
+
+ while(reader.hasNext()) {
+ /** cycle through the XML events */
+
+ eventType = reader.next();
+
+ switch(eventType) {
+
+ case XMLEvent.START_ELEMENT:
+ handleStartElement(reader);
+ break;
+
+ case XMLEvent.CHARACTERS:
+ switch(xmlState) {
+ case IN_BODY:
+ msgBody.append(reader.getText());
+ break;
+ case IN_THREAD:
+ msgThread.append(reader.getText());
+ break;
+ case IN_STATUS:
+ msgStatus.append(reader.getText());
+ break;
+ }
+ break;
+
+ case XMLEvent.END_ELEMENT:
+ xmlState = XMLState.IN_NOTHING;
+ if("message".equals(reader.getName().toString())) {
+
+ /** build a message and add it to the message queue */
+ XMPPMessage msg = new XMPPMessage();
+ msg.setFrom(msgFrom);
+ msg.setTo(msgTo);
+ msg.setBody(msgBody.toString());
+ msg.setThread(msgThread.toString());
+
+ Logger.internal("xmpp message from="+msgFrom+" " + msg.getBody());
+
+ msgQueue.offer(msg);
+ resetBuffers();
+ notifyCoreEvent();
+ }
+ break;
+ }
+ }
+
+ } catch(javax.xml.stream.XMLStreamException se) {
+ /* XXX log an error */
+ xmlState = XMLState.IN_NOTHING;
+ streamState = XMPPStreamState.DISCONNECTED;
+ notifyCoreEvent();
+ }
+ }
+
+
+ /**
+ * Handles the start_element event.
+ */
+ private void handleStartElement(XMLStreamReader reader) {
+
+ String name = reader.getName().toString();
+
+ if("message".equals(name)) {
+ xmlState = XMLState.IN_BODY;
+
+ /** add a special case for the opensrf "router_from" attribute */
+ String rf = reader.getAttributeValue(null, "router_from");
+ if( rf != null )
+ msgFrom = rf;
+ else
+ msgFrom = reader.getAttributeValue(null, "from");
+ msgTo = reader.getAttributeValue(null, "to");
+ return;
+ }
+
+ if("body".equals(name)) {
+ xmlState = XMLState.IN_BODY;
+ return;
+ }
+
+ if("thread".equals(name)) {
+ xmlState = XMLState.IN_THREAD;
+ return;
+ }
+
+ if("stream:stream".equals(name)) {
+ setXMPPStreamState(XMPPStreamState.CONNECT_RECV);
+ return;
+ }
+
+ if("iq".equals(name)) {
+ if("result".equals(reader.getAttributeValue(null, "type")))
+ setXMPPStreamState(XMPPStreamState.CONNECTED);
+ return;
+ }
+
+ if("status".equals(name)) {
+ xmlState = XMLState.IN_STATUS;
+ return;
+ }
+
+ if("stream:error".equals(name)) {
+ setXMPPStreamState(XMPPStreamState.DISCONNECTED);
+ return;
+ }
+
+ if("error".equals(name)) {
+ msgErrType.append(reader.getAttributeValue(null, "type"));
+ msgErrCode = Integer.parseInt(reader.getAttributeValue(null, "code"));
+ setXMPPStreamState(XMPPStreamState.DISCONNECTED);
+ return;
+ }
+ }
+}
+
+
+
+
--- /dev/null
+package org.opensrf.net.xmpp;
+
+import java.io.*;
+import java.net.Socket;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * Represents a single XMPP session. Sessions are responsible for writing to
+ * the stream and for managing a stream reader.
+ */
+public class XMPPSession {
+
+ /** Initial jabber message */
+ public static final String JABBER_CONNECT =
+ "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>";
+
+ /** Basic auth message */
+ public static final String JABBER_BASIC_AUTH =
+ "<iq id='123' type='set'><query xmlns='jabber:iq:auth'>" +
+ "<username>%s</username><password>%s</password><resource>%s</resource></query></iq>";
+
+ public static final String JABBER_DISCONNECT = "</stream:stream>";
+
+ private static Map threadConnections = new ConcurrentHashMap();
+
+ /** jabber domain */
+ private String host;
+ /** jabber port */
+ private int port;
+ /** jabber username */
+ private String username;
+ /** jabber password */
+ private String password;
+ /** jabber resource */
+ private String resource;
+
+ /** XMPP stream reader */
+ XMPPReader reader;
+ /** Fprint-capable socket writer */
+ PrintWriter writer;
+ /** Raw socket output stream */
+ OutputStream outStream;
+ /** The raw socket */
+ Socket socket;
+
+ /** The process-wide session. All communication occurs
+ * accross this single connection */
+ private static XMPPSession globalSession;
+
+
+ /**
+ * Creates a new session.
+ * @param host The jabber domain
+ * @param port The jabber port
+ */
+ public XMPPSession( String host, int port ) {
+ this.host = host;
+ this.port = port;
+ }
+
+ /**
+ * Returns the global, process-wide session
+ */
+ /*
+ public static XMPPSession getGlobalSession() {
+ return globalSession;
+ }
+ */
+
+ public static XMPPSession getThreadSession() {
+ return (XMPPSession) threadConnections.get(new Long(Thread.currentThread().getId()));
+ }
+
+ /**
+ * Sets the given session as the global session for the current thread
+ * @param ses The session
+ */
+ public static void setThreadSession(XMPPSession ses) {
+ /* every time we create a new connection, clean up any dead threads.
+ * this is cheaper than cleaning up the dead threads at every access. */
+ cleanupThreadSessions();
+ threadConnections.put(new Long(Thread.currentThread().getId()), ses);
+ }
+
+ /**
+ * Analyzes the threadSession data to see if there are any sessions
+ * whose controlling thread has gone away.
+ */
+ private static void cleanupThreadSessions() {
+ Thread threads[] = new Thread[Thread.activeCount()];
+ Thread.enumerate(threads);
+ for(Iterator i = threadConnections.keySet().iterator(); i.hasNext(); ) {
+ boolean found = false;
+ Long id = (Long) i.next();
+ for(Thread t : threads) {
+ if(t.getId() == id.longValue()) {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ threadConnections.remove(id);
+ }
+ }
+
+ /**
+ * Sets the global, process-wide section
+ */
+ /*
+ 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 &&
+ !socket.isClosed()
+ );
+ }
+
+
+ /**
+ * Connects to the network.
+ * @param username The jabber username
+ * @param password The jabber password
+ * @param resource The Jabber resource
+ */
+ public void connect(String username, String password, String resource) throws XMPPException {
+
+ this.username = username;
+ this.password = password;
+ this.resource = resource;
+
+ try {
+ /* open the socket and associated streams */
+ socket = new Socket(host, port);
+
+ /** the session maintains control over the output stream */
+ outStream = socket.getOutputStream();
+ writer = new PrintWriter(outStream, true);
+
+ /** pass the input stream to the reader */
+ reader = new XMPPReader(socket.getInputStream());
+
+ } catch(IOException ioe) {
+ throw new
+ XMPPException("unable to communicate with host " + host + " on port " + port);
+ }
+
+ /* build the reader thread */
+ Thread thread = new Thread(reader);
+ thread.setDaemon(true);
+ thread.start();
+
+ synchronized(reader) {
+ /* send the initial jabber message */
+ sendConnect();
+ reader.waitCoreEvent(10000);
+ }
+ if( reader.getXMPPStreamState() != XMPPReader.XMPPStreamState.CONNECT_RECV )
+ throw new XMPPException("unable to connect to jabber server");
+
+ synchronized(reader) {
+ /* send the basic auth message */
+ sendBasicAuth();
+ reader.waitCoreEvent(10000);
+ }
+ if(!connected())
+ throw new XMPPException("Authentication failed");
+ }
+
+ /** Sends the initial jabber message */
+ private void sendConnect() {
+ reader.setXMPPStreamState(XMPPReader.XMPPStreamState.CONNECT_SENT);
+ writer.printf(JABBER_CONNECT, host);
+ }
+
+ /** Send the basic auth message */
+ private void sendBasicAuth() {
+ reader.setXMPPStreamState(XMPPReader.XMPPStreamState.AUTH_SENT);
+ writer.printf(JABBER_BASIC_AUTH, username, password, resource);
+ }
+
+
+ /**
+ * Sends an XMPPMessage.
+ * @param msg The message to send.
+ */
+ public synchronized void send(XMPPMessage msg) throws XMPPException {
+ checkConnected();
+ try {
+ String xml = msg.toXML();
+ outStream.write(xml.getBytes());
+ } catch (Exception e) {
+ throw new XMPPException(e.toString());
+ }
+ }
+
+
+ /**
+ * @throws XMPPException if we are no longer connected.
+ */
+ private void checkConnected() throws XMPPException {
+ if(!connected())
+ throw new XMPPException("Disconnected stream");
+ }
+
+
+ /**
+ * Receives messages from the network.
+ * @param timeout Maximum number of milliseconds to wait for a message to arrive.
+ * If timeout is negative, this method will wait indefinitely.
+ * 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(long timeout) throws XMPPException {
+
+ XMPPMessage msg;
+
+ if(timeout < 0) {
+
+ while(true) { /* wait indefinitely for a message to arrive */
+ reader.waitCoreEvent(timeout);
+ msg = reader.popMessageQueue();
+ if( msg != null ) return msg;
+ checkConnected();
+ }
+
+ } else {
+
+ while(timeout >= 0) { /* wait at most 'timeout' milleseconds for a message to arrive */
+ msg = reader.popMessageQueue();
+ if( msg != null ) return msg;
+ timeout -= reader.waitCoreEvent(timeout);
+ msg = reader.popMessageQueue();
+ if( msg != null ) return msg;
+ checkConnected();
+ if(timeout == 0) break;
+ }
+ }
+
+ return reader.popMessageQueue();
+ }
+
+
+ /**
+ * Disconnects from the jabber server and closes the socket
+ */
+ public void disconnect() {
+ try {
+ outStream.write(JABBER_DISCONNECT.getBytes());
+ socket.close();
+ } catch(Exception e) {}
+ }
+}
+
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.PrintStream;
+
+
+public class MathBench {
+
+ public static void main(String args[]) throws Exception {
+
+ PrintStream out = System.out;
+
+ if(args.length < 2) {
+ out.println("usage: java org.opensrf.test.MathBench <osrfConfig> <numIterations>");
+ return;
+ }
+
+ /** connect to the opensrf network */
+ Sys.bootstrapClient(args[0], "/config/opensrf");
+
+ /** how many iterations */
+ int count = Integer.parseInt(args[1]);
+
+ /** create the client session */
+ ClientSession session = new ClientSession("opensrf.math");
+
+ /** params are 1,2 */
+ List<Object> params = new ArrayList<Object>();
+ params.add(new Integer(1));
+ params.add(new Integer(2));
+
+ Request request;
+ Result result;
+ long start;
+ double total = 0;
+
+ for(int i = 0; i < count; i++) {
+
+ start = new Date().getTime();
+
+ /** create (and send) the request */
+ request = session.request("add", params);
+
+ /** wait up to 3 seconds for a response */
+ result = request.recv(3000);
+
+ /** collect the round-trip time */
+ total += new Date().getTime() - start;
+
+ if(result.getStatusCode() == Status.OK) {
+ out.print("+");
+ } else {
+ out.println("\nrequest failed");
+ out.println("status = " + result.getStatus());
+ out.println("status code = " + result.getStatusCode());
+ }
+
+ /** remove this request from the session's request set */
+ request.cleanup();
+
+ if((i+1) % 100 == 0) /** print 100 responses per line */
+ out.println(" [" + (i+1) + "]");
+ }
+
+ out.println("\nAverage request time is " + (total/count) + " ms");
+
+ /** remove this session from the global session cache */
+ session.cleanup();
+
+ /** disconnect from the opensrf network */
+ Sys.shutdown();
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.util.Cache;
+
+public class TestCache {
+ public static void main(String args[]) throws Exception {
+
+ /**
+ * args is a list of string like so: server:port server2:port server3:port ...
+ */
+
+ Cache.initCache(args);
+ Cache cache = new Cache();
+
+ cache.set("key1", "HI, MA!");
+ cache.set("key2", "HI, MA! 2");
+ cache.set("key3", "HI, MA! 3");
+
+ System.out.println("got key1 = " + (String) cache.get("key1"));
+ System.out.println("got key2 = " + (String) cache.get("key2"));
+ System.out.println("got key3 = " + (String) cache.get("key3"));
+ }
+}
+
+
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+import java.util.Map;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.PrintStream;
+
+public class TestClient {
+
+ public static void main(String args[]) throws Exception {
+
+ /** which opensrf service are we sending our request to */
+ String service;
+ /** which opensrf method we're calling */
+ String method;
+ /** method params, captures from command-line args */
+ List<Object> params;
+ /** knows how to read JSON */
+ JSONReader reader;
+ /** opensrf request */
+ Request request;
+ /** request result */
+ Result result;
+ /** start time for the request */
+ long start;
+ /** for brevity */
+ PrintStream out = System.out;
+
+ if(args.length < 3) {
+ out.println( "usage: org.opensrf.test.TestClient "+
+ "<osrfConfigFile> <service> <method> [<JSONparam1>, <JSONparam2>]");
+ return;
+ }
+
+ /** connect to the opensrf network, default config context
+ * for opensrf_core.xml is /config/opensrf */
+ Sys.bootstrapClient(args[0], "/config/opensrf");
+
+ /* grab the server, method, and any params from the command line */
+ service = args[1];
+ method = args[2];
+ params = new ArrayList<Object>();
+ for(int i = 3; i < args.length; i++)
+ params.add(new JSONReader(args[i]).read());
+
+
+ /** build the client session */
+ ClientSession session = new ClientSession(service);
+
+ /** kick off the timer */
+ start = new Date().getTime();
+
+ /** Create the request object from the session, method and params */
+ request = session.request(method, params);
+
+ while( (result = request.recv(60000)) != null ) {
+ /** loop over the results and print the JSON version of the content */
+
+ if(result.getStatusCode() != 200) {
+ /** make sure the request succeeded */
+ out.println("status = " + result.getStatus());
+ out.println("status code = " + result.getStatusCode());
+ continue;
+ }
+
+ /** JSON-ify the resulting object and print it */
+ out.println("\nresult JSON: " + new JSONWriter(result.getContent()).write());
+ }
+
+ /** How long did the request take? */
+ out.println("Request round trip took: " + (new Date().getTime() - start) + " ms.");
+
+ Sys.shutdown();
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+public class TestConfig {
+ public static void main(String args[]) throws Exception {
+ Config config = new Config("");
+ config.parse(args[0]);
+ Config.setConfig(config);
+ System.out.println(config);
+ System.out.println("");
+
+ for(int i = 1; i < args.length; i++)
+ System.out.println("Found config value: " + args[i] + ": " + Config.global().get(args[i]));
+ }
+}
--- /dev/null
+package org.opensrf.test;
+
+import org.opensrf.*;
+import org.opensrf.util.*;
+import java.util.*;
+
+public class TestJSON {
+
+ public static void main(String args[]) throws Exception {
+
+ Map<String,Object> map = new HashMap<String,Object>();
+ map.put("key1", "value1");
+ map.put("key2", "value2");
+ map.put("key3", "value3");
+ map.put("key4", "athe\u0301s");
+ map.put("key5", null);
+
+ List<Object> list = new ArrayList<Object>(16);
+ list.add(new Integer(1));
+ list.add(new Boolean(true));
+ list.add("WATER");
+ list.add(null);
+ map.put("key6", list);
+
+ System.out.println(new JSONWriter(map).write() + "\n");
+
+ String[] fields = {"isnew", "name", "shortname", "ill_address"};
+ OSRFRegistry.registerObject("aou", OSRFRegistry.WireProtocol.ARRAY, fields);
+
+ OSRFObject obj = new OSRFObject(OSRFRegistry.getRegistry("aou"));
+ obj.put("name", "athens clarke county");
+ obj.put("ill_address", new Integer(1));
+ obj.put("shortname", "ARL-ATH");
+
+ map.put("key7", obj);
+ list.add(obj);
+ System.out.println(new JSONWriter(map).write() + "\n");
+
+
+ Message m = new Message(1, Message.REQUEST);
+ Method method = new Method("opensrf.settings.host_config.get");
+ method.addParam("app07.dev.gapines.org");
+ m.setPayload(method);
+
+ 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());
+ }
+}
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.util.Logger;
+import org.opensrf.util.FileLogger;
+
+
+/** Simple test class for tesing the logging functionality */
+public class TestLog {
+ public static void main(String args[]) {
+ Logger.init(Logger.DEBUG, new FileLogger("test.log"));
+ Logger.error("Hello, world");
+ Logger.warn("Hello, world");
+ Logger.info("Hello, world");
+ Logger.debug("Hello, world");
+ }
+}
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+public class TestMultiSession {
+ public static void main(String[] args) {
+ try {
+ String config = args[0];
+
+ Sys.bootstrapClient(config, "/config/opensrf");
+ MultiSession ses = new MultiSession();
+
+ for(int i = 0; i < 40; i++) {
+ ses.request("opensrf.settings", "opensrf.system.time");
+ }
+
+ while(!ses.isComplete())
+ System.out.println("result = " + ses.recv(5000) + " and id = " + ses.lastId());
+
+ System.out.println("done");
+ Sys.shutdown();
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+
+public class TestSettings {
+ public static void main(String args[]) throws Exception {
+ Sys.bootstrapClient(args[0], "/config/opensrf");
+ SettingsClient client = SettingsClient.instance();
+ String lang = client.getString("/apps/opensrf.settings/language");
+ String impl = client.getString("/apps/opensrf.settings/implementation");
+ System.out.println("opensrf.settings language = " + lang);
+ System.out.println("opensrf.settings implementation = " + impl);
+ }
+}
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.*;
+import org.opensrf.util.*;
+import java.util.Map;
+import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.PrintStream;
+
+/**
+ * Connects to the opensrf network once per thread and runs
+ * and runs a series of request acccross all launched threads.
+ * The purpose is to verify that the java threaded client api
+ * is functioning as expected
+ */
+public class TestThread implements Runnable {
+
+ String args[];
+
+ public TestThread(String args[]) {
+ this.args = args;
+ }
+
+ public void run() {
+
+ try {
+
+ Sys.bootstrapClient(args[0], "/config/opensrf");
+ ClientSession session = new ClientSession(args[3]);
+
+ List params = new ArrayList<Object>();
+ for(int i = 5; i < args.length; i++)
+ params.add(new JSONReader(args[3]).read());
+
+ for(int i = 0; i < Integer.parseInt(args[2]); i++) {
+ System.out.println("thread " + Thread.currentThread().getId()+" sending request " + i);
+ Request request = session.request(args[4], params);
+ Result result = request.recv(3000);
+ if(result != null) {
+ System.out.println("thread " + Thread.currentThread().getId()+
+ " got result JSON: " + new JSONWriter(result.getContent()).write());
+ } else {
+ System.out.println("* thread " + Thread.currentThread().getId()+ " got NO result");
+ }
+ }
+
+ Sys.shutdown();
+ } catch(Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ public static void main(String args[]) throws Exception {
+
+ if(args.length < 5) {
+ System.out.println( "usage: org.opensrf.test.TestClient "+
+ "<osrfConfigFile> <numthreads> <numiter> <service> <method> [<JSONparam1>, <JSONparam2>]");
+ return;
+ }
+
+ int numThreads = Integer.parseInt(args[1]);
+ for(int i = 0; i < numThreads; i++)
+ new Thread(new TestThread(args)).start();
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.util.XMLFlattener;
+import java.io.FileInputStream;
+
+public class TestXMLFlattener {
+ public static void main(String args[]) throws Exception {
+ FileInputStream fis = new FileInputStream(args[0]);
+ XMLFlattener f = new XMLFlattener(fis);
+ System.out.println(f.read());
+ }
+}
--- /dev/null
+package org.opensrf.test;
+import org.opensrf.util.XMLTransformer;
+import java.io.File;
+
+public class TestXMLTransformer {
+ /**
+ * arg[0] path to an XML file
+ * arg[1] path to the XSL file to apply
+ */
+ public static void main(String[] args) {
+ try {
+ File xmlFile = new File(args[0]);
+ File xslFile = new File(args[1]);
+ XMLTransformer t = new XMLTransformer(xmlFile, xslFile);
+ System.out.println(t.apply());
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+
--- /dev/null
+package org.opensrf.test;
+
+import org.opensrf.net.xmpp.XMPPReader;
+import org.opensrf.net.xmpp.XMPPMessage;
+import org.opensrf.net.xmpp.XMPPSession;
+
+public class TestXMPP {
+
+ /**
+ * Connects to the jabber server and waits for inbound messages.
+ * If a recipient is provided, a small message is sent to the recipient.
+ */
+ public static void main(String args[]) throws Exception {
+
+ String host;
+ int port;
+ String username;
+ String password;
+ String resource;
+ String recipient;
+
+ try {
+ host = args[0];
+ port = Integer.parseInt(args[1]);
+ username = args[2];
+ password = args[3];
+ resource = args[4];
+
+ } catch(ArrayIndexOutOfBoundsException e) {
+ System.err.println("usage: org.opensrf.test.TestXMPP <host> <port> <username> <password> <resource> [<recipient>]");
+ return;
+ }
+
+ XMPPSession session = new XMPPSession(host, port);
+ session.connect(username, password, resource);
+
+ XMPPMessage msg;
+
+ if( args.length == 6 ) {
+
+ /** they specified a recipient */
+
+ recipient = args[5];
+ msg = new XMPPMessage();
+ msg.setTo(recipient);
+ msg.setThread("test-thread");
+ msg.setBody("Hello, from java-xmpp");
+ System.out.println("Sending message to " + recipient);
+ session.send(msg);
+ }
+
+ while(true) {
+ System.out.println("waiting for message...");
+ msg = session.recv(-1); /* wait forever for a message to arrive */
+ System.out.println("got message: " + msg.toXML());
+ }
+ }
+}
+
+
+
+
+
--- /dev/null
+package org.opensrf.util;
+import com.danga.MemCached.*;
+import java.util.List;
+
+/**
+ * Memcache client
+ */
+public class Cache extends MemCachedClient {
+
+ public Cache() {
+ super();
+ setCompressThreshold(4096); /* ?? */
+ }
+
+ /**
+ * Initializes the cache client
+ * @param serverList Array of server:port strings specifying the
+ * set of memcache servers this client will talk to
+ */
+ public static void initCache(String[] serverList) {
+ SockIOPool pool = SockIOPool.getInstance();
+ pool.setServers(serverList);
+ pool.initialize();
+ com.danga.MemCached.Logger logger =
+ com.danga.MemCached.Logger.getLogger(MemCachedClient.class.getName());
+ logger.setLevel(logger.LEVEL_ERROR);
+ }
+
+ /**
+ * Initializes the cache client
+ * @param serverList List of server:port strings specifying the
+ * set of memcache servers this client will talk to
+ */
+ public static void initCache(List<String> serverList) {
+ initCache(serverList.toArray(new String[]{}));
+ }
+}
+
--- /dev/null
+package org.opensrf.util;
+
+import org.json.*;
+import java.util.Map;
+import java.util.List;
+
+
+/**
+ * Config reader and accesor module. This module reads an XML config file,
+ * then loads the file into an internal config, whose values may be accessed
+ * by xpath-style lookup paths.
+ */
+public class Config {
+
+ /** The globl config instance */
+ private static Config config;
+ /** The object form of the parsed config */
+ private Map configObject;
+ /**
+ * The log parsing context. This is used as a prefix to the
+ * config item search path. This allows config XML chunks to
+ * be inserted into arbitrary XML files.
+ */
+ private String context;
+
+ public static Config global() {
+ return config;
+ }
+
+
+ /**
+ * @param context The config context
+ */
+ public Config(String context) {
+ this.context = context;
+ }
+
+ /**
+ * Sets the global config object.
+ * @param c The config object to use.
+ */
+ public static void setGlobalConfig(Config c) {
+ config = c;
+ }
+
+ /**
+ * Parses an XML config file.
+ * @param filename The path to the file to parse.
+ */
+ public void parse(String filename) throws ConfigException {
+ try {
+ String xml = Utils.fileToString(filename);
+ JSONObject jobj = XML.toJSONObject(xml);
+ configObject = (Map) new JSONReader(jobj.toString()).readObject();
+ } catch(Exception e) {
+ throw new ConfigException("Error parsing config", e);
+ }
+ }
+
+ public static void setConfig(Config conf) {
+ config = conf;
+ }
+
+ public void setConfigObject(Map config) {
+ this.configObject = config;
+ }
+
+ protected Map getConfigObject() {
+ return this.configObject;
+ }
+
+
+ /**
+ * Returns the configuration value found at the requested path.
+ * @param path The search path
+ * @return The config value, or null if no value exists at the given path.
+ * @throws ConfigException thrown if nothing is found at the path
+ */
+ public String getString(String path) throws ConfigException {
+ try {
+ return (String) get(path);
+ } catch(Exception e) {
+ throw new
+ ConfigException("No config string found at " + path);
+ }
+ }
+
+ /**
+ * Gets the int value at the given path
+ * @param path The search path
+ */
+ public int getInt(String path) throws ConfigException {
+ try {
+ return Integer.parseInt(getString(path));
+ } catch(Exception e) {
+ throw new
+ ConfigException("No config int found at " + path);
+ }
+ }
+
+ /**
+ * Returns the configuration object found at the requested path.
+ * @param path The search path
+ * @return The config value
+ * @throws ConfigException thrown if nothing is found at the path
+ */
+ public Object get(String path) throws ConfigException {
+ try {
+ Object obj = Utils.findPath(configObject, context + path);
+ if(obj == null)
+ throw new ConfigException("");
+ return obj;
+ } catch(Exception e) {
+ e.printStackTrace();
+ throw new ConfigException("No config object found at " + path);
+ }
+ }
+
+ /**
+ * Returns the first item in the list found at the given path. If
+ * no list is found, ConfigException is thrown.
+ * @param path The search path
+ */
+ public Object getFirst(String path) throws ConfigException {
+ Object obj = get(path);
+ if(obj instanceof List)
+ return ((List) obj).get(0);
+ return obj;
+ }
+
+
+ /**
+ * Returns the config as a JSON string
+ */
+ public String toString() {
+ return new JSONWriter(configObject).write();
+ }
+}
+
--- /dev/null
+package org.opensrf.util;
+
+/**
+ * Thrown by the Config module when a user requests a configuration
+ * item that does not exist
+ */
+public class ConfigException extends Exception {
+ public ConfigException(String info) {
+ super(info);
+ }
+ public ConfigException(String info, Throwable t) {
+ super(info, t);
+ }
+}
--- /dev/null
+package org.opensrf.util;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+
+
+public class FileLogger extends Logger {
+
+ /** File to log to */
+ private String filename;
+
+ /**
+ * FileLogger constructor
+ * @param filename The path to the log file
+ */
+ public FileLogger(String filename) {
+ this.filename = filename;
+ }
+
+ /**
+ * Logs the mesage to a file.
+ * @param level The log level
+ * @param msg The mesage to log
+ */
+ protected synchronized void log(short level, String msg) {
+ if(level > logLevel) return;
+
+ BufferedWriter out = null;
+ try {
+ out = new BufferedWriter(new FileWriter(this.filename, true));
+ out.write(formatMessage(level, msg) + "\n");
+
+ } catch(Exception e) {
+ /** If we are unable to write our log message, go ahead and
+ * fall back to the default (stdout) logger */
+ Logger.init(logLevel, new Logger());
+ Logger.logByLevel(ERROR, "Unable to write to log file " + this.filename);
+ Logger.logByLevel(level, msg);
+ }
+
+ try {
+ out.close();
+ } catch(Exception e) {}
+ }
+}
--- /dev/null
+package org.opensrf.util;
+/**
+ * Used to indicate JSON parsing errors
+ */
+public class JSONException extends Exception {
+ public JSONException(String s) {
+ super(s);
+ }
+}
--- /dev/null
+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";
+
+ /** The JSON string to parser */
+ private String json;
+
+ /**
+ * @param json The JSON to parse
+ */
+ public JSONReader(String json) {
+ this.json = json;
+ }
+
+ /**
+ * Parses JSON and creates an object.
+ * @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());
+ }
+ }
+
+ /**
+ * Assumes that a JSON array will be read. Returns
+ * the resulting array as a list.
+ */
+ public List<?> readArray() throws JSONException {
+ Object o = read();
+ try {
+ return (List<?>) o;
+ } catch(Exception e) {
+ throw new JSONException("readArray(): JSON cast exception");
+ }
+ }
+
+ /**
+ * Assumes that a JSON object will be read. Returns
+ * the resulting object as a map.
+ */
+ public Map<?,?> readObject() throws JSONException {
+ Object o = read();
+ try {
+ return (Map<?,?>) o;
+ } catch(Exception e) {
+ throw new JSONException("readObject(): JSON cast exception");
+ }
+ }
+
+
+ /**
+ * Recurse through the object and turn items into maps, lists, etc.
+ */
+ 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;
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.util;
+
+import java.io.*;
+import java.util.*;
+
+
+/**
+ * JSONWriter
+ */
+public class JSONWriter {
+
+ /** The object to serialize to JSON */
+ private Object obj;
+
+ /**
+ * @param obj The object to encode
+ */
+ public JSONWriter(Object obj) {
+ this.obj = obj;
+ }
+
+
+ /**
+ * Encodes a java object to JSON.
+ */
+ 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);
+ }
+
+ /**
+ * Encodes the object as JSON into the provided buffer
+ */
+ 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 ) {
+
+ /** encode arrays as lists */
+ 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 {
+
+ /** encode hashes as maps */
+ 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);
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.util;
+import java.text.SimpleDateFormat;
+import java.text.FieldPosition;
+import java.util.Date;
+
+/**
+ * Basic OpenSRF logging API. This default implementation
+ * logs to stderr.
+ */
+public class Logger {
+
+ /** Log levels */
+ public static final short ERROR = 1;
+ public static final short WARN = 2;
+ public static final short INFO = 3;
+ public static final short DEBUG = 4;
+ public static final short INTERNAL = 5;
+
+ /** The global log instance */
+ private static Logger instance;
+ /** The global log level */
+ protected static short logLevel;
+
+ public Logger() {}
+
+ /** Sets the global Logger instance
+ * @param level The global log level.
+ * @param l The Logger instance to use
+ */
+ public static void init(short level, Logger l) {
+ instance = l;
+ logLevel = level;
+ }
+
+ /**
+ * @return The global Logger instance
+ */
+ public static Logger instance() {
+ return instance;
+ }
+
+ /**
+ * Logs an error message
+ * @param msg The message to log
+ */
+ public static void error(String msg) {
+ instance.log(ERROR, msg);
+ }
+
+ /**
+ * Logs an warning message
+ * @param msg The message to log
+ */
+ public static void warn(String msg) {
+ instance.log(WARN, msg);
+ }
+
+ /**
+ * Logs an info message
+ * @param msg The message to log
+ */
+ public static void info(String msg) {
+ instance.log(INFO, msg);
+ }
+
+ /**
+ * Logs an debug message
+ * @param msg The message to log
+ */
+ public static void debug(String msg) {
+ instance.log(DEBUG, msg);
+ }
+
+ /**
+ * Logs an internal message
+ * @param msg The message to log
+ */
+ public static void internal(String msg) {
+ instance.log(INTERNAL, msg);
+ }
+
+
+ /**
+ * Appends the text representation of the log level
+ * @param sb The stringbuffer to append to
+ * @param level The log level
+ */
+ protected static void appendLevelString(StringBuffer sb, short level) {
+ switch(level) {
+ case DEBUG:
+ sb.append("DEBG"); break;
+ case INFO:
+ sb.append("INFO"); break;
+ case INTERNAL:
+ sb.append("INT "); break;
+ case WARN:
+ sb.append("WARN"); break;
+ case ERROR:
+ sb.append("ERR "); break;
+ }
+ }
+
+ /**
+ * Formats a message for logging. Appends the current date+time
+ * and the log level string.
+ * @param level The log level
+ * @param msg The message to log
+ */
+ protected static String formatMessage(short level, String msg) {
+
+ StringBuffer sb = new StringBuffer();
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(
+ new Date(), sb, new FieldPosition(0));
+
+ sb.append(" [");
+ appendLevelString(sb, level);
+ sb.append(":");
+ sb.append(Thread.currentThread().getId());
+ sb.append("] ");
+ sb.append(msg);
+ return sb.toString();
+ }
+
+ /**
+ * Logs a message by passing the log level explicitly
+ * @param level The log level
+ * @param msg The message to log
+ */
+ public static void logByLevel(short level, String msg) {
+ instance.log(level, msg);
+ }
+
+ /**
+ * Performs the actual logging. Subclasses should override
+ * this method.
+ * @param level The log level
+ * @param msg The message to log
+ */
+ protected synchronized void log(short level, String msg) {
+ if(level > logLevel) return;
+ System.err.println(formatMessage(level, msg));
+ }
+}
+
--- /dev/null
+package org.opensrf.util;
+
+import java.util.Map;
+import java.util.HashMap;
+
+
+/**
+ * Generic OpenSRF network-serializable object. This allows
+ * access to object fields.
+ */
+public class OSRFObject extends HashMap<String, Object> implements OSRFSerializable {
+
+ /** This objects registry */
+ private OSRFRegistry registry;
+
+ public OSRFObject() {
+ }
+
+
+ /**
+ * Creates a new object with the provided registry
+ */
+ public OSRFObject(OSRFRegistry reg) {
+ this();
+ registry = reg;
+ }
+
+
+ /**
+ * Creates a new OpenSRF object based on the net class string
+ * */
+ public OSRFObject(String netClass) {
+ this(OSRFRegistry.getRegistry(netClass));
+ }
+
+
+ /**
+ * @return This object's registry
+ */
+ public OSRFRegistry getRegistry() {
+ return registry;
+ }
+
+ /**
+ * Implement get() to fulfill our contract with OSRFSerializable
+ */
+ public Object get(String field) {
+ return super.get(field);
+ }
+
+ /** Returns the string value found at the given field */
+ public String getString(String field) {
+ return (String) get(field);
+ }
+
+ /** Returns the int value found at the given field */
+ public int getInt(String field) {
+ Object o = get(field);
+ if(o instanceof String)
+ return Integer.parseInt((String) o);
+ return ((Integer) get(field)).intValue();
+ }
+}
--- /dev/null
+package org.opensrf.util;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+
+/**
+ * Manages the registration of OpenSRF network-serializable objects.
+ * A serializable object has a class "hint" (called netClass within) which
+ * describes the type of object. Each object also has a set of field names
+ * for accessing/mutating object properties. Finally, objects have a
+ * serialization wire protocol. Currently supported protocols are HASH
+ * and ARRAY.
+ */
+public class OSRFRegistry implements Serializable{
+
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ /**
+ * Global collection of registered net objects.
+ * Maps netClass names to registries.
+ */
+ private static HashMap<String, OSRFRegistry>
+ registry = new HashMap<String, OSRFRegistry>();
+
+
+ /** Serialization types for registered objects */
+ public enum WireProtocol {
+ ARRAY, HASH
+ };
+
+
+ /** Array of field names for this registered object */
+ String fields[];
+ /** The wire protocol for this object */
+ WireProtocol wireProtocol;
+ /** The network class for this object */
+ String netClass;
+
+ /**
+ * Returns the array of field names
+ */
+ public String[] getFields() {
+ return this.fields;
+ }
+
+
+ /**
+ * Registers a new object.
+ * @param netClass The net class for this object
+ * @param wireProtocol The object's wire protocol
+ * @param fields An array of field names. For objects whose
+ * wire protocol is ARRAY, the positions of the field names
+ * will be used as the array indices for the fields at serialization time
+ */
+ public static OSRFRegistry registerObject(String netClass, WireProtocol wireProtocol, String fields[]) {
+ OSRFRegistry r = new OSRFRegistry(netClass, wireProtocol, fields);
+ registry.put(netClass, r);
+ return r;
+ }
+
+ /**
+ * Returns the registry for the given netclass
+ * @param netClass The network class to lookup
+ */
+ public static OSRFRegistry getRegistry(String netClass) {
+ if( netClass == null ) return null;
+ return (OSRFRegistry) registry.get(netClass);
+ }
+
+
+ /**
+ * @param field The name of the field to lookup
+ * @return the index into the fields array of the given field name.
+ */
+ public int getFieldIndex(String field) {
+ for( int i = 0; i < fields.length; i++ )
+ if( fields[i].equals(field) )
+ return i;
+ return -1;
+ }
+
+ /** Returns the wire protocol of this object */
+ public WireProtocol getWireProtocol() {
+ return this.wireProtocol;
+ }
+
+ /** Returns the netClass ("hint") of this object */
+ public String getNetClass() {
+ return this.netClass;
+ }
+
+ /**
+ * Creates a new registry object.
+ * @param netClass The network class/hint
+ * @param wireProtocol The wire protocol
+ * @param fields The array of field names. For array-based objects,
+ * the fields array must be sorted in accordance with the sorting
+ * of the objects in the array.
+ */
+ public OSRFRegistry(String netClass, WireProtocol wireProtocol, String fields[]) {
+ this.netClass = netClass;
+ this.wireProtocol = wireProtocol;
+ this.fields = fields;
+ }
+}
+
+
--- /dev/null
+package org.opensrf.util;
+
+/**
+ * All network-serializable OpenSRF object must implement this interface.
+ */
+public interface OSRFSerializable {
+
+ /**
+ * Returns the object registry object for the implementing class.
+ */
+ public abstract OSRFRegistry getRegistry();
+
+ /**
+ * Returns the object found at the given field
+ */
+ public abstract Object get(String field);
+}
+
+
--- /dev/null
+package org.opensrf.util;
+import org.opensrf.*;
+import java.util.Map;
+
+/**
+ * Connects to the OpenSRF Settings server to fetch the settings config.
+ * Provides a Config interface for fetching settings via path
+ */
+public class SettingsClient extends Config {
+
+ /** Singleton SettingsClient instance */
+ private static SettingsClient client = new SettingsClient();
+
+ public SettingsClient() {
+ super("");
+ }
+
+ /**
+ * @return The global settings client instance
+ */
+ public static SettingsClient instance() throws ConfigException {
+ if(client.getConfigObject() == null)
+ client.fetchConfig();
+ return client;
+ }
+
+ /**
+ * Fetches the settings object from the settings server
+ */
+ private void fetchConfig() throws ConfigException {
+
+ ClientSession ses = new ClientSession("opensrf.settings");
+ try {
+
+ Request req = ses.request(
+ "opensrf.settings.host_config.get",
+ new String[]{(String)Config.global().getFirst("/domain")});
+
+ Result res = req.recv(12000);
+ if(res == null) {
+ /** throw exception */
+ }
+ setConfigObject((Map) res.getContent());
+
+ } catch(Exception e) {
+ throw new ConfigException("Error fetching settings config", e);
+
+ } finally {
+ ses.cleanup();
+ }
+ }
+}
+
--- /dev/null
+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();
+ }
+
+
+ /**
+ * Escapes a string.
+ */
+ 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);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Descends into the map along the given XPATH-style path
+ * and returns the object found there.
+ * @param path The XPATH-style path to search. Path
+ * components are separated by '/' characters.
+ * Example: /opensrf/loglevel
+ * @return The found object.
+ */
+
+ public static Object findPath(Map map, String path) {
+ String keys[] = path.split("/", -1);
+ int i = 0;
+ if(path.charAt(0) == '/') i++;
+ for(; i < keys.length - 1; i++ )
+ map = (Map) map.get(keys[i]);
+
+ return map.get(keys[i]);
+ }
+}
+
+
+
--- /dev/null
+package org.opensrf.util;
+
+import javax.xml.stream.*;
+import javax.xml.stream.events.* ;
+import javax.xml.namespace.QName;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.io.InputStream;
+import org.opensrf.util.JSONWriter;
+import org.opensrf.util.JSONReader;
+
+/**
+ * Flattens an XML file into a properties map. Values are stored as JSON strings or arrays.
+ * An array is created if more than one value resides at the same key.
+ * e.g. html.head.script = "alert('hello');"
+ */
+public class XMLFlattener {
+
+ /** Flattened properties map */
+ private Map<String, String> props;
+ /** Incoming XML stream */
+ private InputStream inStream;
+ /** Runtime list of encountered elements */
+ private List<String> elementList;
+
+ /**
+ * Creates a new reader. Initializes the message queue.
+ * Sets the stream state to disconnected, and the xml
+ * state to in_nothing.
+ * @param inStream the inbound XML stream
+ */
+ public XMLFlattener(InputStream inStream) {
+ props = new HashMap<String, String>();
+ this.inStream = inStream;
+ elementList = new ArrayList<String>();
+ }
+
+ /** Turns an array of strings into a dot-separated key string */
+ private String listToString() {
+ ListIterator itr = elementList.listIterator();
+ StringBuffer sb = new StringBuffer();
+ while(itr.hasNext()) {
+ sb.append(itr.next());
+ if(itr.hasNext())
+ sb.append(".");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parses XML data from the provided stream.
+ */
+ public Map read() throws javax.xml.stream.XMLStreamException {
+
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+
+ /** disable as many unused features as possible to speed up the parsing */
+ factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
+ factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
+ factory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+
+ /** create the stream reader */
+ XMLStreamReader reader = factory.createXMLStreamReader(inStream);
+ int eventType;
+
+ while(reader.hasNext()) {
+ /** cycle through the XML events */
+
+ eventType = reader.next();
+ if(reader.isWhiteSpace()) continue;
+
+ switch(eventType) {
+
+ case XMLEvent.START_ELEMENT:
+ elementList.add(reader.getName().toString());
+ break;
+
+ case XMLEvent.CHARACTERS:
+ String text = reader.getText();
+ String key = listToString();
+
+ if(props.containsKey(key)) {
+
+ /* something in the map already has this key */
+
+ Object o = null;
+ try {
+ o = new JSONReader(props.get(key)).read();
+ } catch(org.opensrf.util.JSONException e){}
+
+ if(o instanceof List) {
+ /* if the map contains a list, append to the list and re-encode */
+ ((List) o).add(text);
+
+ } else {
+ /* if the map just contains a string, start building a new list
+ * with the old string and append the new string */
+ List<String> arr = new ArrayList<String>();
+ arr.add((String) o);
+ arr.add(text);
+ o = arr;
+ }
+
+ props.put(key, new JSONWriter(o).write());
+
+ } else {
+ props.put(key, new JSONWriter(text).write());
+ }
+ break;
+
+ case XMLEvent.END_ELEMENT:
+ elementList.remove(elementList.size()-1);
+ break;
+ }
+ }
+
+ return props;
+ }
+}
+
+
+
+
--- /dev/null
+package org.opensrf.util;
+import javax.xml.transform.*;
+import javax.xml.transform.stream.*;
+import javax.xml.parsers.*;
+import java.io.File;
+import java.io.ByteArrayInputStream;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+
+/**
+ * Performs XSL transformations.
+ * TODO: Add ability to pass in XSL variables
+ */
+public class XMLTransformer {
+
+ /** The XML to transform */
+ private Source xmlSource;
+ /** The stylesheet to apply */
+ private Source xslSource;
+
+ public XMLTransformer(Source xmlSource, Source xslSource) {
+ this.xmlSource = xmlSource;
+ this.xslSource = xslSource;
+ }
+
+ public XMLTransformer(String xmlString, File xslFile) {
+ this(
+ new StreamSource(new ByteArrayInputStream(xmlString.getBytes())),
+ new StreamSource(xslFile));
+ }
+
+ public XMLTransformer(File xmlFile, File xslFile) {
+ this(
+ new StreamSource(xmlFile),
+ new StreamSource(xslFile));
+ }
+
+ /**
+ * Applies the transformation and puts the result into the provided output stream
+ */
+ public void apply(OutputStream outStream) throws TransformerException, TransformerConfigurationException {
+ Result result = new StreamResult(outStream);
+ Transformer trans = TransformerFactory.newInstance().newTransformer(xslSource);
+ trans.transform(xmlSource, result);
+ }
+
+ /**
+ * Applies the transformation and return the resulting string
+ * @return The String created by the XSL transformation
+ */
+ public String apply() throws TransformerException, TransformerConfigurationException {
+ OutputStream outStream = new ByteArrayOutputStream();
+ this.apply(outStream);
+ return outStream.toString();
+ }
+}
+
+