af1987371f0796e6b7077da0f0915d8b0dc087e6
[OpenSRF.git] / src / javascript / opensrf.js
1 /* -----------------------------------------------------------------------
2  * Copyright (C) 2008  Georgia Public Library Service
3  * Bill Erickson <erickson@esilibrary.com>
4  *  
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * ----------------------------------------------------------------------- */
15
16 /* session states */
17 var OSRF_APP_SESSION_CONNECTED = 0;
18 var OSRF_APP_SESSION_CONNECTING = 1;
19 var OSRF_APP_SESSION_DISCONNECTED = 2;
20
21 /* types of transport layers */
22 var OSRF_TRANSPORT_TYPE_XHR = 1;
23 var OSRF_TRANSPORT_TYPE_XMPP = 2;
24
25 /* message types */
26 var OSRF_MESSAGE_TYPE_REQUEST = 'REQUEST';
27 var OSRF_MESSAGE_TYPE_STATUS = 'STATUS';
28 var OSRF_MESSAGE_TYPE_RESULT = 'RESULT';
29 var OSRF_MESSAGE_TYPE_CONNECT = 'CONNECT';
30 var OSRF_MESSAGE_TYPE_DISCONNECT = 'DISCONNECT';
31
32 /* message statuses */
33 var OSRF_STATUS_CONTINUE = 100;
34 var OSRF_STATUS_OK = 200;
35 var OSRF_STATUS_ACCEPTED = 202;
36 var OSRF_STATUS_COMPLETE = 205;
37 var OSRF_STATUS_REDIRECTED = 307;
38 var OSRF_STATUS_BADREQUEST = 400;
39 var OSRF_STATUS_UNAUTHORIZED = 401;
40 var OSRF_STATUS_FORBIDDEN = 403;
41 var OSRF_STATUS_NOTFOUND = 404;
42 var OSRF_STATUS_NOTALLOWED = 405;
43 var OSRF_STATUS_TIMEOUT = 408;
44 var OSRF_STATUS_EXPFAILED = 417;
45 var OSRF_STATUS_INTERNALSERVERERROR = 500;
46 var OSRF_STATUS_NOTIMPLEMENTED = 501;
47 var OSRF_STATUS_VERSIONNOTSUPPORTED = 505;
48
49 var OpenSRF = {};
50
51 /* makes cls a subclass of pcls */
52 OpenSRF.set_subclass = function(cls, pcls) {
53     var str = cls+'.prototype = new '+pcls+'();';
54     str += cls+'.prototype.constructor = '+cls+';';
55     str += cls+'.baseClass = '+pcls+'.prototype.constructor;';
56     str += cls+'.prototype.super = '+pcls+'.prototype;';
57     eval(str);
58 }
59
60
61 /* general session superclass */
62 OpenSRF.Session = function() {
63     this.remote_id = null;
64     this.state = OSRF_APP_SESSION_DISCONNECTED;
65 }
66
67 OpenSRF.Session.transport = OSRF_TRANSPORT_TYPE_XHR; /* default to XHR */
68 OpenSRF.Session.cache = {};
69 OpenSRF.Session.find_session = function(thread_trace) {
70     return OpenSRF.Session.cache[thread_trace];
71 }
72 OpenSRF.Session.prototype.cleanup = function() {
73     delete OpenSRF.Session.cache[this.thread];
74 }
75
76 OpenSRF.Session.prototype.send = function(osrf_msg, args) {
77     switch(OpenSRF.Session.transport) {
78         case OSRF_TRANSPORT_TYPE_XHR:
79             return this.send_xhr(osrf_msg, args);
80         case OSRF_TRANSPORT_TYPE_XMPP:
81             return this.send_xmpp(osrf_msg, args);
82     }
83 }
84
85 OpenSRF.Session.prototype.send_xhr = function(osrf_msg, args) {
86     args.thread = this.thread;
87     args.rcpt = this.remote_id;
88     args.rcpt_service = this.service;
89     new OpenSRF.XHRequest(osrf_msg, args).send();
90 }
91
92 OpenSRF.Session.prototype.send_xmpp = function(osrf_msg, args) {
93     alert('xmpp transport not yet implemented');
94 }
95
96
97 /* client sessions make requests */
98 OpenSRF.ClientSession = function(service) {
99     this.service = service
100     this.remote_id = null;
101     this.locale = 'en-US';
102     this.last_id = 0;
103     this.thread = Math.random() + '' + new Date().getTime();
104     this.do_connect = false;
105     this.requests = [];
106     this.onconnect = null;
107     OpenSRF.Session.cache[this.thread] = this;
108 }
109 OpenSRF.set_subclass('OpenSRF.ClientSession', 'OpenSRF.Session');
110
111
112 OpenSRF.ClientSession.prototype.connect = function(args) {
113     if(args && args.onconnect)
114         this.onconnect = args.onconnect;
115
116     message = new osrfMessage({
117         'threadTrace' : this.reqid, 
118         'type' : OSRF_MESSAGE_TYPE_CONNECT,
119     });
120
121     this.send(message, {'timeout' : this.timeout});
122 }
123
124 OpenSRF.ClientSession.prototype.disconnect = function(args) {
125     this.send(
126         new osrfMessage({
127             'threadTrace' : this.reqid, 
128             'type' : OSRF_MESSAGE_TYPE_DISCONNECT,
129         })
130     );
131 }
132
133
134 OpenSRF.ClientSession.prototype.request = function(args) {
135
136     if(typeof args == 'string') { 
137         params = [];
138         for(var i = 1; i < arguments.length; i++)
139             params.push(arguments[i]);
140
141         args = {
142             method : args, 
143             params : params
144         };
145     } else {
146         if(typeof args == 'undefined')
147             args = {};
148     }
149
150     var req = new OpenSRF.Request(this, this.last_id++, args);
151     this.requests.push(req);
152     return req;
153 }
154
155 OpenSRF.ClientSession.prototype.find_request = function(reqid) {
156     for(var i = 0; i < this.requests.length; i++) {
157         var req = this.requests[i];
158         if(req.reqid == reqid)
159             return req;
160     }
161     return null;
162 }
163
164 OpenSRF.Request = function(session, reqid, args) {
165     this.session = session;
166     this.reqid = reqid;
167     this.onresponse = args.onresponse;
168     this.onerror = args.onerror;
169     this.oncomplete = args.oncomplete;
170     this.method = args.method;
171     this.params = args.params;
172     this.timeout = args.timeout;
173     this.response_queue = [];
174     this.complete = false;
175 }
176
177 OpenSRF.Request.prototype.recv = function(timeout) {
178     if(this.response_queue.length > 0)
179         return this.response_queue.shift();
180 }
181
182 OpenSRF.Request.prototype.send = function() {
183     method = new osrfMethod({'method':this.method, 'params':this.params});
184     message = new osrfMessage({
185         'threadTrace' : this.reqid, 
186         'type' : OSRF_MESSAGE_TYPE_REQUEST, 
187         'payload' : method, 
188         'locale' : this.session.locale
189     });
190
191     this.session.send(message, {
192         'timeout' : this.timeout,
193         'onresponse' : this.onresponse,
194         'oncomplete' : this.oncomplete,
195         'onerror' : this.onerror
196     });
197 }
198
199 OpenSRF.NetMessage = function(to, from, thread, body) {
200     this.to = to;
201     this.from = from;
202     this.thread = thread;
203     this.body = body;
204 }
205
206 OpenSRF.Stack = function() {
207 }
208
209 OpenSRF.Stack.push = function(net_msg, stack_callback) {
210     var ses = OpenSRF.Session.find_session(net_msg.thread); 
211     if(!ses) return;
212     ses.remote_id = net_msg.sender;
213     osrf_msgs = JSON2js(net_msg.body);
214     for(var i = 0; i < osrf_msgs.length; i++) 
215         OpenSRF.Stack.handle_message(ses, osrf_msgs[i], stack_callback);        
216 }
217
218 OpenSRF.Stack.handle_message = function(ses, osrf_msg, stack_callback) {
219     
220     var req = null;
221
222     if(osrf_msg.type() == OSRF_MESSAGE_TYPE_STATUS) {
223
224         var payload = osrf_msg.payload();
225         var status = payload.statusCode();
226         var status_text = payload.status();
227
228         if(status == OSRF_STATUS_COMPLETE) {
229             req = ses.find_request(osrf_msg.threadTrace());
230             if(req) req.complete = true;
231         }
232
233         if(status == OSRF_STATUS_OK) {
234             ses.state = OSRF_APP_SESSION_CONNECTED;
235         }
236
237         if(status == OSRF_STATUS_NOTFOUND) {
238             alert('NOT_FOUND: ' + status_text);
239             return;
240         }
241     }
242
243     if(osrf_msg.type() == OSRF_MESSAGE_TYPE_RESULT) {
244         req = ses.find_request(osrf_msg.threadTrace());
245         if(req) 
246             req.response_queue.push(osrf_msg.payload());
247     }
248
249     if(stack_callback)
250         stack_callback(ses, req);
251 }
252
253 /* The following classes map directly to network-serializable opensrf objects */
254
255 function osrfMessage(hash) {
256     this.hash = hash;
257     this._encodehash = true;
258 }
259 osrfMessage.prototype.threadTrace = function(d) { 
260     if(arguments.length == 1) 
261         this.hash.threadTrace = d; 
262     return this.hash.threadTrace; 
263 }
264 osrfMessage.prototype.type = function(d) { 
265     if(arguments.length == 1) 
266         this.hash.type = d; 
267     return this.hash.type; 
268 }
269 osrfMessage.prototype.payload = function(d) { 
270     if(arguments.length == 1) 
271         this.hash.payload = d; 
272     return this.hash.payload; 
273 }
274 osrfMessage.prototype.locale = function(d) { 
275     if(arguments.length == 1) 
276         this.hash.locale = d; 
277     return this.hash.locale; 
278 }
279 osrfMessage.prototype.serialize = function() {
280     return {
281         "__c":"osrfMessage",
282         "__p": {
283             'threadTrace' : this.hash.threadTrace,
284             'type' : this.hash.type,
285             'payload' : (this.hash.payload) ? this.hash.payload.serialize() : 'null',
286             'locale' : this.hash.locale
287         }
288     };
289 }
290
291 function osrfMethod(hash) {
292     this.hash = hash;
293     this._encodehash = true;
294
295 osrfMethod.prototype.method = function() {
296     if(arguments.length == 1) 
297         this.hash.method = d; 
298     return this.hash.method; 
299 }
300 osrfMethod.prototype.params = function() {
301     if(arguments.length == 1) 
302         this.hash.params = d; 
303     return this.hash.params; 
304 }
305 osrfMethod.prototype.serialize = function() {
306     return {
307         "__c":"osrfMethod",
308         "__p": {
309             'method' : this.hash.method,
310             'params' : this.hash.params
311         }
312     };
313 }
314
315 function osrfMethodException(hash) {
316     this.hash = hash;
317     this._encodehash = true;
318 }
319 osrfMethodException.prototype.status = function() {
320     if(arguments.length == 1) 
321         this.hash.status = d; 
322     return this.hash.status; 
323 }
324 osrfMethodException.prototype.statusCode = function() {
325     if(arguments.length == 1) 
326         this.hash.statusCode = d; 
327     return this.hash.statusCode; 
328 }
329 function osrfConnectStatus(hash) { 
330     this.hash = hash;
331     this._encodehash = true;
332 }
333 osrfConnectStatus.prototype.status = function() {
334     if(arguments.length == 1) 
335         this.hash.status = d; 
336     return this.hash.status; 
337 }
338 osrfConnectStatus.prototype.statusCode = function() {
339     if(arguments.length == 1) 
340         this.hash.statusCode = d; 
341     return this.hash.statusCode; 
342 }
343 function osrfResult(hash) {
344     this.hash = hash;
345     this._encodehash = true;
346 }
347 osrfResult.prototype.status = function() {
348     if(arguments.length == 1) 
349         this.hash.status = d; 
350     return this.hash.status; 
351 }
352 osrfResult.prototype.statusCode = function() {
353     if(arguments.length == 1) 
354         this.hash.statusCode = d; 
355     return this.hash.statusCode; 
356 }
357 osrfResult.prototype.content = function() {
358     if(arguments.length == 1) 
359         this.hash.content = d; 
360     return this.hash.content; 
361 }
362
363
364