]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libstack/osrf_application.c
stateful backend sessions now only timeout of no request was received within
[OpenSRF.git] / src / libstack / osrf_application.c
1 #include "osrf_application.h"
2 #include "objson/object.h"
3
4 //osrfApplication* __osrfAppList = NULL; 
5
6 osrfHash* __osrfAppHash = NULL;
7
8
9 int osrfAppRegisterApplication( char* appName, char* soFile ) {
10         if(!appName || ! soFile) return -1;
11         char* error;
12
13         if(!__osrfAppHash) __osrfAppHash = osrfNewHash();
14
15         osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
16
17         osrfApplication* app = safe_malloc(sizeof(osrfApplication));
18         app->handle = dlopen (soFile, RTLD_NOW);
19
20         if(!app->handle) {
21                 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
22                 dlerror(); /* clear the error */
23                 free(app);
24                 return -1;
25         }
26
27         app->methods = osrfNewHash();
28         osrfHashSet( __osrfAppHash, app, appName );
29
30         /* see if we can run the initialize method */
31         int (*init) (void);
32         *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
33
34         if( (error = dlerror()) != NULL ) {
35                 osrfLogWarning( OSRF_LOG_MARK, 
36                         "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
37
38         } else {
39
40                 /* run the method */
41                 int ret;
42                 if( (ret = (*init)()) ) {
43                         osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
44                                         "'osrfAppInitialize', not registering...", appName );
45                         //free(app->name); /* need a method to remove an application from the list */
46                         //free(app);
47                         return ret;
48                 }
49         }
50
51         __osrfAppRegisterSysMethods(appName);
52
53         osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
54
55         osrfLogSetAppname(appName);
56
57         return 0;
58 }
59
60 int osrfAppRunChildInit(char* appname) {
61         osrfApplication* app = _osrfAppFindApplication(appname);
62         if(!app) return -1;
63
64         char* error;
65         int ret;
66         int (*childInit) (void);
67
68         *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
69
70         if( (error = dlerror()) != NULL ) {
71                 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
72                 return 0;
73         }
74
75         if( (ret = (*childInit)()) ) {
76                 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
77                 return -1;
78         }
79
80         osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
81         return 0;
82 }
83
84
85 int osrfAppRegisterMethod( char* appName, char* methodName, 
86                 char* symbolName, char* notes, int argc, int options ) {
87
88         return osrfAppRegisterExtendedMethod(
89                         appName,
90                         methodName,
91                         symbolName,
92                         notes,
93                         argc,
94                         options,
95                         NULL
96         );
97
98 }
99
100 int osrfAppRegisterExtendedMethod( char* appName, char* methodName, 
101                 char* symbolName, char* notes, int argc, int options, void * user_data ) {
102
103         if( !appName || ! methodName  ) return -1;
104
105         osrfApplication* app = _osrfAppFindApplication(appName);
106         if(!app) {
107                 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
108                 return -1;
109         }
110
111         osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
112
113         osrfMethod* method = _osrfAppBuildMethod(
114                 methodName, symbolName, notes, argc, options, user_data );              
115         method->options = options;
116
117         /* plug the method into the list of methods */
118         osrfHashSet( app->methods, method, method->name );
119
120         if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
121                 int newops = options | OSRF_METHOD_ATOMIC;
122                 osrfMethod* atomicMethod = _osrfAppBuildMethod(
123                         methodName, symbolName, notes, argc, newops, NULL );            
124                 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
125                 atomicMethod->userData = method->userData;
126         }
127
128         return 0;
129 }
130
131
132
133 osrfMethod* _osrfAppBuildMethod( char* methodName, 
134         char* symbolName, char* notes, int argc, int options, void* user_data ) {
135
136         osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
137
138         if(methodName) method->name             = strdup(methodName);
139         if(symbolName) method->symbol           = strdup(symbolName);
140         if(notes) method->notes                         = strdup(notes);
141         if(user_data) method->userData  = user_data;
142
143         method->argc                                                    = argc;
144         method->options                                         = options;
145
146         if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
147                 char mb[strlen(method->name) + 8];
148                 sprintf(mb, "%s.atomic", method->name);
149                 free(method->name);
150                 method->name = strdup(mb);
151                 method->options |= OSRF_METHOD_STREAMING;
152         }
153
154         return method;
155 }
156
157
158 int __osrfAppRegisterSysMethods( char* app ) {
159
160         osrfAppRegisterMethod( 
161                         app, OSRF_SYSMETHOD_INTROSPECT, NULL, 
162                         "Return a list of methods whose names have the same initial "
163                         "substring as that of the provided method name PARAMS( methodNameSubstring )", 
164                         1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
165
166         osrfAppRegisterMethod( 
167                         app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL, 
168                         "Returns a complete list of methods. PARAMS()", 0, 
169                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
170
171         osrfAppRegisterMethod( 
172                         app, OSRF_SYSMETHOD_ECHO, NULL, 
173                         "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0, 
174                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
175
176         return 0;
177 }
178
179 osrfApplication* _osrfAppFindApplication( char* name ) {
180         if(!name) return NULL;
181         return (osrfApplication*) osrfHashGet(__osrfAppHash, name);
182 }
183
184 osrfMethod* __osrfAppFindMethod( osrfApplication* app, char* methodName ) {
185         if(!app || ! methodName) return NULL;
186         return (osrfMethod*) osrfHashGet( app->methods, methodName );
187 }
188
189 osrfMethod* _osrfAppFindMethod( char* appName, char* methodName ) {
190         if(!appName || ! methodName) return NULL;
191         return __osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
192 }
193
194
195 int osrfAppRunMethod( char* appName, char* methodName, 
196                 osrfAppSession* ses, int reqId, jsonObject* params ) {
197
198         if( !(appName && methodName && ses) ) return -1;
199
200         char* error;
201         osrfApplication* app;
202         osrfMethod* method;
203         osrfMethodContext context;
204
205         context.session = ses;
206         context.params = params;
207         context.request = reqId;
208         context.responses = NULL;
209
210         /* this is the method we're gonna run */
211         int (*meth) (osrfMethodContext*);       
212
213         if( !(app = _osrfAppFindApplication(appName)) )
214                 return osrfAppRequestRespondException( ses, 
215                                 reqId, "Application not found: %s", appName );
216         
217         if( !(method = __osrfAppFindMethod( app, methodName )) ) 
218                 return osrfAppRequestRespondException( ses, reqId, 
219                                 "Method [%s] not found for service %s", methodName, appName );
220
221         context.method = method;
222
223         #ifdef OSRF_STRICT_PARAMS
224         if( method->argc > 0 ) {
225                 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
226                         return osrfAppRequestRespondException( ses, reqId, 
227                                 "Not enough params for method %s / service %s", methodName, appName );
228         }
229         #endif
230
231         int retcode = 0;
232
233         if( method->options & OSRF_METHOD_SYSTEM ) {
234                 retcode = __osrfAppRunSystemMethod(&context);
235
236         } else {
237
238                 /* open and now run the method */
239                 *(void **) (&meth) = dlsym(app->handle, method->symbol);
240
241                 if( (error = dlerror()) != NULL ) {
242                         return osrfAppRequestRespondException( ses, reqId, 
243                                 "Unable to execute method [%s]  for service %s", methodName, appName );
244                 }
245
246                 retcode = (*meth) (&context);
247         }
248
249         if(retcode < 0) 
250                 return osrfAppRequestRespondException( 
251                                 ses, reqId, "An unknown server error occurred" );
252
253         return __osrfAppPostProcess( &context, retcode );
254
255 }
256
257
258 int osrfAppRespond( osrfMethodContext* ctx, jsonObject* data ) {
259         return _osrfAppRespond( ctx, data, 0 );
260 }
261
262 int osrfAppRespondComplete( osrfMethodContext* context, jsonObject* data ) {
263         return _osrfAppRespond( context, data, 1 );
264 }
265
266 int _osrfAppRespond( osrfMethodContext* ctx, jsonObject* data, int complete ) {
267         if(!(ctx && ctx->method)) return -1;
268
269         if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
270                 osrfLogDebug( OSRF_LOG_MARK,   
271                         "Adding responses to stash for atomic method %s", ctx->method->name );
272
273                 if( ctx->responses == NULL )                                                                                            
274                         ctx->responses = jsonParseString("[]");                                                 
275
276                 if ( data != NULL )
277                         jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
278         }
279
280
281         if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
282                         !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
283
284                 if(complete) 
285                         osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
286                 else
287                         osrfAppRequestRespond( ctx->session, ctx->request, data );
288                 return 0;
289         }
290
291         return 0;
292 }
293
294
295
296
297 int __osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
298         if(!(ctx && ctx->method)) return -1;
299
300         osrfLogDebug( OSRF_LOG_MARK,  "Postprocessing method %s with retcode %d",
301                         ctx->method->name, retcode );
302
303         if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
304
305                 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
306                 jsonObjectFree(ctx->responses);
307                 ctx->responses = NULL;
308
309         } else {
310
311                 if( retcode > 0 ) 
312                         osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
313                                         "osrfConnectStatus", ctx->request, "Request Complete" );
314         }
315
316         return 0;
317 }
318
319 int osrfAppRequestRespondException( osrfAppSession* ses, int request, char* msg, ... ) {
320         if(!ses) return -1;
321         if(!msg) msg = "";
322         VA_LIST_TO_STRING(msg);
323         osrfLogWarning( OSRF_LOG_MARK,  "Returning method exception with message: %s", VA_BUF );
324         osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request,  VA_BUF );
325         return 0;
326 }
327
328
329 static void __osrfAppSetIntrospectMethod( osrfMethodContext* ctx, osrfMethod* method, jsonObject* resp ) {
330         if(!(ctx && resp)) return;
331
332         jsonObjectSetKey(resp, "api_name",      jsonNewObject(method->name));
333         jsonObjectSetKey(resp, "method",                jsonNewObject(method->symbol));
334         jsonObjectSetKey(resp, "service",       jsonNewObject(ctx->session->remote_service));
335         jsonObjectSetKey(resp, "notes",         jsonNewObject(method->notes));
336         jsonObjectSetKey(resp, "argc",          jsonNewNumberObject(method->argc));
337
338         jsonObjectSetKey(resp, "sysmethod", 
339                         jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
340         jsonObjectSetKey(resp, "atomic",                
341                         jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
342         jsonObjectSetKey(resp, "cachable",      
343                         jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
344
345         jsonObjectSetClass(resp, "method");
346 }
347
348
349
350 int __osrfAppRunSystemMethod(osrfMethodContext* ctx) {
351         OSRF_METHOD_VERIFY_CONTEXT(ctx);
352
353         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
354                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
355
356                 return osrfAppIntrospectAll(ctx);
357         }
358
359
360         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
361                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
362
363                 return osrfAppIntrospect(ctx);
364         }
365
366         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
367                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
368
369                 return osrfAppEcho(ctx);
370         }
371
372
373         osrfAppRequestRespondException( ctx->session, 
374                         ctx->request, "System method implementation not found");
375
376         return 0;
377 }
378
379
380 int osrfAppIntrospect( osrfMethodContext* ctx ) {
381
382         jsonObject* resp = NULL;
383         char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
384         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
385         int len = 0;
386
387         if(!methodSubstring) return 1; /* respond with no methods */
388
389         if(app) {
390
391                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
392                 osrfMethod* method;
393
394                 while( (method = osrfHashIteratorNext(itr)) ) {
395                         if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
396                                 if( !strncmp( method->name, methodSubstring, len) ) {
397                                         resp = jsonNewObject(NULL);
398                                         __osrfAppSetIntrospectMethod( ctx, method, resp );
399                                         osrfAppRespond(ctx, resp);
400                                         jsonObjectFree(resp);
401                                 }
402                         }
403                 }
404                 osrfHashIteratorFree(itr);
405                 return 1;
406         }
407
408         return -1;
409
410 }
411
412
413 int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
414         jsonObject* resp = NULL;
415         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
416
417         if(app) {
418                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
419                 osrfMethod* method;
420                 while( (method = osrfHashIteratorNext(itr)) ) {
421                         resp = jsonNewObject(NULL);
422                         __osrfAppSetIntrospectMethod( ctx, method, resp );
423                         osrfAppRespond(ctx, resp);
424                         jsonObjectFree(resp);
425                 }
426                 osrfHashIteratorFree(itr);
427                 return 1;
428         }
429
430         return -1;
431 }
432
433 int osrfAppEcho( osrfMethodContext* ctx ) {
434         OSRF_METHOD_VERIFY_CONTEXT(ctx);
435         int i;
436         for( i = 0; i < ctx->params->size; i++ ) {
437                 jsonObject* str = jsonObjectGetIndex(ctx->params,i);
438                 osrfAppRespond(ctx, str);
439         }
440         return 1;
441 }
442