From 92e1b6189d4e4b6989b11e981b1cf93d4678ae17 Mon Sep 17 00:00:00 2001 From: miker Date: Wed, 9 Apr 2008 02:25:05 +0000 Subject: [PATCH] adding example dojo-izing class for opensrf. this should go into OpenSRF 1.0, and the js libs should be made to live under /js/ git-svn-id: svn://svn.open-ils.org/ILS/branches/dojo-admin@9277 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/web/conify/js/OpenSRF.js | 14 + Open-ILS/web/conify/js/opensrf/opensrf.js | 395 ++++++++++++++++++++++++++ Open-ILS/web/conify/js/opensrf/opensrf_xhr.js | 123 ++++++++ 3 files changed, 532 insertions(+) create mode 100644 Open-ILS/web/conify/js/OpenSRF.js create mode 100644 Open-ILS/web/conify/js/opensrf/opensrf.js create mode 100644 Open-ILS/web/conify/js/opensrf/opensrf_xhr.js diff --git a/Open-ILS/web/conify/js/OpenSRF.js b/Open-ILS/web/conify/js/OpenSRF.js new file mode 100644 index 0000000000..38d69f1f56 --- /dev/null +++ b/Open-ILS/web/conify/js/OpenSRF.js @@ -0,0 +1,14 @@ +if(!dojo._hasResource['OpenSRF']){ + + dojo._hasResource['OpenSRF'] = true; + dojo.provide('OpenSRF'); + dojo.require('opensrf.opensrf', true); + dojo.require('opensrf.opensrf_xhr', true); + + OpenSRF.session_cache = {}; + OpenSRF.CachedClientSession = function ( app ) { + if (this.session_cache[app]) return this.session_cache[app]; + this.session_cache[app] = new OpenSRF.ClientSession ( app ); + return this.session_cache[app]; + } +} diff --git a/Open-ILS/web/conify/js/opensrf/opensrf.js b/Open-ILS/web/conify/js/opensrf/opensrf.js new file mode 100644 index 0000000000..ca86ba5464 --- /dev/null +++ b/Open-ILS/web/conify/js/opensrf/opensrf.js @@ -0,0 +1,395 @@ +/* ----------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ----------------------------------------------------------------------- */ + +/* session states */ +var OSRF_APP_SESSION_CONNECTED = 0; +var OSRF_APP_SESSION_CONNECTING = 1; +var OSRF_APP_SESSION_DISCONNECTED = 2; + +/* types of transport layers */ +var OSRF_TRANSPORT_TYPE_XHR = 1; +var OSRF_TRANSPORT_TYPE_XMPP = 2; + +/* message types */ +var OSRF_MESSAGE_TYPE_REQUEST = 'REQUEST'; +var OSRF_MESSAGE_TYPE_STATUS = 'STATUS'; +var OSRF_MESSAGE_TYPE_RESULT = 'RESULT'; +var OSRF_MESSAGE_TYPE_CONNECT = 'CONNECT'; +var OSRF_MESSAGE_TYPE_DISCONNECT = 'DISCONNECT'; + +/* message statuses */ +var OSRF_STATUS_CONTINUE = 100; +var OSRF_STATUS_OK = 200; +var OSRF_STATUS_ACCEPTED = 202; +var OSRF_STATUS_COMPLETE = 205; +var OSRF_STATUS_REDIRECTED = 307; +var OSRF_STATUS_BADREQUEST = 400; +var OSRF_STATUS_UNAUTHORIZED = 401; +var OSRF_STATUS_FORBIDDEN = 403; +var OSRF_STATUS_NOTFOUND = 404; +var OSRF_STATUS_NOTALLOWED = 405; +var OSRF_STATUS_TIMEOUT = 408; +var OSRF_STATUS_EXPFAILED = 417; +var OSRF_STATUS_INTERNALSERVERERROR = 500; +var OSRF_STATUS_NOTIMPLEMENTED = 501; +var OSRF_STATUS_VERSIONNOTSUPPORTED = 505; + +var OpenSRF = {}; + +/* makes cls a subclass of pcls */ +OpenSRF.set_subclass = function(cls, pcls) { + var str = cls+'.prototype = new '+pcls+'();'; + str += cls+'.prototype.constructor = '+cls+';'; + str += cls+'.baseClass = '+pcls+'.prototype.constructor;'; + str += cls+'.prototype.super = '+pcls+'.prototype;'; + eval(str); +} + + +/* general session superclass */ +OpenSRF.Session = function() { + this.remote_id = null; + this.state = OSRF_APP_SESSION_DISCONNECTED; +} + +OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR; /* default to XHR */ +OpenSRF.Session.cache = {}; +OpenSRF.Session.find_session = function(thread_trace) { + return OpenSRF.Session.cache[thread_trace]; +} +OpenSRF.Session.prototype.cleanup = function() { + delete OpenSRF.Session.cache[this.thread]; +} + +OpenSRF.Session.prototype.send = function(osrf_msg, args) { + args = (args) ? args : {}; + switch(OpenSRF.Session.transport) { + case OSRF_TRANSPORT_TYPE_XHR: + return this.send_xhr(osrf_msg, args); + case OSRF_TRANSPORT_TYPE_XMPP: + return this.send_xmpp(osrf_msg, args); + } +} + +OpenSRF.Session.prototype.send_xhr = function(osrf_msg, args) { + args.thread = this.thread; + args.rcpt = this.remote_id; + args.rcpt_service = this.service; + new OpenSRF.XHRequest(osrf_msg, args).send(); +} + +OpenSRF.Session.prototype.send_xmpp = function(osrf_msg, args) { + alert('xmpp transport not yet implemented'); +} + + +/* client sessions make requests */ +OpenSRF.ClientSession = function(service) { + this.service = service + this.remote_id = null; + this.locale = 'en-US'; + this.last_id = 0; + this.thread = Math.random() + '' + new Date().getTime(); + this.requests = []; + this.onconnect = null; + OpenSRF.Session.cache[this.thread] = this; +} +OpenSRF.set_subclass('OpenSRF.ClientSession', 'OpenSRF.Session'); + + +OpenSRF.ClientSession.prototype.connect = function(args) { + args = (args) ? args : {}; + + if(args.onconnect) + this.onconnect = args.onconnect; + + /* if no handler is provided, make this a synchronous call */ + if(!this.onconnect) + this.timeout = (args.timeout) ? args.timeout : 5; + + message = new osrfMessage({ + 'threadTrace' : this.reqid, + 'type' : OSRF_MESSAGE_TYPE_CONNECT, + }); + + this.send(message, {'timeout' : this.timeout}); + + if(this.onconnect || this.state == OSRF_APP_SESSION_CONNECTED) + return true; + return false; +} + +OpenSRF.ClientSession.prototype.disconnect = function(args) { + this.send( + new osrfMessage({ + 'threadTrace' : this.reqid, + 'type' : OSRF_MESSAGE_TYPE_DISCONNECT, + }) + ); +} + + +OpenSRF.ClientSession.prototype.request = function(args) { + + if(typeof args == 'string') { + params = []; + for(var i = 1; i < arguments.length; i++) + params.push(arguments[i]); + + args = { + method : args, + params : params + }; + } else { + if(typeof args == 'undefined') + args = {}; + } + + var req = new OpenSRF.Request(this, this.last_id++, args); + this.requests.push(req); + return req; +} + +OpenSRF.ClientSession.prototype.find_request = function(reqid) { + for(var i = 0; i < this.requests.length; i++) { + var req = this.requests[i]; + if(req.reqid == reqid) + return req; + } + return null; +} + +OpenSRF.Request = function(session, reqid, args) { + this.session = session; + this.reqid = reqid; + + /* callbacks */ + this.onresponse = args.onresponse; + this.oncomplete = args.oncomplete; + this.onerror = args.onerror; + this.onmethoderror = args.onmethoderror; + this.ontransporterror = args.ontransporterror; + + this.method = args.method; + this.params = args.params; + this.timeout = args.timeout; + this.response_queue = []; + this.complete = false; +} + +OpenSRF.Request.prototype.recv = function(timeout) { + if(this.response_queue.length > 0) + return this.response_queue.shift(); + return null; +} + +OpenSRF.Request.prototype.send = function() { + method = new osrfMethod({'method':this.method, 'params':this.params}); + message = new osrfMessage({ + 'threadTrace' : this.reqid, + 'type' : OSRF_MESSAGE_TYPE_REQUEST, + 'payload' : method, + 'locale' : this.session.locale + }); + + this.session.send(message, { + 'timeout' : this.timeout, + 'onresponse' : this.onresponse, + 'oncomplete' : this.oncomplete, + 'onerror' : this.onerror, + 'onmethoderror' : this.onmethoderror, + 'ontransporterror' : this.ontransporterror + }); +} + +OpenSRF.NetMessage = function(to, from, thread, body) { + this.to = to; + this.from = from; + this.thread = thread; + this.body = body; +} + +OpenSRF.Stack = function() { +} + +OpenSRF.Stack.push = function(net_msg, callbacks) { + var ses = OpenSRF.Session.find_session(net_msg.thread); + if(!ses) return; + ses.remote_id = net_msg.sender; + osrf_msgs = JSON2js(net_msg.body); + for(var i = 0; i < osrf_msgs.length; i++) + OpenSRF.Stack.handle_message(ses, osrf_msgs[i], callbacks); +} + +OpenSRF.Stack.handle_message = function(ses, osrf_msg, callbacks) { + + var req = null; + + if(osrf_msg.type() == OSRF_MESSAGE_TYPE_STATUS) { + + var payload = osrf_msg.payload(); + var status = payload.statusCode(); + var status_text = payload.status(); + + if(status == OSRF_STATUS_COMPLETE) { + req = ses.find_request(osrf_msg.threadTrace()); + if(req) { + req.complete = true; + if(callbacks.oncomplete && !req.oncomplete_called) { + req.oncomplete_called = true; + return callbacks.oncomplete(req); + } + } + } + + if(status == OSRF_STATUS_OK) { + ses.state = OSRF_APP_SESSION_CONNECTED; + + /* call the connect callback */ + if(ses.onconnect && !ses.onconnect_called) { + ses.onconnect_called = true; + return ses.onconnect(); + } + } + + if(status == OSRF_STATUS_NOTFOUND) { + req = ses.find_request(osrf_msg.threadTrace()); + if(callbacks.onmethoderror) + return callbacks.onmethoderror(req, status, status_text); + } + } + + if(osrf_msg.type() == OSRF_MESSAGE_TYPE_RESULT) { + req = ses.find_request(osrf_msg.threadTrace()); + if(req) { + req.response_queue.push(osrf_msg.payload()); + if(callbacks.onresponse) + return callbacks.onresponse(req); + } + } +} + +/* The following classes map directly to network-serializable opensrf objects */ + +function osrfMessage(hash) { + this.hash = hash; + this._encodehash = true; +} +osrfMessage.prototype.threadTrace = function(d) { + if(arguments.length == 1) + this.hash.threadTrace = d; + return this.hash.threadTrace; +} +osrfMessage.prototype.type = function(d) { + if(arguments.length == 1) + this.hash.type = d; + return this.hash.type; +} +osrfMessage.prototype.payload = function(d) { + if(arguments.length == 1) + this.hash.payload = d; + return this.hash.payload; +} +osrfMessage.prototype.locale = function(d) { + if(arguments.length == 1) + this.hash.locale = d; + return this.hash.locale; +} +osrfMessage.prototype.serialize = function() { + return { + "__c":"osrfMessage", + "__p": { + 'threadTrace' : this.hash.threadTrace, + 'type' : this.hash.type, + 'payload' : (this.hash.payload) ? this.hash.payload.serialize() : 'null', + 'locale' : this.hash.locale + } + }; +} + +function osrfMethod(hash) { + this.hash = hash; + this._encodehash = true; +} +osrfMethod.prototype.method = function() { + if(arguments.length == 1) + this.hash.method = d; + return this.hash.method; +} +osrfMethod.prototype.params = function() { + if(arguments.length == 1) + this.hash.params = d; + return this.hash.params; +} +osrfMethod.prototype.serialize = function() { + return { + "__c":"osrfMethod", + "__p": { + 'method' : this.hash.method, + 'params' : this.hash.params + } + }; +} + +function osrfMethodException(hash) { + this.hash = hash; + this._encodehash = true; +} +osrfMethodException.prototype.status = function() { + if(arguments.length == 1) + this.hash.status = d; + return this.hash.status; +} +osrfMethodException.prototype.statusCode = function() { + if(arguments.length == 1) + this.hash.statusCode = d; + return this.hash.statusCode; +} +function osrfConnectStatus(hash) { + this.hash = hash; + this._encodehash = true; +} +osrfConnectStatus.prototype.status = function() { + if(arguments.length == 1) + this.hash.status = d; + return this.hash.status; +} +osrfConnectStatus.prototype.statusCode = function() { + if(arguments.length == 1) + this.hash.statusCode = d; + return this.hash.statusCode; +} +function osrfResult(hash) { + this.hash = hash; + this._encodehash = true; +} +osrfResult.prototype.status = function() { + if(arguments.length == 1) + this.hash.status = d; + return this.hash.status; +} +osrfResult.prototype.statusCode = function() { + if(arguments.length == 1) + this.hash.statusCode = d; + return this.hash.statusCode; +} +osrfResult.prototype.content = function() { + if(arguments.length == 1) + this.hash.content = d; + return this.hash.content; +} + + + diff --git a/Open-ILS/web/conify/js/opensrf/opensrf_xhr.js b/Open-ILS/web/conify/js/opensrf/opensrf_xhr.js new file mode 100644 index 0000000000..8c7944192f --- /dev/null +++ b/Open-ILS/web/conify/js/opensrf/opensrf_xhr.js @@ -0,0 +1,123 @@ +/* ----------------------------------------------------------------------- + * Copyright (C) 2008 Georgia Public Library Service + * Bill Erickson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * ----------------------------------------------------------------------- */ + +var OSRF_HTTP_HEADER_TO = 'X-OpenSRF-to'; +var OSRF_HTTP_HEADER_XID = 'X-OpenSRF-thread'; +var OSRF_HTTP_HEADER_FROM = 'X-OpenSRF-from'; +var OSRF_HTTP_HEADER_THREAD = 'X-OpenSRF-thread'; +var OSRF_HTTP_HEADER_TIMEOUT = 'X-OpenSRF-timeout'; +var OSRF_HTTP_HEADER_SERVICE = 'X-OpenSRF-service'; +var OSRF_HTTP_HEADER_MULTIPART = 'X-OpenSRF-multipart'; +var OSRF_HTTP_TRANSLATOR = '/osrf-http-translator'; /* XXX config */ +var OSRF_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded'; + + +OpenSRF.XHRequest = function(osrf_msg, args) { + this.message = osrf_msg; + this.args = args; + this.xreq = new XMLHttpRequest(); /* XXX browser check */ +} + +OpenSRF.XHRequest.prototype.send = function() { + var xhr_req = this; + var xreq = this.xreq + + if(this.args.timeout) { + /* this is a standard blocking (non-multipart) call */ + xreq.open('POST', OSRF_HTTP_TRANSLATOR, false); + + } else { + + if( /* XXX browser != mozilla */ false ) { + + /* standard asynchronous call */ + xreq.onreadystatechange = function() { + if(xreq.readyState == 4) + xhr_req.core_handler(); + } + xreq.open('POST', OSRF_HTTP_TRANSLATOR, true); + + } else { + + /* asynchronous multipart call */ + xreq.multipart = true; + xreq.onload = function(evt) {xhr_req.core_handler();} + xreq.open('POST', OSRF_HTTP_TRANSLATOR, true); + xreq.setRequestHeader(OSRF_HTTP_HEADER_MULTIPART, 'true'); + + /* multipart requests do not pass the status info to the onload if there + is no new data to load. Capture the status on the readystate handler */ + xreq.onreadystatechange = function() { + if(xreq.readyState == 4 && xreq.status >= 400) + xhr_req.transport_error_handler(); + } + } + } + + xreq.setRequestHeader('Content-Type', OSRF_POST_CONTENT_TYPE); + xreq.setRequestHeader(OSRF_HTTP_HEADER_THREAD, this.args.thread); + if(this.args.rcpt) + xreq.setRequestHeader(OSRF_HTTP_HEADER_TO, this.args.rcpt); + else + xreq.setRequestHeader(OSRF_HTTP_HEADER_SERVICE, this.args.rcpt_service); + + var post = 'osrf-msg=' + encodeURIComponent(js2JSON([this.message.serialize()])); + xreq.send(post); + + if(this.args.timeout) /* this was a blocking call, manually run the handler */ + this.core_handler() + + return this; +} + +OpenSRF.XHRequest.prototype.core_handler = function() { + sender = this.xreq.getResponseHeader(OSRF_HTTP_HEADER_FROM); + thread = this.xreq.getResponseHeader(OSRF_HTTP_HEADER_THREAD); + json = this.xreq.responseText; + stat = this.xreq.status; + + if(stat >= 400) + return this.transport_error_handler(); + + OpenSRF.Stack.push( + new OpenSRF.NetMessage(null, sender, thread, json), + { + onresponse : this.args.onresponse, + oncomplete : this.args.oncomplete, + onerror : this.args.onerror, + onmethoderror : this.method_error_handler() + } + ); +} + + +OpenSRF.XHRequest.prototype.method_error_handler = function() { + var xhr = this; + return function(req, status, status_text) { + if(xhr.args.onmethoderror) + xhr.args.onmethoderror(req, status, status_text); + if(xhr.args.onerror) + xhr.args.onerror(xhr.message, xhr.args.rcpt || xhr.args.rcpt_service, xhr.args.thread); + } +} + +OpenSRF.XHRequest.prototype.transport_error_handler = function() { + if(this.args.ontransporterror) + this.args.ontransporterror(this.xreq); + if(this.args.onerror) + this.args.onerror(this.message, this.args.rcpt || this.args.rcpt_service, this.args.thread); +} + + -- 2.11.0