]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_app_session.c
ba43416081e370495582aeb7e2cd4be544712a5b
[OpenSRF.git] / src / libopensrf / osrf_app_session.c
1 /**
2         @file osrf_app_session.c
3         @brief Implementation of osrfAppSession.
4 */
5
6 #include <time.h>
7 #include "opensrf/osrf_app_session.h"
8 #include "opensrf/osrf_stack.h"
9
10 struct osrf_app_request_struct {
11         /** The controlling session. */
12         struct osrf_app_session_struct* session;
13
14         /** Request id.  It is the same as the thread_trace of the REQUEST message
15                 for which it was created.
16         */
17         int request_id;
18         /** True if we have received a 'request complete' message from our request. */
19         int complete;
20         /** The original REQUEST message payload. */
21         osrfMessage* payload;
22         /** Linked list of responses to the request. */
23         osrfMessage* result;
24
25         /** Boolean; if true, then a call that is waiting on a response will reset the
26         timeout and set this variable back to false. */
27         int reset_timeout;
28 };
29 typedef struct osrf_app_request_struct osrfAppRequest;
30
31 /* Send the given message */
32 static int _osrf_app_session_send( osrfAppSession*, osrfMessage* msg );
33
34 static int osrfAppSessionMakeLocaleRequest(
35                 osrfAppSession* session, const jsonObject* params, const char* method_name,
36                 int protocol, osrfStringArray* param_strings, char* locale );
37
38 /** @brief The global session cache.
39
40         Key: session_id.  Data: osrfAppSession.
41 */
42 static osrfHash* osrfAppSessionCache = NULL;
43
44 // --------------------------------------------------------------------------
45 // Request API
46 // --------------------------------------------------------------------------
47
48 /**
49         @brief Create a new osrfAppRequest.
50         @param session Pointer to the osrfAppSession that will own the new osrfAppRequest.
51         @param msg Pointer to the osrfMessage representing the request.
52         @return Pointer to the new osrfAppRequest.
53
54         The calling code is responsible for freeing the osrfAppRequest by calling
55         _osrf_app_request_free().  In practice this normally happens via a callback installed
56         in an osrfList.
57 */
58 static osrfAppRequest* _osrf_app_request_init(
59                 osrfAppSession* session, osrfMessage* msg ) {
60
61         osrfAppRequest* req =
62                 (osrfAppRequest*) safe_malloc(sizeof(osrfAppRequest));
63
64         req->session        = session;
65         req->request_id     = msg->thread_trace;
66         req->complete       = 0;
67         req->payload        = msg;
68         req->result         = NULL;
69         req->reset_timeout  = 0;
70
71         return req;
72 }
73
74
75 /**
76         @brief Free an osrfAppRequest and everything it owns.
77         @param req Pointer to an osrfAppRequest, cast to a void pointer.
78
79         This function is installed as a callback in the osrfList request_queue, a member
80         of osrfAppSession.
81 */
82 static void _osrf_app_request_free( void * req ){
83         if( req ) {
84                 osrfAppRequest* r = (osrfAppRequest*) req;
85                 if( r->payload )
86                         osrfMessageFree( r->payload );
87
88                 /* Free the messages in the result queue */
89                 osrfMessage* next_msg;
90                 while( r->result ) {
91                         next_msg = r->result->next;
92                         osrfMessageFree( r->result );
93                         r->result = next_msg;
94                 }
95
96                 free( r );
97         }
98 }
99
100 /**
101         @brief Append a new message to the list of responses to a request.
102         @param req Pointer to the osrfAppRequest for the original REQUEST message.
103         @param result Pointer to an osrfMessage received in response to the request.
104
105         We maintain a linked list of response messages, and traverse it to find the end.
106 */
107 static void _osrf_app_request_push_queue( osrfAppRequest* req, osrfMessage* result ){
108         if(req == NULL || result == NULL)
109                 return;
110
111         osrfLogDebug( OSRF_LOG_MARK, "App Session pushing request [%d] onto request queue",
112                         result->thread_trace );
113         if(req->result == NULL) {
114                 req->result = result;   // Add the first node
115
116         } else {
117
118                 // Find the last node in the list, and append the new node to it
119                 osrfMessage* ptr = req->result;
120                 osrfMessage* ptr2 = req->result->next;
121                 while( ptr2 ) {
122                         ptr = ptr2;
123                         ptr2 = ptr2->next;
124                 }
125                 ptr->next = result;
126         }
127 }
128
129 /**
130         @brief Remove an osrfAppRequest (identified by request_id) from an osrfAppSession.
131         @param session Pointer to the osrfAppSession that owns the osrfAppRequest.
132         @param req_id request_id of the osrfAppRequest to be removed.
133
134         The osrfAppRequest itself is freed by a callback installed in the osrfList where it resides.
135 */
136 void osrf_app_session_request_finish(
137                 osrfAppSession* session, int req_id ){
138
139         if( session ) {
140                 osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
141                 if(req)
142                         osrfListRemove( req->session->request_queue, req->request_id );
143         }
144 }
145
146
147 /**
148         @brief Set the timeout for a request to one second.
149         @param session Pointer to the relevant osrfAppSession.
150         @param req_id Request ID of the request whose timeout is to be reset.
151
152         The request to be reset is identified by the combination of session and request id.
153 */
154 void osrf_app_session_request_reset_timeout( osrfAppSession* session, int req_id ) {
155         if(session == NULL)
156                 return;
157         osrfLogDebug( OSRF_LOG_MARK, "Resetting request timeout %d", req_id );
158         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
159         if( req )
160                 req->reset_timeout = 1;
161 }
162
163 /**
164         Checks the receive queue for messages.  If any are found, the first
165         is popped off and returned.  Otherwise, this method will wait at most timeout
166         seconds for a message to appear in the receive queue.  Once it arrives it is returned.
167         If no messages arrive in the timeout provided, null is returned.
168 */
169 /**
170         @brief Return the next response message for a given request, subject to a timeout.
171         @param req Pointer to the osrfAppRequest representing the request.
172         @param timeout Maxmimum time to wait, in seconds.
173
174         @return 
175
176         If the input queue for this request is not empty, dequeue the next message and return it.
177 */
178 static osrfMessage* _osrf_app_request_recv( osrfAppRequest* req, int timeout ) {
179
180         if(req == NULL) return NULL;
181
182         if( req->result != NULL ) {
183                 /* Dequeue the next message in the list */
184                 osrfMessage* tmp_msg = req->result;
185                 req->result = req->result->next;
186                 return tmp_msg;
187         }
188
189         time_t start = time(NULL);
190         time_t remaining = (time_t) timeout;
191
192         while( remaining >= 0 ) {
193                 /* tell the session to wait for stuff */
194                 osrfLogDebug( OSRF_LOG_MARK,  "In app_request receive with remaining time [%d]",
195                                 (int) remaining );
196
197                 osrf_app_session_queue_wait( req->session, 0, NULL );
198                 if(req->session->transport_error) {
199                         osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
200                         return NULL;
201                 }
202
203                 if( req->result != NULL ) { /* if we received anything */
204                         /* pop off the first message in the list */
205                         osrfLogDebug( OSRF_LOG_MARK, "app_request_recv received a message, returning it" );
206                         osrfMessage* ret_msg = req->result;
207                         req->result = ret_msg->next;
208                         if (ret_msg->sender_locale)
209                                 osrf_app_session_set_locale(req->session, ret_msg->sender_locale);
210
211                         return ret_msg;
212                 }
213
214                 if( req->complete )
215                         return NULL;
216
217                 osrf_app_session_queue_wait( req->session, (int) remaining, NULL );
218
219                 if(req->session->transport_error) {
220                         osrfLogError(OSRF_LOG_MARK, "Transport error in recv()");
221                         return NULL;
222                 }
223
224                 if( req->result != NULL ) { /* if we received anything */
225                         /* pop off the first message in the list */
226                         osrfLogDebug( OSRF_LOG_MARK,  "app_request_recv received a message, returning it");
227                         osrfMessage* ret_msg = req->result;
228                         req->result = ret_msg->next;
229                         if (ret_msg->sender_locale)
230                                 osrf_app_session_set_locale(req->session, ret_msg->sender_locale);
231
232                         return ret_msg;
233                 }
234                 if( req->complete )
235                         return NULL;
236
237                 if(req->reset_timeout) {
238                         remaining = (time_t) timeout;
239                         req->reset_timeout = 0;
240                         osrfLogDebug( OSRF_LOG_MARK, "Received a timeout reset");
241                 } else {
242                         remaining -= (int) (time(NULL) - start);
243                 }
244         }
245
246         char* paramString = jsonObjectToJSON(req->payload->_params);
247         osrfLogInfo( OSRF_LOG_MARK, "Returning NULL from app_request_recv after timeout: %s %s",
248                 req->payload->method_name, paramString);
249         free(paramString);
250
251         return NULL;
252 }
253
254 /** Resend this requests original request message */
255 static int _osrf_app_request_resend( osrfAppRequest* req ) {
256         if(req == NULL) return 0;
257         if(!req->complete) {
258                 osrfLogDebug( OSRF_LOG_MARK,  "Resending request [%d]", req->request_id );
259                 return _osrf_app_session_send( req->session, req->payload );
260         }
261         return 1;
262 }
263
264
265 // --------------------------------------------------------------------------
266 // Session API
267 // --------------------------------------------------------------------------
268
269 /** Install a locale for the session */
270 char* osrf_app_session_set_locale( osrfAppSession* session, const char* locale ) {
271         if (!session || !locale)
272                 return NULL;
273
274         if(session->session_locale) {
275                 if( strlen(session->session_locale) >= strlen(locale) ) {
276                         /* There's room available; just copy */
277                         strcpy(session->session_locale, locale);
278                 } else {
279                         free(session->session_locale);
280                         session->session_locale = strdup( locale );
281                 }
282         } else {
283                 session->session_locale = strdup( locale );
284         }
285
286         return session->session_locale;
287 }
288
289 /**
290         @brief Find the osrfAppSession for a given session id.
291         @param session_id The session id to look for.
292         @return Pointer to the corresponding osrfAppSession if found, or NULL if not.
293
294         Search the global session cache for the specified session id.
295 */
296 osrfAppSession* osrf_app_session_find_session( const char* session_id ) {
297         if(session_id)
298                 return osrfHashGet( osrfAppSessionCache, session_id );
299         return NULL;
300 }
301
302
303 /**
304         @brief Add a session to the global session cache, keyed by session id.
305         @param session Pointer to the osrfAppSession to be added.
306
307         If a cache doesn't exist yet, create one.  It's an osrfHash using session ids for the
308         key and osrfAppSessions for the data.
309 */
310 static void _osrf_app_session_push_session( osrfAppSession* session ) {
311         if( session ) {
312                 if( osrfAppSessionCache == NULL )
313                         osrfAppSessionCache = osrfNewHash();
314                 if( osrfHashGet( osrfAppSessionCache, session->session_id ) )
315                         return;   // A session with this id is already in the cache.  Shouldn't happen.
316                 osrfHashSet( osrfAppSessionCache, session, session->session_id );
317         }
318 }
319
320 /** Allocates and initializes a new app_session */
321
322 osrfAppSession* osrfAppSessionClientInit( const char* remote_service ) {
323
324         if (!remote_service) {
325                 osrfLogWarning( OSRF_LOG_MARK, "No remote service specified in osrfAppSessionClientInit");
326                 return NULL;
327         }
328
329         osrfAppSession* session = safe_malloc(sizeof(osrfAppSession));
330
331         session->transport_handle = osrfSystemGetTransportClient();
332         if( session->transport_handle == NULL ) {
333                 osrfLogWarning( OSRF_LOG_MARK, "No transport client for service 'client'");
334                 free( session );
335                 return NULL;
336         }
337
338         osrfStringArray* arr = osrfNewStringArray(8);
339         osrfConfigGetValueList(NULL, arr, "/domain");
340         const char* domain = osrfStringArrayGetString(arr, 0);
341
342         if (!domain) {
343                 osrfLogWarning( OSRF_LOG_MARK, "No domains specified in the OpenSRF config file");
344                 free( session );
345                 osrfStringArrayFree(arr);
346                 return NULL;
347         }
348
349         char* router_name = osrfConfigGetValue(NULL, "/router_name");
350         if (!router_name) {
351                 osrfLogWarning( OSRF_LOG_MARK, "No router name specified in the OpenSRF config file");
352                 free( session );
353                 osrfStringArrayFree(arr);
354                 return NULL;
355         }
356
357         char target_buf[512];
358         target_buf[ 0 ] = '\0';
359
360         int len = snprintf( target_buf, sizeof(target_buf), "%s@%s/%s",
361                         router_name ? router_name : "(null)",
362                         domain ? domain : "(null)",
363                         remote_service ? remote_service : "(null)" );
364         osrfStringArrayFree(arr);
365         //free(domain);
366         free(router_name);
367
368         if( len >= sizeof( target_buf ) ) {
369                 osrfLogWarning( OSRF_LOG_MARK, "Buffer overflow for remote_id");
370                 free( session );
371                 return NULL;
372         }
373
374         session->request_queue = osrfNewList();
375         session->request_queue->freeItem = &_osrf_app_request_free;
376         session->remote_id = strdup(target_buf);
377         session->orig_remote_id = strdup(session->remote_id);
378         session->remote_service = strdup(remote_service);
379         session->session_locale = NULL;
380         session->transport_error = 0;
381
382         #ifdef ASSUME_STATELESS
383         session->stateless = 1;
384         osrfLogDebug( OSRF_LOG_MARK, "%s session is stateless", remote_service );
385         #else
386         session->stateless = 0;
387         osrfLogDebug( OSRF_LOG_MARK, "%s session is NOT stateless", remote_service );
388         #endif
389
390         /* build a chunky, random session id */
391         char id[256];
392
393         snprintf(id, sizeof(id), "%f.%d%ld", get_timestamp_millis(), (int)time(NULL), (long) getpid());
394         session->session_id = strdup(id);
395         osrfLogDebug( OSRF_LOG_MARK,  "Building a new client session with id [%s] [%s]",
396                         session->remote_service, session->session_id );
397
398         session->thread_trace = 0;
399         session->state = OSRF_SESSION_DISCONNECTED;
400         session->type = OSRF_SESSION_CLIENT;
401
402         session->userData = NULL;
403         session->userDataFree = NULL;
404
405         _osrf_app_session_push_session( session );
406         return session;
407 }
408
409 osrfAppSession* osrf_app_server_session_init(
410                 const char* session_id, const char* our_app, const char* remote_id ) {
411
412         osrfLogDebug( OSRF_LOG_MARK, "Initing server session with session id %s, service %s,"
413                         " and remote_id %s", session_id, our_app, remote_id );
414
415         osrfAppSession* session = osrf_app_session_find_session( session_id );
416         if(session) return session;
417
418         session = safe_malloc(sizeof(osrfAppSession));
419
420         session->transport_handle = osrfSystemGetTransportClient();
421         if( session->transport_handle == NULL ) {
422                 osrfLogWarning( OSRF_LOG_MARK, "No transport client for service '%s'", our_app );
423                 free(session);
424                 return NULL;
425         }
426
427         int stateless = 0;
428         char* statel = osrf_settings_host_value("/apps/%s/stateless", our_app );
429         if(statel) stateless = atoi(statel);
430         free(statel);
431
432
433         session->request_queue = osrfNewList();
434         session->request_queue->freeItem = &_osrf_app_request_free;
435         session->remote_id = strdup(remote_id);
436         session->orig_remote_id = strdup(remote_id);
437         session->session_id = strdup(session_id);
438         session->remote_service = strdup(our_app);
439         session->stateless = stateless;
440
441         #ifdef ASSUME_STATELESS
442         session->stateless = 1;
443         #else
444         session->stateless = 0;
445         #endif
446
447         session->thread_trace = 0;
448         session->state = OSRF_SESSION_DISCONNECTED;
449         session->type = OSRF_SESSION_SERVER;
450         session->session_locale = NULL;
451
452         session->userData = NULL;
453         session->userDataFree = NULL;
454
455         _osrf_app_session_push_session( session );
456         return session;
457
458 }
459
460 /**
461         @brief Create a REQUEST message, send it, and save it for future reference.
462         @param session Pointer to the current session, which has the addressing information.
463         @param params One way of specifying the parameters for the method.
464         @param method_name The name of the method to be called.
465         @param protocol Protocol.
466         @param param_strings Another way of specifying the parameters for the method.
467         @return The request ID of the resulting REQUEST message, or -1 upon error.
468
469         DEPRECATED.  Use osrfAppSessionSendRequest() instead.  It is identical except that it
470         doesn't use the param_strings argument, which is redundant, confusing, and unused.
471
472         If @a params is non-NULL, use it to specify the parameters to the method.  Otherwise
473         use @a param_strings.
474
475         If @a params points to a JSON_ARRAY, then pass each element of the array as a separate
476         parameter.  If @a params points to any other kind of jsonObject, pass it as a single
477         parameter.
478
479         If @a params is NULL, and @a param_strings is not NULL, then each pointer in the
480         osrfStringArray must point to a JSON string encoding a parameter.  Pass them.
481
482         At this writing, all calls to this function use @a params to pass parameters, rather than
483         @a param_strings.
484
485         This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
486 */
487 int osrfAppSessionMakeRequest(
488                 osrfAppSession* session, const jsonObject* params,
489                 const char* method_name, int protocol, osrfStringArray* param_strings ) {
490
491         return osrfAppSessionMakeLocaleRequest( session, params,
492                         method_name, protocol, param_strings, NULL );
493 }
494
495 /**
496         @brief Create a REQUEST message, send it, and save it for future reference.
497         @param session Pointer to the current session, which has the addressing information.
498         @param params One way of specifying the parameters for the method.
499         @param method_name The name of the method to be called.
500         @param protocol Protocol.
501         @return The request ID of the resulting REQUEST message, or -1 upon error.
502
503         If @a params points to a JSON_ARRAY, then pass each element of the array as a separate
504         parameter.  If @a params points to any other kind of jsonObject, pass it as a single
505         parameter.
506
507         This function is a thin wrapper for osrfAppSessionMakeLocaleRequest().
508 */
509 int osrfAppSessionSendRequest( osrfAppSession* session, const jsonObject* params,
510                 const char* method_name, int protocol ) {
511
512         return osrfAppSessionMakeLocaleRequest( session, params,
513                  method_name, protocol, NULL, NULL );
514 }
515
516 /**
517         @brief Create a REQUEST message, send it, and save it for future reference.
518         @param session Pointer to the current session, which has the addressing information.
519         @param params One way of specifying the parameters for the method.
520         @param method_name The name of the method to be called.
521         @param protocol Protocol.
522         @param param_strings Another way of specifying the parameters for the method.
523         @param locale Pointer to a locale string.
524         @return The request ID of the resulting REQUEST message, or -1 upon error.
525
526         See the discussion of osrfAppSessionSendRequest(), which at this writing is the only
527         place that calls this function, except for the similar but deprecated function
528         osrfAppSessionMakeRequest().
529
530         At this writing, the @a param_strings and @a locale parameters are always NULL.
531 */
532 static int osrfAppSessionMakeLocaleRequest(
533                 osrfAppSession* session, const jsonObject* params, const char* method_name,
534                 int protocol, osrfStringArray* param_strings, char* locale ) {
535
536         if(session == NULL) return -1;
537
538         osrfLogMkXid();
539
540         osrfMessage* req_msg = osrf_message_init( REQUEST, ++(session->thread_trace), protocol );
541         osrf_message_set_method(req_msg, method_name);
542
543         if (locale) {
544                 osrf_message_set_locale(req_msg, locale);
545         } else if (session->session_locale) {
546                 osrf_message_set_locale(req_msg, session->session_locale);
547         }
548
549         if(params) {
550                 osrf_message_set_params(req_msg, params);
551
552         } else {
553
554                 if(param_strings) {
555                         int i;
556                         for(i = 0; i!= param_strings->size ; i++ ) {
557                                 osrf_message_add_param(req_msg,
558                                         osrfStringArrayGetString(param_strings,i));
559                         }
560                 }
561         }
562
563         osrfAppRequest* req = _osrf_app_request_init( session, req_msg );
564         if(_osrf_app_session_send( session, req_msg ) ) {
565                 osrfLogWarning( OSRF_LOG_MARK,  "Error sending request message [%d]",
566                                 session->thread_trace );
567                 _osrf_app_request_free(req);
568                 return -1;
569         }
570
571         osrfLogDebug( OSRF_LOG_MARK,  "Pushing [%d] onto request queue for session [%s] [%s]",
572                         req->request_id, session->remote_service, session->session_id );
573         osrfListSet( session->request_queue, req, req->request_id );
574         return req->request_id;
575 }
576
577 void osrf_app_session_set_complete( osrfAppSession* session, int request_id ) {
578         if(session == NULL)
579                 return;
580
581         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, request_id );
582         if(req) req->complete = 1;
583 }
584
585 int osrf_app_session_request_complete( const osrfAppSession* session, int request_id ) {
586         if(session == NULL)
587                 return 0;
588         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, request_id );
589         if(req)
590                 return req->complete;
591         return 0;
592 }
593
594
595 /** Resets the remote connection id to that of the original*/
596 void osrf_app_session_reset_remote( osrfAppSession* session ){
597         if( session==NULL )
598                 return;
599
600         osrfLogDebug( OSRF_LOG_MARK,  "App Session [%s] [%s] resetting remote id to %s",
601                         session->remote_service, session->session_id, session->orig_remote_id );
602
603         osrf_app_session_set_remote( session, session->orig_remote_id );
604 }
605
606 void osrf_app_session_set_remote( osrfAppSession* session, const char* remote_id ) {
607         if(session == NULL)
608                 return;
609
610         if( session->remote_id ) {
611                 if( strlen(session->remote_id) >= strlen(remote_id) ) {
612                         // There's enough room; just copy it
613                         strcpy(session->remote_id, remote_id);
614                 } else {
615                         free(session->remote_id );
616                         session->remote_id = strdup( remote_id );
617                 }
618         } else
619                 session->remote_id = strdup( remote_id );
620 }
621
622 /**
623         pushes the given message into the result list of the app_request
624         with the given request_id
625 */
626 int osrf_app_session_push_queue(
627                 osrfAppSession* session, osrfMessage* msg ){
628         if(session == NULL || msg == NULL) return 0;
629
630         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, msg->thread_trace );
631         if(req == NULL) return 0;
632         _osrf_app_request_push_queue( req, msg );
633
634         return 0;
635 }
636
637 /** Attempts to connect to the remote service */
638 int osrfAppSessionConnect( osrfAppSession* session ) {
639
640         if(session == NULL)
641                 return 0;
642
643         if(session->state == OSRF_SESSION_CONNECTED) {
644                 return 1;
645         }
646
647         int timeout = 5; /* XXX CONFIG VALUE */
648
649         osrfLogDebug( OSRF_LOG_MARK,  "AppSession connecting to %s", session->remote_id );
650
651         /* defaulting to protocol 1 for now */
652         osrfMessage* con_msg = osrf_message_init( CONNECT, session->thread_trace, 1 );
653         osrf_app_session_reset_remote( session );
654         session->state = OSRF_SESSION_CONNECTING;
655         int ret = _osrf_app_session_send( session, con_msg );
656         osrfMessageFree(con_msg);
657         if(ret)
658                 return 0;
659
660         time_t start = time(NULL);
661         time_t remaining = (time_t) timeout;
662
663         while( session->state != OSRF_SESSION_CONNECTED && remaining >= 0 ) {
664                 osrf_app_session_queue_wait( session, remaining, NULL );
665                 if(session->transport_error) {
666                         osrfLogError(OSRF_LOG_MARK, "cannot communicate with %s", session->remote_service);
667                         return 0;
668                 }
669                 remaining -= (int) (time(NULL) - start);
670         }
671
672         if(session->state == OSRF_SESSION_CONNECTED)
673                 osrfLogDebug( OSRF_LOG_MARK, " * Connected Successfully to %s", session->remote_service );
674
675         if(session->state != OSRF_SESSION_CONNECTED)
676                 return 0;
677
678         return 1;
679 }
680
681
682
683 /** Disconnects from the remote service */
684 int osrf_app_session_disconnect( osrfAppSession* session){
685         if(session == NULL)
686                 return 1;
687
688         if(session->state == OSRF_SESSION_DISCONNECTED)
689                 return 1;
690
691         if(session->stateless && session->state != OSRF_SESSION_CONNECTED) {
692                 osrfLogDebug( OSRF_LOG_MARK,
693                                 "Exiting disconnect on stateless session %s",
694                                 session->session_id);
695                 return 1;
696         }
697
698         osrfLogDebug(OSRF_LOG_MARK,  "AppSession disconnecting from %s", session->remote_id );
699
700         osrfMessage* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
701         _osrf_app_session_send( session, dis_msg );
702         session->state = OSRF_SESSION_DISCONNECTED;
703
704         osrfMessageFree( dis_msg );
705         osrf_app_session_reset_remote( session );
706         return 1;
707 }
708
709 int osrf_app_session_request_resend( osrfAppSession* session, int req_id ) {
710         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
711         return _osrf_app_request_resend( req );
712 }
713
714
715 static int osrfAppSessionSendBatch( osrfAppSession* session, osrfMessage* msgs[], int size ) {
716
717         if( !(session && msgs && size > 0) ) return 0;
718         int retval = 0;
719
720         osrfMessage* msg = msgs[0];
721
722         if(msg) {
723
724                 osrf_app_session_queue_wait( session, 0, NULL );
725
726                 if(session->state != OSRF_SESSION_CONNECTED)  {
727
728                         if(session->stateless) { /* stateless session always send to the root listener */
729                                 osrf_app_session_reset_remote(session);
730
731                         } else {
732
733                                 /* do an auto-connect if necessary */
734                                 if( ! session->stateless &&
735                                         (msg->m_type != CONNECT) &&
736                                         (msg->m_type != DISCONNECT) &&
737                                         (session->state != OSRF_SESSION_CONNECTED) ) {
738
739                                         if(!osrfAppSessionConnect( session ))
740                                                 return 0;
741                                 }
742                         }
743                 }
744         }
745
746         char* string = osrfMessageSerializeBatch(msgs, size);
747
748         if( string ) {
749
750                 transport_message* t_msg = message_init(
751                                 string, "", session->session_id, session->remote_id, NULL );
752                 message_set_osrf_xid( t_msg, osrfLogGetXid() );
753
754                 retval = client_send_message( session->transport_handle, t_msg );
755
756                 if( retval ) osrfLogError(OSRF_LOG_MARK, "client_send_message failed");
757
758                 osrfLogInfo(OSRF_LOG_MARK, "[%s] sent %d bytes of data to %s",
759                         session->remote_service, strlen(string), t_msg->recipient );
760
761                 osrfLogDebug(OSRF_LOG_MARK, "Sent: %s", string );
762
763                 free(string);
764                 message_free( t_msg );
765         }
766
767         return retval;
768 }
769
770
771
772 static int _osrf_app_session_send( osrfAppSession* session, osrfMessage* msg ){
773         if( !(session && msg) ) return 0;
774         osrfMessage* a[1];
775         a[0] = msg;
776         return osrfAppSessionSendBatch( session, a, 1 );
777 }
778
779
780 /**
781         Waits up to 'timeout' seconds for some data to arrive.
782         Any data that arrives will be processed according to its
783         payload and message type.  This method will return after
784         any data has arrived.
785 */
786 int osrf_app_session_queue_wait( osrfAppSession* session, int timeout, int* recvd ){
787         if(session == NULL) return 0;
788         osrfLogDebug(OSRF_LOG_MARK, "AppSession in queue_wait with timeout %d", timeout );
789         return osrf_stack_process(session->transport_handle, timeout, recvd);
790 }
791
792 /** Disconnects (if client) and removes the given session from the global session cache
793         ! This frees all attached app_requests !
794 */
795 void osrfAppSessionFree( osrfAppSession* session ){
796         if(session == NULL) return;
797
798         /* Disconnect */
799
800         osrfLogDebug(OSRF_LOG_MARK,  "AppSession [%s] [%s] destroying self and deleting requests",
801                         session->remote_service, session->session_id );
802         if(session->type == OSRF_SESSION_CLIENT
803                         && session->state != OSRF_SESSION_DISCONNECTED ) { /* disconnect if we're a client */
804                 osrfMessage* dis_msg = osrf_message_init( DISCONNECT, session->thread_trace, 1 );
805                 _osrf_app_session_send( session, dis_msg );
806                 osrfMessageFree(dis_msg);
807         }
808
809         /* Remove self from the global session cache */
810
811         osrfHashRemove( osrfAppSessionCache, session->session_id );
812
813         /* Free the memory */
814
815         if( session->userDataFree && session->userData )
816                 session->userDataFree(session->userData);
817
818         if(session->session_locale)
819                 free(session->session_locale);
820
821         free(session->remote_id);
822         free(session->orig_remote_id);
823         free(session->session_id);
824         free(session->remote_service);
825         osrfListFree(session->request_queue);
826         free(session);
827 }
828
829 osrfMessage* osrfAppSessionRequestRecv(
830                 osrfAppSession* session, int req_id, int timeout ) {
831         if(req_id < 0 || session == NULL)
832                 return NULL;
833         osrfAppRequest* req = OSRF_LIST_GET_INDEX( session->request_queue, req_id );
834         return _osrf_app_request_recv( req, timeout );
835 }
836
837
838
839 int osrfAppRequestRespond( osrfAppSession* ses, int requestId, const jsonObject* data ) {
840         if(!ses || ! data ) return -1;
841
842         osrfMessage* msg = osrf_message_init( RESULT, requestId, 1 );
843         osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
844         char* json = jsonObjectToJSON( data );
845
846         osrf_message_set_result_content( msg, json );
847         _osrf_app_session_send( ses, msg );
848
849         free(json);
850         osrfMessageFree( msg );
851
852         return 0;
853 }
854
855
856 int osrfAppRequestRespondComplete(
857                 osrfAppSession* ses, int requestId, const jsonObject* data ) {
858
859         osrfMessage* status = osrf_message_init( STATUS, requestId, 1);
860         osrf_message_set_status_info( status, "osrfConnectStatus", "Request Complete",
861                         OSRF_STATUS_COMPLETE );
862
863         if (data) {
864                 osrfMessage* payload = osrf_message_init( RESULT, requestId, 1 );
865                 osrf_message_set_status_info( payload, NULL, "OK", OSRF_STATUS_OK );
866
867                 char* json = jsonObjectToJSON( data );
868                 osrf_message_set_result_content( payload, json );
869                 free(json);
870
871                 osrfMessage* ms[2];
872                 ms[0] = payload;
873                 ms[1] = status;
874
875                 osrfAppSessionSendBatch( ses, ms, 2 );
876
877                 osrfMessageFree( payload );
878         } else {
879                 osrfAppSessionSendBatch( ses, &status, 1 );
880         }
881
882         osrfMessageFree( status );
883
884         return 0;
885 }
886
887 int osrfAppSessionStatus( osrfAppSession* ses, int type,
888                 const char* name, int reqId, const char* message ) {
889
890         if(ses) {
891                 osrfMessage* msg = osrf_message_init( STATUS, reqId, 1);
892                 osrf_message_set_status_info( msg, name, message, type );
893                 _osrf_app_session_send( ses, msg );
894                 osrfMessageFree( msg );
895                 return 0;
896         }
897         return -1;
898 }
899
900 /**
901         @brief Free the global session cache.
902         
903         Note that the osrfHash that implements the global session cache does @em not have a
904         callback function installed for freeing its cargo.  As a result, any outstanding
905         osrfAppSessions are leaked, along with all the osrfAppRequests and osrfMessages they
906         own.
907 */
908 void osrfAppSessionCleanup( void ) {
909         osrfHashFree(osrfAppSessionCache);
910         osrfAppSessionCache = NULL;
911 }
912
913
914
915
916