1 #include <opensrf/osrf_application.h>
4 @file osrf_application.c
5 @brief Load and manage shared object libraries.
7 Maintain a registry of applications, using an osrfHash keyed on application name,
9 For each application, load a shared object library so that we can call
10 application-specific functions dynamically. In order to map method names to the
11 corresponding functions (i.e. symbol names in the library), maintain a registry of
12 methods, using an osrfHash keyed on method name.
15 // The following macro is commented out because it ia no longer used.
17 // Used internally to make sure the method description provided is OK
19 #define OSRF_METHOD_VERIFY_DESCRIPTION(app, d) \
23 osrfLogError( OSRF_LOG_MARK, "No method name provided in description" ), \
27 osrfLogError( OSRF_LOG_MARK, "No method symbol provided in description" ), \
39 @name Well known method names
40 @brief These methods are automatically implemented for every application.
43 #define OSRF_SYSMETHOD_INTROSPECT "opensrf.system.method"
44 #define OSRF_SYSMETHOD_INTROSPECT_ATOMIC "opensrf.system.method.atomic"
45 #define OSRF_SYSMETHOD_INTROSPECT_ALL "opensrf.system.method.all"
46 #define OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC "opensrf.system.method.all.atomic"
47 #define OSRF_SYSMETHOD_ECHO "opensrf.system.echo"
48 #define OSRF_SYSMETHOD_ECHO_ATOMIC "opensrf.system.echo.atomic"
52 @brief Represent an Application.
55 void* handle; /**< Handle to the shared object library. */
56 osrfHash* methods; /**< Registry of method names. */
57 void (*onExit) (void); /**< Exit handler for the application. */
60 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
61 const char* notes, int argc, int options, void* );
62 static void osrfAppSetOnExit(osrfApplication* app, const char* appName);
63 static void _osrfAppRegisterSysMethods( const char* app );
64 static inline osrfApplication* _osrfAppFindApplication( const char* name );
65 static inline osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName );
66 static int _osrfAppRespond( osrfMethodContext* context, const jsonObject* data, int complete );
67 static int _osrfAppPostProcess( osrfMethodContext* context, int retcode );
68 static int _osrfAppRunSystemMethod(osrfMethodContext* context);
69 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method,
71 static int osrfAppIntrospect( osrfMethodContext* ctx );
72 static int osrfAppIntrospectAll( osrfMethodContext* ctx );
73 static int osrfAppEcho( osrfMethodContext* ctx );
76 @brief Registry of applications.
78 The key of the hash is the application name, and the associated data is an osrfApplication.
80 static osrfHash* _osrfAppHash = NULL;
83 @brief Register an application.
84 @param appName Name of the application.
85 @param soFile Name of the shared object file to be loaded for this application.
86 @return Zero if successful, or -1 upon error.
88 Open the shared object file and call its osrfAppInitialize() function, if it has one.
89 Register the standard system methods for it. Arrange for the application name to
90 appear in subsequent log messages.
92 int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
93 if( !appName || ! soFile ) return -1;
96 osrfLogSetAppname( appName );
99 _osrfAppHash = osrfNewHash();
101 osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
103 void* handle = dlopen( soFile, RTLD_NOW );
105 const char* msg = dlerror();
106 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, msg );
110 osrfApplication* app = safe_malloc(sizeof(osrfApplication));
111 app->handle = handle;
113 app->methods = osrfNewHash();
114 osrfHashSet( _osrfAppHash, app, appName );
116 /* see if we can run the initialize method */
118 *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
120 if( (error = dlerror()) != NULL ) {
121 osrfLogWarning( OSRF_LOG_MARK,
122 "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s",
129 if( (ret = (*init)()) ) {
130 osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
131 "'osrfAppInitialize', not registering...", appName );
132 //free(app->name); /* need a method to remove an application from the list */
138 _osrfAppRegisterSysMethods(appName);
140 osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
142 osrfAppSetOnExit(app, appName);
148 @brief Save a pointer to the application's exit function.
149 @param app Pointer to the osrfApplication.
150 @param appName Application name (used only for log messages).
152 Look in the shared object for a symbol named "osrfAppChildExit". If you find one, save
153 it as a pointer to the application's exit function. If present, this function will be
154 called when a server's child process (a so-called "drone") is shutting down.
156 static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
157 if(!(app && appName)) return;
159 /* see if we can run the initialize method */
161 void (*onExit) (void);
162 *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
164 if( (error = dlerror()) != NULL ) {
165 osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
169 osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
170 app->onExit = (*onExit);
174 @brief Run the application-specific child initialization function for a given application.
175 @param appname Name of the application.
176 @return Zero if successful, or if the application has no child initialization function; -1
177 if the application is not registered, or if the function returns non-zero.
179 The child initialization function must be named "osrfAppChildInit" within the shared
180 object library. It initializes a drone process of a server.
182 int osrfAppRunChildInit(const char* appname) {
183 osrfApplication* app = _osrfAppFindApplication(appname);
188 int (*childInit) (void);
190 *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
192 if( (error = dlerror()) != NULL ) {
193 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
197 if( (ret = (*childInit)()) ) {
198 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
202 osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
207 @brief Call the exit handler for every application that has one.
209 Normally a server's child process (a so-called "drone") calls this function just before
212 void osrfAppRunExitCode( void ) {
213 osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
214 osrfApplication* app;
215 while( (app = osrfHashIteratorNext(itr)) ) {
217 osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
218 osrfHashIteratorKey(itr) );
222 osrfHashIteratorFree(itr);
226 @brief Register a method for a specified application.
228 @param appName Name of the application that implements the method.
229 @param methodName The fully qualified name of the method.
230 @param symbolName The symbol name (function name) that implements the method.
231 @param notes Public documentation for this method.
232 @param argc The minimum number of arguments for the function.
233 @param options Bit switches setting various options.
234 @return Zero on success, or -1 on error.
236 Registering a method enables us to call the right function when a client requests a
239 The @a options parameter is zero or more of the following macros, OR'd together:
242 - OSRF_METHOD_STREAMING
244 - OSRF_METHOD_CACHABLE
246 If the OSRF_METHOD_STREAMING bit is set, also register an ".atomic" version of
249 int osrfAppRegisterMethod( const char* appName, const char* methodName,
250 const char* symbolName, const char* notes, int argc, int options ) {
252 return osrfAppRegisterExtendedMethod(
264 @brief Register a method for a specified application.
266 @param appName Name of the application that implements the method.
267 @param methodName The fully qualified name of the method.
268 @param symbolName The symbol name (function name) that implements the method.
269 @param notes Public documentation for this method.
270 @param argc How many arguments this method expects.
271 @param options Bit switches setting various options.
272 @param user_data Opaque pointer to be passed to the dynamically called function.
273 @return Zero on success, or -1 on error.
275 This function is identical to osrfAppRegisterMethod(), except that it also installs
276 a method-specific opaque pointer. When we call the corresponding function at
277 run time, this pointer will be available to the function via the method context.
279 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName,
280 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
282 if( !appName || ! methodName ) return -1;
284 osrfApplication* app = _osrfAppFindApplication(appName);
286 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
290 osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
292 osrfMethod* method = _osrfAppBuildMethod(
293 methodName, symbolName, notes, argc, options, user_data );
294 method->options = options;
296 /* plug the method into the list of methods */
297 osrfHashSet( app->methods, method, method->name );
299 if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
300 int newops = options | OSRF_METHOD_ATOMIC;
301 osrfMethod* atomicMethod = _osrfAppBuildMethod(
302 methodName, symbolName, notes, argc, newops, user_data );
303 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
310 @brief Allocate and populate an osrfMethod.
311 @param methodName Name of the method.
312 @param symbolName Name of the function that implements the method.
313 @param notes Remarks documenting the method.
314 @param argc Minimum number of arguments to the method.
315 @param options Bit switches setting various options.
316 @param user_data An opaque pointer to be passed in the method context.
317 @return Pointer to the newly allocated osrfMethod.
319 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
320 const char* notes, int argc, int options, void* user_data ) {
322 osrfMethod* method = safe_malloc(sizeof(osrfMethod));
325 methodName = ""; // should never happen
327 if( options & OSRF_METHOD_ATOMIC ) {
328 // Append ".atomic" to the name, and make the method streaming
329 char mb[ strlen( methodName ) + 8 ];
330 sprintf( mb, "%s.atomic", methodName );
331 method->name = strdup( mb );
332 options |= OSRF_METHOD_STREAMING;
334 method->name = strdup(methodName);
338 method->symbol = strdup(symbolName);
340 method->symbol = NULL;
343 method->notes = strdup(notes);
345 method->notes = NULL;
348 method->options = options;
351 method->userData = user_data;
357 @brief Register all of the system methods for this application.
358 @param app Application name.
360 A client can call these methods the same way it calls application-specific methods,
361 but they are implemented by functions here in this module, not by functions in the
364 static void _osrfAppRegisterSysMethods( const char* app ) {
366 osrfAppRegisterMethod(
367 app, OSRF_SYSMETHOD_INTROSPECT, NULL,
368 "Return a list of methods whose names have the same initial "
369 "substring as that of the provided method name PARAMS( methodNameSubstring )",
370 1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
372 osrfAppRegisterMethod(
373 app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL,
374 "Returns a complete list of methods. PARAMS()", 0,
375 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
377 osrfAppRegisterMethod(
378 app, OSRF_SYSMETHOD_ECHO, NULL,
379 "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0,
380 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
384 @brief Look up an application by name in the application registry.
385 @param name The name of the application.
386 @return Pointer to the corresponding osrfApplication if found, or NULL if not.
388 static inline osrfApplication* _osrfAppFindApplication( const char* name ) {
389 return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
393 @brief Look up a method by name for a given application.
394 @param app Pointer to the osrfApplication that owns the method.
395 @param methodName Name of the method to find.
396 @return Pointer to the corresponding osrfMethod if found, or NULL if not.
398 static inline osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
399 if( !app ) return NULL;
400 return (osrfMethod*) osrfHashGet( app->methods, methodName );
404 @brief Look up a method by name for an application with a given name.
405 @param appName Name of the osrfApplication.
406 @param methodName Name of the method to find.
407 @return Pointer to the corresponding osrfMethod if found, or NULL if not.
409 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
410 if( !appName ) return NULL;
411 return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
415 @brief Call the function that implements a specified method.
416 @param appName Name of the application.
417 @param methodName Name of the method.
418 @param ses Pointer to the current application session.
419 @param reqId The request id of the request invoking the method.
420 @param params Pointer to a jsonObject encoding the parameters to the method.
421 @return Zero if successful, or -1 upon failure.
423 If we can't find a function corresponding to the method, or if we call it and it returns
424 a negative return code, send a STATUS message to the client to report an exception.
426 A return code of -1 means that the @a appName, @a methodName, or @a ses parameter was NULL.
428 int osrfAppRunMethod( const char* appName, const char* methodName,
429 osrfAppSession* ses, int reqId, jsonObject* params ) {
431 if( !(appName && methodName && ses) ) return -1;
433 // Find the application, and then find the method for it
434 osrfApplication* app = _osrfAppFindApplication(appName);
436 return osrfAppRequestRespondException( ses,
437 reqId, "Application not found: %s", appName );
439 osrfMethod* method = osrfAppFindMethod( app, methodName );
441 return osrfAppRequestRespondException( ses, reqId,
442 "Method [%s] not found for service %s", methodName, appName );
444 #ifdef OSRF_STRICT_PARAMS
445 if( method->argc > 0 ) {
446 // Make sure that the client has passed at least the minimum number of arguments.
447 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
448 return osrfAppRequestRespondException( ses, reqId,
449 "Not enough params for method %s / service %s", methodName, appName );
453 // Build an osrfMethodContext, which we will pass by pointer to the function.
454 osrfMethodContext context;
456 context.session = ses;
457 context.method = method;
458 context.params = params;
459 context.request = reqId;
460 context.responses = NULL;
464 if( method->options & OSRF_METHOD_SYSTEM ) {
465 retcode = _osrfAppRunSystemMethod(&context);
469 // Function pointer through which we will call the function dynamically
470 int (*meth) (osrfMethodContext*);
472 // Open the function that implements the method
473 meth = dlsym(app->handle, method->symbol);
475 const char* error = dlerror();
476 if( error != NULL ) {
477 return osrfAppRequestRespondException( ses, reqId,
478 "Unable to execute method [%s] for service %s", methodName, appName );
482 retcode = meth( &context );
486 return osrfAppRequestRespondException(
487 ses, reqId, "An unknown server error occurred" );
489 return _osrfAppPostProcess( &context, retcode );
493 @brief Either send or enqueue a response to a client.
494 @param ctx Pointer to the current method context.
495 @param data Pointer to the response, in the form of a jsonObject.
496 @return Zero if successful, or -1 upon error. The only recognized errors are if either
497 the @a context pointer or its method pointer is NULL.
499 For an atomic method, add a copy of the response data to a cache within the method
500 context, to be sent later. Otherwise, send a RESULT message to the client, with the
503 Note that, for an atomic method, this function is equivalent to osrfAppRespondComplete():
504 we send the STATUS message after the method returns, and not before.
506 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
507 return _osrfAppRespond( ctx, data, 0 );
511 @brief Either send or enqueue a response to a client, with a completion notice.
512 @param context Pointer to the current method context.
513 @param data Pointer to the response, in the form of a jsonObject.
514 @return Zero if successful, or -1 upon error. The only recognized errors are if either
515 the @a context pointer or its method pointer is NULL.
517 For an atomic method, add a copy of the response data to a cache within the method
518 context, to be sent later. Otherwise, send a RESULT message to the client, with the
519 results in @a data. Also send a STATUS message to indicate that the response is complete.
521 Note that, for an atomic method, this function is equivalent to osrfAppRespond(): we
522 send the STATUS message after the method returns, and not before.
524 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
525 return _osrfAppRespond( context, data, 1 );
529 @brief Either send or enqueue a response to a client, optionally with a completion notice.
530 @param ctx Pointer to the method context.
531 @param data Pointer to the response, in the form of a jsonObject.
532 @param complete Boolean: if true, we will accompany the RESULT message with a STATUS
533 message indicating that the response is complete.
534 @return Zero if successful, or -1 upon error. The only recognized errors are if either
535 the @a ctx pointer or its method pointer is NULL.
537 For an atomic method, add a copy of the response data to a cache within the method
538 context, to be sent later. In this case the @a complete parameter has no effect,
539 because we'll send the STATUS message later when we send the cached results.
541 If the method is cachable but not atomic, do nothing, ignoring the results in @a data.
542 Apparently there are no cachable methods at this writing. If we ever invent some, we
543 may need to revisit this function.
545 If the method is neither atomic nor cachable, then send a RESULT message to the client,
546 with the results in @a data. If @a complete is true, also send a STATUS message to
547 indicate that the response is complete.
549 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
550 if(!(ctx && ctx->method)) return -1;
552 if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
553 osrfLogDebug( OSRF_LOG_MARK,
554 "Adding responses to stash for atomic method %s", ctx->method->name );
556 // If we don't already have one, create a JSON_ARRAY to serve as a cache.
557 if( ctx->responses == NULL )
558 ctx->responses = jsonNewObjectType( JSON_ARRAY );
560 // Add a copy of the data object to the cache.
562 jsonObjectPush( ctx->responses, jsonObjectClone(data) );
565 if( !(ctx->method->options & OSRF_METHOD_ATOMIC ) &&
566 !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
569 osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
571 osrfAppRequestRespond( ctx->session, ctx->request, data );
578 @brief Finish up the processing of a request.
579 @param ctx Pointer to the method context.
580 @param retcode The return code from the method's function.
581 @return 0 if successfull, or -1 upon error.
583 For an atomic method: send whatever responses we have been saving up, together with a
584 STATUS message to say that we're finished.
586 For a non-atomic method: if the return code from the method is greater than zero, just
587 send the STATUS message. If the return code is zero, do nothing; the method presumably
588 sent the STATUS message on its own.
590 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
591 if(!(ctx && ctx->method)) return -1;
593 osrfLogDebug( OSRF_LOG_MARK, "Postprocessing method %s with retcode %d",
594 ctx->method->name, retcode );
597 // We have cached responses to return, collected in a JSON ARRAY (we haven't sent
598 // any responses yet). Now send them all at once, followed by a STATUS message
599 // to say that we're finished.
600 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
601 jsonObjectFree(ctx->responses);
602 ctx->responses = NULL;
605 // We have no cached responses to return.
607 // Send a STATUS message to say that we're finished.
608 osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,
609 "osrfConnectStatus", ctx->request, "Request Complete" );
616 @brief Send a STATUS message to the client, notifying it of an error.
617 @param ses Pointer to the current application session.
618 @param request Request ID of the request.
619 @param msg A printf-style format string defining an explanatory message to be sent to
620 the client. Subsequent parameters, if any, will be formatted and inserted into the
621 resulting output string.
622 @return -1 if the @a ses parameter is NULL; otherwise zero.
624 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
627 VA_LIST_TO_STRING(msg);
628 osrfLogWarning( OSRF_LOG_MARK, "Returning method exception with message: %s", VA_BUF );
629 osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request, VA_BUF );
634 @brief Introspect a specified method.
635 @param ctx Pointer to the method context.
636 @param method Pointer to the osrfMethod for the specified method.
637 @param resp Pointer to the jsonObject into which method information will be placed.
639 Treating the @a resp object as a JSON_HASH, insert entries for various bits of information
640 about the specified method.
642 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method,
644 if(!(ctx && resp)) return;
646 jsonObjectSetKey(resp, "api_name", jsonNewObject(method->name));
647 jsonObjectSetKey(resp, "method", jsonNewObject(method->symbol));
648 jsonObjectSetKey(resp, "service", jsonNewObject(ctx->session->remote_service));
649 jsonObjectSetKey(resp, "notes", jsonNewObject(method->notes));
650 jsonObjectSetKey(resp, "argc", jsonNewNumberObject(method->argc));
652 jsonObjectSetKey(resp, "sysmethod",
653 jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
654 jsonObjectSetKey(resp, "atomic",
655 jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
656 jsonObjectSetKey(resp, "cachable",
657 jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
661 @brief Run the requested system method.
662 @param ctx The method context.
663 @return Zero if the method is run successfully; -1 if the method was not run; 1 if the
664 method was run and the application code now needs to send a 'request complete' message.
666 A system method is a well known method implemented here for all servers. Instead of
667 looking in the shared object, branch on the method name and call the corresponding
670 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
671 if( osrfMethodVerifyContext( ctx ) < 0 ) {
672 osrfLogError( OSRF_LOG_MARK, "_osrfAppRunSystemMethod: Received invalid method context" );
676 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) ||
677 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
678 return osrfAppIntrospectAll(ctx);
681 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
682 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
683 return osrfAppIntrospect(ctx);
686 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
687 !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
688 return osrfAppEcho(ctx);
691 osrfAppRequestRespondException( ctx->session,
692 ctx->request, "System method implementation not found");
698 @brief Run the introspect method for a specified method or group of methods.
699 @param ctx Pointer to the method context.
700 @return 1 if successful, or if no search target is specified as a parameter; -1 if unable
701 to find a pointer to the application.
703 Traverse the list of methods, and report on each one whose name starts with the specified
704 search target. In effect, the search target ends with an implicit wild card.
706 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
708 // Get the name of the method to introspect
709 const char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
710 if( !methodSubstring )
711 return 1; /* respond with no methods */
713 // Get a pointer to the application
714 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
716 return -1; // Oops, no application...
719 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
722 while( (method = osrfHashIteratorNext(itr)) ) {
723 if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
724 if( !strncmp( method->name, methodSubstring, len) ) {
725 jsonObject* resp = jsonNewObject(NULL);
726 _osrfAppSetIntrospectMethod( ctx, method, resp );
727 osrfAppRespond(ctx, resp);
728 jsonObjectFree(resp);
733 osrfHashIteratorFree(itr);
738 @brief Run the implement_all method.
739 @param ctx Pointer to the method context.
740 @return 1 if successful, or -1 if unable to find a pointer to the application.
742 Report on all of the methods of the application.
744 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
745 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
748 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
750 while( (method = osrfHashIteratorNext(itr)) ) {
751 jsonObject* resp = jsonNewObject(NULL);
752 _osrfAppSetIntrospectMethod( ctx, method, resp );
753 osrfAppRespond(ctx, resp);
754 jsonObjectFree(resp);
756 osrfHashIteratorFree(itr);
763 @brief Run the echo method.
764 @param ctx Pointer to the method context.
765 @return -1 if the method context is invalid or corrupted; otherwise 1.
767 Send the client a copy of each parameter.
769 static int osrfAppEcho( osrfMethodContext* ctx ) {
770 if( osrfMethodVerifyContext( ctx ) < 0 ) {
771 osrfLogError( OSRF_LOG_MARK, "osrfAppEcho: Received invalid method context" );
776 for( i = 0; i < ctx->params->size; i++ ) {
777 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
778 osrfAppRespond(ctx, str);
784 @brief Perform a series of sanity tests on an osrfMethodContext.
785 @param ctx Pointer to the osrfMethodContext to be checked.
786 @return Zero if the osrfMethodContext passes all tests, or -1 if it doesn't.
788 int osrfMethodVerifyContext( osrfMethodContext* ctx )
791 osrfLogError( OSRF_LOG_MARK, "Context is NULL in app request" );
795 if( !ctx->session ) {
796 osrfLogError( OSRF_LOG_MARK, "Session is NULL in app request" );
802 osrfLogError( OSRF_LOG_MARK, "Method is NULL in app request" );
806 if( ctx->method->argc ) {
808 osrfLogError( OSRF_LOG_MARK,
809 "Params is NULL in app request %s", ctx->method->name );
812 if( ctx->params->type != JSON_ARRAY ) {
813 osrfLogError( OSRF_LOG_MARK,
814 "'params' is not a JSON array for method %s", ctx->method->name );
819 if( !ctx->method->name ) {
820 osrfLogError( OSRF_LOG_MARK, "Method name is NULL" );
824 // Log the call, with the method and parameters
825 char* params_str = jsonObjectToJSON( ctx->params );
827 osrfLogInfo( OSRF_LOG_MARK, "CALL:\t%s %s - %s",
828 ctx->session->remote_service, ctx->method->name, params_str );