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