1 #include <opensrf/osrf_application.h>
3 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
4 const char* notes, int argc, int options, void* );
5 static void osrfAppSetOnExit(osrfApplication* app, const char* appName);
6 static int _osrfAppRegisterSysMethods( const char* app );
7 static osrfApplication* _osrfAppFindApplication( const char* name );
8 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName );
9 static int _osrfAppRespond( osrfMethodContext* context, const jsonObject* data, int complete );
10 static int _osrfAppPostProcess( osrfMethodContext* context, int retcode );
11 static int _osrfAppRunSystemMethod(osrfMethodContext* context);
12 static int osrfAppIntrospect( osrfMethodContext* ctx );
13 static int osrfAppIntrospectAll( osrfMethodContext* ctx );
14 static int osrfAppEcho( osrfMethodContext* ctx );
16 static osrfHash* _osrfAppHash = NULL;
18 int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
19 if(!appName || ! soFile) return -1;
22 if(!_osrfAppHash) _osrfAppHash = osrfNewHash();
24 osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
26 osrfApplication* app = safe_malloc(sizeof(osrfApplication));
27 app->handle = dlopen (soFile, RTLD_NOW);
31 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
32 dlerror(); /* clear the error */
37 app->methods = osrfNewHash();
38 osrfHashSet( _osrfAppHash, app, appName );
40 /* see if we can run the initialize method */
42 *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
44 if( (error = dlerror()) != NULL ) {
45 osrfLogWarning( OSRF_LOG_MARK,
46 "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
52 if( (ret = (*init)()) ) {
53 osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
54 "'osrfAppInitialize', not registering...", appName );
55 //free(app->name); /* need a method to remove an application from the list */
61 _osrfAppRegisterSysMethods(appName);
63 osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
65 osrfLogSetAppname(appName);
67 osrfAppSetOnExit(app, appName);
73 static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
74 if(!(app && appName)) return;
76 /* see if we can run the initialize method */
78 void (*onExit) (void);
79 *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
81 if( (error = dlerror()) != NULL ) {
82 osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
86 osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
87 app->onExit = (*onExit);
91 int osrfAppRunChildInit(const char* appname) {
92 osrfApplication* app = _osrfAppFindApplication(appname);
97 int (*childInit) (void);
99 *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
101 if( (error = dlerror()) != NULL ) {
102 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
106 if( (ret = (*childInit)()) ) {
107 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
111 osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
116 void osrfAppRunExitCode() {
117 osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
118 osrfApplication* app;
119 while( (app = osrfHashIteratorNext(itr)) ) {
121 osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
122 osrfHashIteratorKey(itr) );
126 osrfHashIteratorFree(itr);
130 int osrfAppRegisterMethod( const char* appName, const char* methodName,
131 const char* symbolName, const char* notes, int argc, int options ) {
133 return osrfAppRegisterExtendedMethod(
145 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName,
146 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
148 if( !appName || ! methodName ) return -1;
150 osrfApplication* app = _osrfAppFindApplication(appName);
152 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
156 osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
158 osrfMethod* method = _osrfAppBuildMethod(
159 methodName, symbolName, notes, argc, options, user_data );
160 method->options = options;
162 /* plug the method into the list of methods */
163 osrfHashSet( app->methods, method, method->name );
165 if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
166 int newops = options | OSRF_METHOD_ATOMIC;
167 osrfMethod* atomicMethod = _osrfAppBuildMethod(
168 methodName, symbolName, notes, argc, newops, NULL );
169 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
170 atomicMethod->userData = method->userData;
178 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
179 const char* notes, int argc, int options, void* user_data ) {
181 osrfMethod* method = safe_malloc(sizeof(osrfMethod));
183 if(methodName) method->name = strdup(methodName);
184 else method->name = NULL;
185 if(symbolName) method->symbol = strdup(symbolName);
186 else method->symbol = NULL;
187 if(notes) method->notes = strdup(notes);
188 else method->notes = NULL;
189 if(user_data) method->userData = user_data;
192 method->options = options;
194 if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
195 char mb[strlen(method->name) + 8];
196 sprintf(mb, "%s.atomic", method->name);
198 method->name = strdup(mb);
199 method->options |= OSRF_METHOD_STREAMING;
207 Registers all of the system methods for this app so that they may be
208 treated the same as other methods */
209 static int _osrfAppRegisterSysMethods( const char* app ) {
211 osrfAppRegisterMethod(
212 app, OSRF_SYSMETHOD_INTROSPECT, NULL,
213 "Return a list of methods whose names have the same initial "
214 "substring as that of the provided method name PARAMS( methodNameSubstring )",
215 1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
217 osrfAppRegisterMethod(
218 app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL,
219 "Returns a complete list of methods. PARAMS()", 0,
220 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
222 osrfAppRegisterMethod(
223 app, OSRF_SYSMETHOD_ECHO, NULL,
224 "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0,
225 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
231 Finds the given app in the list of apps
232 @param name The name of the application
233 @return The application pointer or NULL if there is no such application
235 static osrfApplication* _osrfAppFindApplication( const char* name ) {
236 if(!name) return NULL;
237 return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
241 Finds the given method for the given app
242 @param app The application object
243 @param methodName The method to find
244 @return A method pointer or NULL if no such method
245 exists for the given application
247 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
248 if(!app || ! methodName) return NULL;
249 return (osrfMethod*) osrfHashGet( app->methods, methodName );
252 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
253 if(!appName || ! methodName) return NULL;
254 return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
258 int osrfAppRunMethod( const char* appName, const char* methodName,
259 osrfAppSession* ses, int reqId, jsonObject* params ) {
261 if( !(appName && methodName && ses) ) return -1;
264 osrfApplication* app;
266 osrfMethodContext context;
268 context.session = ses;
269 context.params = params;
270 context.request = reqId;
271 context.responses = NULL;
273 /* this is the method we're gonna run */
274 int (*meth) (osrfMethodContext*);
276 if( !(app = _osrfAppFindApplication(appName)) )
277 return osrfAppRequestRespondException( ses,
278 reqId, "Application not found: %s", appName );
280 if( !(method = osrfAppFindMethod( app, methodName )) )
281 return osrfAppRequestRespondException( ses, reqId,
282 "Method [%s] not found for service %s", methodName, appName );
284 context.method = method;
286 #ifdef OSRF_STRICT_PARAMS
287 if( method->argc > 0 ) {
288 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
289 return osrfAppRequestRespondException( ses, reqId,
290 "Not enough params for method %s / service %s", methodName, appName );
296 if( method->options & OSRF_METHOD_SYSTEM ) {
297 retcode = _osrfAppRunSystemMethod(&context);
301 /* open and now run the method */
302 *(void **) (&meth) = dlsym(app->handle, method->symbol);
304 if( (error = dlerror()) != NULL ) {
305 return osrfAppRequestRespondException( ses, reqId,
306 "Unable to execute method [%s] for service %s", methodName, appName );
309 retcode = (*meth) (&context);
313 return osrfAppRequestRespondException(
314 ses, reqId, "An unknown server error occurred" );
316 return _osrfAppPostProcess( &context, retcode );
321 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
322 return _osrfAppRespond( ctx, data, 0 );
325 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
326 return _osrfAppRespond( context, data, 1 );
329 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
330 if(!(ctx && ctx->method)) return -1;
332 if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
333 osrfLogDebug( OSRF_LOG_MARK,
334 "Adding responses to stash for atomic method %s", ctx->method->name );
336 if( ctx->responses == NULL )
337 ctx->responses = jsonParseString("[]");
340 jsonObjectPush( ctx->responses, jsonObjectClone(data) );
344 if( !(ctx->method->options & OSRF_METHOD_ATOMIC) &&
345 !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
348 osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
350 osrfAppRequestRespond( ctx->session, ctx->request, data );
360 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
361 if(!(ctx && ctx->method)) return -1;
363 osrfLogDebug( OSRF_LOG_MARK, "Postprocessing method %s with retcode %d",
364 ctx->method->name, retcode );
366 if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
368 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
369 jsonObjectFree(ctx->responses);
370 ctx->responses = NULL;
375 osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,
376 "osrfConnectStatus", ctx->request, "Request Complete" );
382 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
385 VA_LIST_TO_STRING(msg);
386 osrfLogWarning( OSRF_LOG_MARK, "Returning method exception with message: %s", VA_BUF );
387 osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request, VA_BUF );
392 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method, jsonObject* resp ) {
393 if(!(ctx && resp)) return;
395 jsonObjectSetKey(resp, "api_name", jsonNewObject(method->name));
396 jsonObjectSetKey(resp, "method", jsonNewObject(method->symbol));
397 jsonObjectSetKey(resp, "service", jsonNewObject(ctx->session->remote_service));
398 jsonObjectSetKey(resp, "notes", jsonNewObject(method->notes));
399 jsonObjectSetKey(resp, "argc", jsonNewNumberObject(method->argc));
401 jsonObjectSetKey(resp, "sysmethod",
402 jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
403 jsonObjectSetKey(resp, "atomic",
404 jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
405 jsonObjectSetKey(resp, "cachable",
406 jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
408 jsonObjectSetClass(resp, "method");
412 Trys to run the requested method as a system method.
413 A system method is a well known method that all
415 @param context The current method context
416 @return 0 if the method is run successfully, return < 0 means
417 the method was not run, return > 0 means the method was run
418 and the application code now needs to send a 'request complete'
421 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
422 if( osrfMethodVerifyContext( ctx ) < 0 ) {
423 osrfLogError( OSRF_LOG_MARK, "_osrfAppRunSystemMethod: Received invalid method context" );
427 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) ||
428 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
430 return osrfAppIntrospectAll(ctx);
434 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
435 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
437 return osrfAppIntrospect(ctx);
440 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
441 !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
443 return osrfAppEcho(ctx);
447 osrfAppRequestRespondException( ctx->session,
448 ctx->request, "System method implementation not found");
454 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
456 jsonObject* resp = NULL;
457 char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
458 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
461 if(!methodSubstring) return 1; /* respond with no methods */
465 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
468 while( (method = osrfHashIteratorNext(itr)) ) {
469 if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
470 if( !strncmp( method->name, methodSubstring, len) ) {
471 resp = jsonNewObject(NULL);
472 _osrfAppSetIntrospectMethod( ctx, method, resp );
473 osrfAppRespond(ctx, resp);
474 jsonObjectFree(resp);
478 osrfHashIteratorFree(itr);
487 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
488 jsonObject* resp = NULL;
489 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
492 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
494 while( (method = osrfHashIteratorNext(itr)) ) {
495 resp = jsonNewObject(NULL);
496 _osrfAppSetIntrospectMethod( ctx, method, resp );
497 osrfAppRespond(ctx, resp);
498 jsonObjectFree(resp);
500 osrfHashIteratorFree(itr);
507 static int osrfAppEcho( osrfMethodContext* ctx ) {
508 if( osrfMethodVerifyContext( ctx ) < 0 ) {
509 osrfLogError( OSRF_LOG_MARK, "osrfAppEcho: Received invalid method context" );
514 for( i = 0; i < ctx->params->size; i++ ) {
515 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
516 osrfAppRespond(ctx, str);
522 Determine whether the context looks healthy.
523 Return 0 if it does, or -1 if it doesn't.
525 int osrfMethodVerifyContext( osrfMethodContext* ctx )
528 osrfLogError( OSRF_LOG_MARK, "Context is NULL in app request" );
532 if( !ctx->session ) {
533 osrfLogError( OSRF_LOG_MARK, "Session is NULL in app request" );
539 osrfLogError( OSRF_LOG_MARK, "Method is NULL in app request" );
543 if( ctx->method->argc ) {
545 osrfLogError( OSRF_LOG_MARK,
546 "Params is NULL in app request %s", ctx->method->name );
549 if( ctx->params->type != JSON_ARRAY ) {
550 osrfLogError( OSRF_LOG_MARK,
551 "'params' is not a JSON array for method %s", ctx->method->name );
556 if( !ctx->method->name ) {
557 osrfLogError( OSRF_LOG_MARK, "Method name is NULL" );
561 // Log the call, with the method and parameters
562 char* params_str = jsonObjectToJSON( ctx->params );
564 osrfLogInfo( OSRF_LOG_MARK, "CALL: %s %s - %s",
565 ctx->session->remote_service, ctx->method->name, params_str );