1 #include <opensrf/osrf_application.h>
4 @file osrf_application.c
5 @brief Routines to manage dynamically loaded libraries.
8 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
9 const char* notes, int argc, int options, void* );
10 static void osrfAppSetOnExit(osrfApplication* app, const char* appName);
11 static int _osrfAppRegisterSysMethods( const char* app );
12 static osrfApplication* _osrfAppFindApplication( const char* name );
13 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName );
14 static int _osrfAppRespond( osrfMethodContext* context, const jsonObject* data, int complete );
15 static int _osrfAppPostProcess( osrfMethodContext* context, int retcode );
16 static int _osrfAppRunSystemMethod(osrfMethodContext* context);
17 static int osrfAppIntrospect( osrfMethodContext* ctx );
18 static int osrfAppIntrospectAll( osrfMethodContext* ctx );
19 static int osrfAppEcho( osrfMethodContext* ctx );
21 static osrfHash* _osrfAppHash = NULL;
23 int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
24 if(!appName || ! soFile) return -1;
28 _osrfAppHash = osrfNewHash();
30 osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
32 osrfApplication* app = safe_malloc(sizeof(osrfApplication));
33 app->handle = dlopen (soFile, RTLD_NOW);
37 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
38 dlerror(); /* clear the error */
43 app->methods = osrfNewHash();
44 osrfHashSet( _osrfAppHash, app, appName );
46 /* see if we can run the initialize method */
48 *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
50 if( (error = dlerror()) != NULL ) {
51 osrfLogWarning( OSRF_LOG_MARK,
52 "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s",
59 if( (ret = (*init)()) ) {
60 osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
61 "'osrfAppInitialize', not registering...", appName );
62 //free(app->name); /* need a method to remove an application from the list */
68 _osrfAppRegisterSysMethods(appName);
70 osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
72 osrfLogSetAppname(appName);
74 osrfAppSetOnExit(app, appName);
80 static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
81 if(!(app && appName)) return;
83 /* see if we can run the initialize method */
85 void (*onExit) (void);
86 *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
88 if( (error = dlerror()) != NULL ) {
89 osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
93 osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
94 app->onExit = (*onExit);
98 int osrfAppRunChildInit(const char* appname) {
99 osrfApplication* app = _osrfAppFindApplication(appname);
104 int (*childInit) (void);
106 *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
108 if( (error = dlerror()) != NULL ) {
109 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
113 if( (ret = (*childInit)()) ) {
114 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
118 osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
123 void osrfAppRunExitCode() {
124 osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
125 osrfApplication* app;
126 while( (app = osrfHashIteratorNext(itr)) ) {
128 osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
129 osrfHashIteratorKey(itr) );
133 osrfHashIteratorFree(itr);
137 int osrfAppRegisterMethod( const char* appName, const char* methodName,
138 const char* symbolName, const char* notes, int argc, int options ) {
140 return osrfAppRegisterExtendedMethod(
152 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName,
153 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
155 if( !appName || ! methodName ) return -1;
157 osrfApplication* app = _osrfAppFindApplication(appName);
159 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
163 osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
165 osrfMethod* method = _osrfAppBuildMethod(
166 methodName, symbolName, notes, argc, options, user_data );
167 method->options = options;
169 /* plug the method into the list of methods */
170 osrfHashSet( app->methods, method, method->name );
172 if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
173 int newops = options | OSRF_METHOD_ATOMIC;
174 osrfMethod* atomicMethod = _osrfAppBuildMethod(
175 methodName, symbolName, notes, argc, newops, NULL );
176 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
177 atomicMethod->userData = method->userData;
185 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
186 const char* notes, int argc, int options, void* user_data ) {
188 osrfMethod* method = safe_malloc(sizeof(osrfMethod));
191 method->name = strdup(methodName);
196 method->symbol = strdup(symbolName);
198 method->symbol = NULL;
201 method->notes = strdup(notes);
203 method->notes = NULL;
206 method->userData = user_data;
209 method->options = options;
211 if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
212 char mb[strlen(method->name) + 8];
213 sprintf(mb, "%s.atomic", method->name);
215 method->name = strdup(mb);
216 method->options |= OSRF_METHOD_STREAMING;
224 Registers all of the system methods for this app so that they may be
225 treated the same as other methods
227 static int _osrfAppRegisterSysMethods( const char* app ) {
229 osrfAppRegisterMethod(
230 app, OSRF_SYSMETHOD_INTROSPECT, NULL,
231 "Return a list of methods whose names have the same initial "
232 "substring as that of the provided method name PARAMS( methodNameSubstring )",
233 1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
235 osrfAppRegisterMethod(
236 app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL,
237 "Returns a complete list of methods. PARAMS()", 0,
238 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
240 osrfAppRegisterMethod(
241 app, OSRF_SYSMETHOD_ECHO, NULL,
242 "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0,
243 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
249 Finds the given app in the list of apps
250 @param name The name of the application
251 @return The application pointer or NULL if there is no such application
253 static osrfApplication* _osrfAppFindApplication( const char* name ) {
254 if(!name) return NULL;
255 return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
259 @brief Find the given method for the given app.
260 @param app The application object.
261 @param methodName The method to find.
262 @return A method pointer or NULL if no such method exists for the given application.
264 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
265 if(!app || ! methodName) return NULL;
266 return (osrfMethod*) osrfHashGet( app->methods, methodName );
269 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
270 if(!appName || ! methodName) return NULL;
271 return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
275 int osrfAppRunMethod( const char* appName, const char* methodName,
276 osrfAppSession* ses, int reqId, jsonObject* params ) {
278 if( !(appName && methodName && ses) ) return -1;
281 osrfApplication* app;
283 osrfMethodContext context;
285 context.session = ses;
286 context.params = params;
287 context.request = reqId;
288 context.responses = NULL;
290 /* this is the method we're gonna run */
291 int (*meth) (osrfMethodContext*);
293 if( !(app = _osrfAppFindApplication(appName)) )
294 return osrfAppRequestRespondException( ses,
295 reqId, "Application not found: %s", appName );
297 if( !(method = osrfAppFindMethod( app, methodName )) )
298 return osrfAppRequestRespondException( ses, reqId,
299 "Method [%s] not found for service %s", methodName, appName );
301 context.method = method;
303 #ifdef OSRF_STRICT_PARAMS
304 if( method->argc > 0 ) {
305 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
306 return osrfAppRequestRespondException( ses, reqId,
307 "Not enough params for method %s / service %s", methodName, appName );
313 if( method->options & OSRF_METHOD_SYSTEM ) {
314 retcode = _osrfAppRunSystemMethod(&context);
318 /* open and now run the method */
319 *(void **) (&meth) = dlsym(app->handle, method->symbol);
321 if( (error = dlerror()) != NULL ) {
322 return osrfAppRequestRespondException( ses, reqId,
323 "Unable to execute method [%s] for service %s", methodName, appName );
326 retcode = (*meth) (&context);
330 return osrfAppRequestRespondException(
331 ses, reqId, "An unknown server error occurred" );
333 return _osrfAppPostProcess( &context, retcode );
338 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
339 return _osrfAppRespond( ctx, data, 0 );
342 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
343 return _osrfAppRespond( context, data, 1 );
346 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
347 if(!(ctx && ctx->method)) return -1;
349 if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
350 osrfLogDebug( OSRF_LOG_MARK,
351 "Adding responses to stash for atomic method %s", ctx->method->name );
353 if( ctx->responses == NULL )
354 ctx->responses = jsonNewObjectType( JSON_ARRAY );
357 jsonObjectPush( ctx->responses, jsonObjectClone(data) );
361 if( !(ctx->method->options & OSRF_METHOD_ATOMIC ) &&
362 !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
365 osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
367 osrfAppRequestRespond( ctx->session, ctx->request, data );
375 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
376 if(!(ctx && ctx->method)) return -1;
378 osrfLogDebug( OSRF_LOG_MARK, "Postprocessing method %s with retcode %d",
379 ctx->method->name, retcode );
381 if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
383 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
384 jsonObjectFree(ctx->responses);
385 ctx->responses = NULL;
390 osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,
391 "osrfConnectStatus", ctx->request, "Request Complete" );
397 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
400 VA_LIST_TO_STRING(msg);
401 osrfLogWarning( OSRF_LOG_MARK, "Returning method exception with message: %s", VA_BUF );
402 osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request, VA_BUF );
407 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method, jsonObject* resp ) {
408 if(!(ctx && resp)) return;
410 jsonObjectSetKey(resp, "api_name", jsonNewObject(method->name));
411 jsonObjectSetKey(resp, "method", jsonNewObject(method->symbol));
412 jsonObjectSetKey(resp, "service", jsonNewObject(ctx->session->remote_service));
413 jsonObjectSetKey(resp, "notes", jsonNewObject(method->notes));
414 jsonObjectSetKey(resp, "argc", jsonNewNumberObject(method->argc));
416 jsonObjectSetKey(resp, "sysmethod",
417 jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
418 jsonObjectSetKey(resp, "atomic",
419 jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
420 jsonObjectSetKey(resp, "cachable",
421 jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
425 Tries to run the requested method as a system method.
426 A system method is a well known method that all
428 @param context The current method context
429 @return 0 if the method is run successfully, return < 0 means
430 the method was not run, return > 0 means the method was run
431 and the application code now needs to send a 'request complete'
434 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
435 if( osrfMethodVerifyContext( ctx ) < 0 ) {
436 osrfLogError( OSRF_LOG_MARK, "_osrfAppRunSystemMethod: Received invalid method context" );
440 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) ||
441 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
443 return osrfAppIntrospectAll(ctx);
447 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
448 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
450 return osrfAppIntrospect(ctx);
453 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
454 !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
456 return osrfAppEcho(ctx);
460 osrfAppRequestRespondException( ctx->session,
461 ctx->request, "System method implementation not found");
467 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
469 jsonObject* resp = NULL;
470 const char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
471 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
474 if(!methodSubstring) return 1; /* respond with no methods */
478 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
481 while( (method = osrfHashIteratorNext(itr)) ) {
482 if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
483 if( !strncmp( method->name, methodSubstring, len) ) {
484 resp = jsonNewObject(NULL);
485 _osrfAppSetIntrospectMethod( ctx, method, resp );
486 osrfAppRespond(ctx, resp);
487 jsonObjectFree(resp);
491 osrfHashIteratorFree(itr);
500 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
501 jsonObject* resp = NULL;
502 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
505 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
507 while( (method = osrfHashIteratorNext(itr)) ) {
508 resp = jsonNewObject(NULL);
509 _osrfAppSetIntrospectMethod( ctx, method, resp );
510 osrfAppRespond(ctx, resp);
511 jsonObjectFree(resp);
513 osrfHashIteratorFree(itr);
520 static int osrfAppEcho( osrfMethodContext* ctx ) {
521 if( osrfMethodVerifyContext( ctx ) < 0 ) {
522 osrfLogError( OSRF_LOG_MARK, "osrfAppEcho: Received invalid method context" );
527 for( i = 0; i < ctx->params->size; i++ ) {
528 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
529 osrfAppRespond(ctx, str);
535 Determine whether the context looks healthy.
536 Return 0 if it does, or -1 if it doesn't.
538 int osrfMethodVerifyContext( osrfMethodContext* ctx )
541 osrfLogError( OSRF_LOG_MARK, "Context is NULL in app request" );
545 if( !ctx->session ) {
546 osrfLogError( OSRF_LOG_MARK, "Session is NULL in app request" );
552 osrfLogError( OSRF_LOG_MARK, "Method is NULL in app request" );
556 if( ctx->method->argc ) {
558 osrfLogError( OSRF_LOG_MARK,
559 "Params is NULL in app request %s", ctx->method->name );
562 if( ctx->params->type != JSON_ARRAY ) {
563 osrfLogError( OSRF_LOG_MARK,
564 "'params' is not a JSON array for method %s", ctx->method->name );
569 if( !ctx->method->name ) {
570 osrfLogError( OSRF_LOG_MARK, "Method name is NULL" );
574 // Log the call, with the method and parameters
575 char* params_str = jsonObjectToJSON( ctx->params );
577 osrfLogInfo( OSRF_LOG_MARK, "CALL:\t%s %s - %s",
578 ctx->session->remote_service, ctx->method->name, params_str );