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