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"
51 #define OSRF_MSG_BUFFER_SIZE 10240
54 @brief Represent an Application.
57 void* handle; /**< Handle to the shared object library. */
58 osrfHash* methods; /**< Registry of method names. */
59 void (*onExit) (void); /**< Exit handler for the application. */
62 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
63 const char* notes, int argc, int options, void* );
64 static void osrfAppSetOnExit(osrfApplication* app, const char* appName);
65 static void _osrfAppRegisterSysMethods( const char* app );
66 static inline osrfApplication* _osrfAppFindApplication( const char* name );
67 static inline osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName );
68 static int _osrfAppRespond( osrfMethodContext* context, const jsonObject* data, int complete );
69 static int _osrfAppPostProcess( osrfMethodContext* context, int retcode );
70 static int _osrfAppRunSystemMethod(osrfMethodContext* context);
71 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method,
73 static int osrfAppIntrospect( osrfMethodContext* ctx );
74 static int osrfAppIntrospectAll( osrfMethodContext* ctx );
75 static int osrfAppEcho( osrfMethodContext* ctx );
78 @brief Registry of applications.
80 The key of the hash is the application name, and the associated data is an osrfApplication.
82 static osrfHash* _osrfAppHash = NULL;
85 @brief Register an application.
86 @param appName Name of the application.
87 @param soFile Name of the shared object file to be loaded for this application.
88 @return Zero if successful, or -1 upon error.
90 Open the shared object file and call its osrfAppInitialize() function, if it has one.
91 Register the standard system methods for it. Arrange for the application name to
92 appear in subsequent log messages.
94 int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
95 if( !appName || ! soFile ) return -1;
98 osrfLogSetAppname( appName );
101 _osrfAppHash = osrfNewHash();
103 osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
105 void* handle = dlopen( soFile, RTLD_NOW );
107 const char* msg = dlerror();
108 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, msg );
112 osrfApplication* app = safe_malloc(sizeof(osrfApplication));
113 app->handle = handle;
115 app->methods = osrfNewHash();
116 osrfHashSet( _osrfAppHash, app, appName );
118 /* see if we can run the initialize method */
120 *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
122 if( (error = dlerror()) != NULL ) {
123 osrfLogWarning( OSRF_LOG_MARK,
124 "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s",
131 if( (ret = (*init)()) ) {
132 osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
133 "'osrfAppInitialize', not registering...", appName );
134 //free(app->name); /* need a method to remove an application from the list */
140 _osrfAppRegisterSysMethods(appName);
142 osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
144 osrfAppSetOnExit(app, appName);
150 @brief Save a pointer to the application's exit function.
151 @param app Pointer to the osrfApplication.
152 @param appName Application name (used only for log messages).
154 Look in the shared object for a symbol named "osrfAppChildExit". If you find one, save
155 it as a pointer to the application's exit function. If present, this function will be
156 called when a server's child process (a so-called "drone") is shutting down.
158 static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
159 if(!(app && appName)) return;
161 /* see if we can run the initialize method */
163 void (*onExit) (void);
164 *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
166 if( (error = dlerror()) != NULL ) {
167 osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
171 osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
172 app->onExit = (*onExit);
176 @brief Run the application-specific child initialization function for a given application.
177 @param appname Name of the application.
178 @return Zero if successful, or if the application has no child initialization function; -1
179 if the application is not registered, or if the function returns non-zero.
181 The child initialization function must be named "osrfAppChildInit" within the shared
182 object library. It initializes a drone process of a server.
184 int osrfAppRunChildInit(const char* appname) {
185 osrfApplication* app = _osrfAppFindApplication(appname);
190 int (*childInit) (void);
192 *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
194 if( (error = dlerror()) != NULL ) {
195 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
199 if( (ret = (*childInit)()) ) {
200 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
204 osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
209 @brief Call the exit handler for every application that has one.
211 Normally a server's child process (a so-called "drone") calls this function just before
214 void osrfAppRunExitCode( void ) {
215 osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
216 osrfApplication* app;
217 while( (app = osrfHashIteratorNext(itr)) ) {
219 osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
220 osrfHashIteratorKey(itr) );
224 osrfHashIteratorFree(itr);
228 @brief Register a method for a specified application.
230 @param appName Name of the application that implements the method.
231 @param methodName The fully qualified name of the method.
232 @param symbolName The symbol name (function name) that implements the method.
233 @param notes Public documentation for this method.
234 @param argc The minimum number of arguments for the function.
235 @param options Bit switches setting various options.
236 @return Zero on success, or -1 on error.
238 Registering a method enables us to call the right function when a client requests a
241 The @a options parameter is zero or more of the following macros, OR'd together:
243 - OSRF_METHOD_SYSTEM called by static linkage (shouldn't be used here)
244 - OSRF_METHOD_STREAMING method may return more than one response
245 - OSRF_METHOD_ATOMIC return all responses collected in a single RESULT message
246 - OSRF_METHOD_CACHABLE cache results in memcache
248 If the OSRF_METHOD_STREAMING bit is set, also register an ".atomic" version of the method.
250 int osrfAppRegisterMethod( const char* appName, const char* methodName,
251 const char* symbolName, const char* notes, int argc, int options ) {
253 return osrfAppRegisterExtendedMethod(
265 @brief Register a method for a specified application.
267 @param appName Name of the application that implements the method.
268 @param methodName The fully qualified name of the method.
269 @param symbolName The symbol name (function name) that implements the method.
270 @param notes Public documentation for this method.
271 @param argc How many arguments this method expects.
272 @param options Bit switches setting various options.
273 @param user_data Opaque pointer to be passed to the dynamically called function.
274 @return Zero on success, or -1 on error.
276 This function is identical to osrfAppRegisterMethod(), except that it also installs
277 a method-specific opaque pointer. When we call the corresponding function at
278 run time, this pointer will be available to the function via the method context.
280 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName,
281 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
283 if( !appName || ! methodName ) return -1;
285 osrfApplication* app = _osrfAppFindApplication(appName);
287 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
291 osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
293 osrfMethod* method = _osrfAppBuildMethod(
294 methodName, symbolName, notes, argc, options, user_data );
295 method->options = options;
297 /* plug the method into the list of methods */
298 osrfHashSet( app->methods, method, method->name );
300 if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
301 int newops = options | OSRF_METHOD_ATOMIC;
302 osrfMethod* atomicMethod = _osrfAppBuildMethod(
303 methodName, symbolName, notes, argc, newops, user_data );
304 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
311 @brief Allocate and populate an osrfMethod.
312 @param methodName Name of the method.
313 @param symbolName Name of the function that implements the method.
314 @param notes Remarks documenting the method.
315 @param argc Minimum number of arguments to the method.
316 @param options Bit switches setting various options.
317 @param user_data An opaque pointer to be passed in the method context.
318 @return Pointer to the newly allocated osrfMethod.
320 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
321 const char* notes, int argc, int options, void* user_data ) {
323 osrfMethod* method = safe_malloc(sizeof(osrfMethod));
326 methodName = ""; // should never happen
328 if( options & OSRF_METHOD_ATOMIC ) {
329 // Append ".atomic" to the name, and make the method atomic
330 char mb[ strlen( methodName ) + 8 ];
331 sprintf( mb, "%s.atomic", methodName );
332 method->name = strdup( mb );
333 options |= OSRF_METHOD_STREAMING;
335 method->name = strdup(methodName);
339 method->symbol = strdup(symbolName);
341 method->symbol = NULL;
344 method->notes = strdup(notes);
346 method->notes = NULL;
349 method->options = options;
352 method->userData = user_data;
358 @brief Register all of the system methods for this application.
359 @param app Application name.
361 A client can call these methods the same way it calls application-specific methods,
362 but they are implemented by functions here in this module, not by functions in the
365 static void _osrfAppRegisterSysMethods( const char* app ) {
367 osrfAppRegisterMethod(
368 app, OSRF_SYSMETHOD_INTROSPECT, NULL,
369 "Return a list of methods whose names have the same initial "
370 "substring as that of the provided method name PARAMS( methodNameSubstring )",
371 1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
373 osrfAppRegisterMethod(
374 app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL,
375 "Returns a complete list of methods. PARAMS()", 0,
376 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
378 osrfAppRegisterMethod(
379 app, OSRF_SYSMETHOD_ECHO, NULL,
380 "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0,
381 OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
385 @brief Look up an application by name in the application registry.
386 @param name The name of the application.
387 @return Pointer to the corresponding osrfApplication if found, or NULL if not.
389 static inline osrfApplication* _osrfAppFindApplication( const char* name ) {
390 return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
394 @brief Look up a method by name for a given application.
395 @param app Pointer to the osrfApplication that owns the method.
396 @param methodName Name of the method to find.
397 @return Pointer to the corresponding osrfMethod if found, or NULL if not.
399 static inline osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
400 if( !app ) return NULL;
401 return (osrfMethod*) osrfHashGet( app->methods, methodName );
405 @brief Look up a method by name for an application with a given name.
406 @param appName Name of the osrfApplication.
407 @param methodName Name of the method to find.
408 @return Pointer to the corresponding osrfMethod if found, or NULL if not.
410 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
411 if( !appName ) return NULL;
412 return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
416 @brief Call the function that implements a specified method.
417 @param appName Name of the application.
418 @param methodName Name of the method.
419 @param ses Pointer to the current application session.
420 @param reqId The request id of the request invoking the method.
421 @param params Pointer to a jsonObject encoding the parameters to the method.
422 @return Zero if successful, or -1 upon failure.
424 If we can't find a function corresponding to the method, or if we call it and it returns
425 a negative return code, send a STATUS message to the client to report an exception.
427 A return code of -1 means that the @a appName, @a methodName, or @a ses parameter was NULL.
429 int osrfAppRunMethod( const char* appName, const char* methodName,
430 osrfAppSession* ses, int reqId, jsonObject* params ) {
432 if( !(appName && methodName && ses) ) return -1;
434 // Find the application, and then find the method for it
435 osrfApplication* app = _osrfAppFindApplication(appName);
437 return osrfAppRequestRespondException( ses,
438 reqId, "Application not found: %s", appName );
440 osrfMethod* method = osrfAppFindMethod( app, methodName );
442 return osrfAppRequestRespondException( ses, reqId,
443 "Method [%s] not found for service %s", methodName, appName );
445 #ifdef OSRF_STRICT_PARAMS
446 if( method->argc > 0 ) {
447 // Make sure that the client has passed at least the minimum number of arguments.
448 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
449 return osrfAppRequestRespondException( ses, reqId,
450 "Not enough params for method %s / service %s", methodName, appName );
454 // Build an osrfMethodContext, which we will pass by pointer to the function.
455 osrfMethodContext context;
457 context.session = ses;
458 context.method = method;
459 context.params = params;
460 context.request = reqId;
461 context.responses = NULL;
465 if( method->options & OSRF_METHOD_SYSTEM ) {
466 retcode = _osrfAppRunSystemMethod(&context);
470 // Function pointer through which we will call the function dynamically
471 int (*meth) (osrfMethodContext*);
473 // Open the function that implements the method
474 meth = dlsym(app->handle, method->symbol);
476 const char* error = dlerror();
477 if( error != NULL ) {
478 return osrfAppRequestRespondException( ses, reqId,
479 "Unable to execute method [%s] for service %s", methodName, appName );
483 retcode = meth( &context );
487 return osrfAppRequestRespondException(
488 ses, reqId, "An unknown server error occurred" );
490 retcode = _osrfAppPostProcess( &context, retcode );
492 if( context.responses )
493 jsonObjectFree( context.responses );
498 @brief Either send or enqueue a response to a client.
499 @param ctx Pointer to the current method context.
500 @param data Pointer to the response, in the form of a jsonObject.
501 @return Zero if successful, or -1 upon error. The only recognized errors are if either
502 the @a context pointer or its method pointer is NULL.
504 For an atomic method, add a copy of the response data to a cache within the method
505 context, to be sent later. Otherwise, send a RESULT message to the client, with the
508 Note that, for an atomic method, this function is equivalent to osrfAppRespondComplete():
509 we send the STATUS message after the method returns, and not before.
511 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
512 return _osrfAppRespond( ctx, data, 0 );
516 @brief Either send or enqueue a response to a client, with a completion notice.
517 @param context Pointer to the current method context.
518 @param data Pointer to the response, in the form of a jsonObject.
519 @return Zero if successful, or -1 upon error. The only recognized errors are if either
520 the @a context pointer or its method pointer is NULL.
522 For an atomic method, add a copy of the response data to a cache within the method
523 context, to be sent later. Otherwise, send a RESULT message to the client, with the
524 results in @a data. Also send a STATUS message to indicate that the response is complete.
526 Note that, for an atomic method, this function is equivalent to osrfAppRespond(): we
527 send the STATUS message after the method returns, and not before.
529 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
530 return _osrfAppRespond( context, data, 1 );
534 @brief Send any response messages that have accumulated in the output buffer.
535 @param ses Pointer to the current application session.
536 @param outbuf Pointer to the output buffer.
537 @return Zero if successful, or -1 if not.
539 Used only by servers to respond to clients.
541 static int flush_responses( osrfAppSession* ses, growing_buffer* outbuf ) {
543 // Collect any inbound traffic on the socket(s). This doesn't accomplish anything for the
544 // immediate task at hand, but it may help to keep TCP from getting clogged in some cases.
545 osrf_app_session_queue_wait( ses, 0, NULL );
548 if( buffer_length( outbuf ) > 0 ) { // If there's anything to send...
549 buffer_add_char( outbuf, ']' ); // Close the JSON array
550 if( osrfSendTransportPayload( ses, OSRF_BUFFER_C_STR( ses->outbuf ))) {
551 osrfLogError( OSRF_LOG_MARK, "Unable to flush response buffer" );
555 buffer_reset( ses->outbuf );
560 @brief Add a message to an output buffer.
561 @param outbuf Pointer to the output buffer.
562 @param msg Pointer to the message to be added, in the form of a JSON string.
564 Since the output buffer is in the form of a JSON array, prepend a left bracket to the
565 first message, and a comma to subsequent ones.
567 Used only by servers to respond to clients.
569 static inline void append_msg( growing_buffer* outbuf, const char* msg ) {
570 if( outbuf && msg ) {
571 char prefix = buffer_length( outbuf ) > 0 ? ',' : '[';
572 buffer_add_char( outbuf, prefix );
573 buffer_add( outbuf, msg );
578 @brief Either send or enqueue a response to a client, optionally with a completion notice.
579 @param ctx Pointer to the method context.
580 @param data Pointer to the response, in the form of a jsonObject.
581 @param complete Boolean: if true, we will accompany the RESULT message with a STATUS
582 message indicating that the response is complete.
583 @return Zero if successful, or -1 upon error.
585 For an atomic method, add a copy of the response data to a cache within the method
586 context, to be sent later. In this case the @a complete parameter has no effect,
587 because we'll send the STATUS message later when we send the cached results.
589 If the method is not atomic, translate the message into JSON and append it to a buffer,
590 flushing the buffer as needed to avoid overflow. If @a complete is true, append
591 a STATUS message (as JSON) to the buffer and flush the buffer.
593 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
594 if(!(ctx && ctx->method)) return -1;
596 if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
597 osrfLogDebug( OSRF_LOG_MARK,
598 "Adding responses to stash for atomic method %s", ctx->method->name );
600 // If we don't already have one, create a JSON_ARRAY to serve as a cache.
601 if( ctx->responses == NULL )
602 ctx->responses = jsonNewObjectType( JSON_ARRAY );
604 // Add a copy of the data object to the cache.
606 jsonObjectPush( ctx->responses, jsonObjectClone(data) );
608 osrfLogDebug( OSRF_LOG_MARK,
609 "Adding responses to stash for method %s", ctx->method->name );
612 // If you want to flush the intput buffers for every output message,
613 // this is the place to do it.
614 //osrf_app_session_queue_wait( ctx->session, 0, NULL );
616 // Create an OSRF message
617 osrfMessage* msg = osrf_message_init( RESULT, ctx->request, 1 );
618 osrf_message_set_status_info( msg, NULL, "OK", OSRF_STATUS_OK );
619 osrf_message_set_result( msg, data );
621 // Serialize the OSRF message into JSON text
622 char* json = jsonObjectToJSON( osrfMessageToJSON( msg ));
623 osrfMessageFree( msg );
625 // If the new message would overflow the buffer, flush the output buffer first
626 int len_so_far = buffer_length( ctx->session->outbuf );
627 if( len_so_far && (strlen( json ) + len_so_far >= OSRF_MSG_BUFFER_SIZE - 3) ) {
628 if( flush_responses( ctx->session, ctx->session->outbuf ))
632 // Append the JSON text to the output buffer
633 append_msg( ctx->session->outbuf, json );
638 // Create a STATUS message
639 osrfMessage* status_msg = osrf_message_init( STATUS, ctx->request, 1 );
640 osrf_message_set_status_info( status_msg, "osrfConnectStatus", "Request Complete",
641 OSRF_STATUS_COMPLETE );
643 // Serialize the STATUS message into JSON text
644 char* json = jsonObjectToJSON( osrfMessageToJSON( status_msg ));
645 osrfMessageFree( status_msg );
647 // Add the STATUS message to the output buffer.
648 // It's short, so don't worry about avoiding overflow.
649 append_msg( ctx->session->outbuf, json );
652 // Flush the output buffer, sending any accumulated messages.
653 if( flush_responses( ctx->session, ctx->session->outbuf ))
662 @brief Finish up the processing of a request.
663 @param ctx Pointer to the method context.
664 @param retcode The return code from the method's function.
665 @return 0 if successfull, or -1 upon error.
667 For an atomic method: send whatever responses we have been saving up, together with a
668 STATUS message to say that we're finished.
670 For a non-atomic method: if the return code from the method is greater than zero, just
671 send the STATUS message. If the return code is zero, do nothing; the method presumably
672 sent the STATUS message on its own.
674 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
675 if(!(ctx && ctx->method)) return -1;
677 osrfLogDebug( OSRF_LOG_MARK, "Postprocessing method %s with retcode %d",
678 ctx->method->name, retcode );
681 // We have cached atomic responses to return, collected in a JSON ARRAY (we
682 // haven't sent any responses yet). Now send them all at once, followed by
683 // a STATUS message to say that we're finished.
684 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
687 // We have no cached atomic responses to return, but we may have some
688 // non-atomic messages waiting in the buffer.
690 // Send a STATUS message to say that we're finished, and to force a
691 // final flush of the buffer.
692 osrfAppRespondComplete( ctx, NULL );
699 @brief Send a STATUS message to the client, notifying it of an error.
700 @param ses Pointer to the current application session.
701 @param request Request ID of the request.
702 @param msg A printf-style format string defining an explanatory message to be sent to
703 the client. Subsequent parameters, if any, will be formatted and inserted into the
704 resulting output string.
705 @return -1 if the @a ses parameter is NULL; otherwise zero.
707 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
710 VA_LIST_TO_STRING(msg);
711 osrfLogWarning( OSRF_LOG_MARK, "Returning method exception with message: %s", VA_BUF );
712 osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request, VA_BUF );
717 @brief Introspect a specified method.
718 @param ctx Pointer to the method context.
719 @param method Pointer to the osrfMethod for the specified method.
720 @param resp Pointer to the jsonObject into which method information will be placed.
722 Treating the @a resp object as a JSON_HASH, insert entries for various bits of information
723 about the specified method.
725 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method,
727 if(!(ctx && resp)) return;
729 jsonObjectSetKey(resp, "api_name", jsonNewObject(method->name));
730 jsonObjectSetKey(resp, "method", jsonNewObject(method->symbol));
731 jsonObjectSetKey(resp, "service", jsonNewObject(ctx->session->remote_service));
732 jsonObjectSetKey(resp, "notes", jsonNewObject(method->notes));
733 jsonObjectSetKey(resp, "argc", jsonNewNumberObject(method->argc));
735 jsonObjectSetKey(resp, "sysmethod",
736 jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
737 jsonObjectSetKey(resp, "atomic",
738 jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
739 jsonObjectSetKey(resp, "cachable",
740 jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
744 @brief Run the requested system method.
745 @param ctx The method context.
746 @return Zero if the method is run successfully; -1 if the method was not run; 1 if the
747 method was run and the application code now needs to send a 'request complete' message.
749 A system method is a well known method implemented here for all servers. Instead of
750 looking in the shared object, branch on the method name and call the corresponding
753 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
754 if( osrfMethodVerifyContext( ctx ) < 0 ) {
755 osrfLogError( OSRF_LOG_MARK, "_osrfAppRunSystemMethod: Received invalid method context" );
759 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) ||
760 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
761 return osrfAppIntrospectAll(ctx);
764 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
765 !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
766 return osrfAppIntrospect(ctx);
769 if( !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
770 !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
771 return osrfAppEcho(ctx);
774 osrfAppRequestRespondException( ctx->session,
775 ctx->request, "System method implementation not found");
781 @brief Run the introspect method for a specified method or group of methods.
782 @param ctx Pointer to the method context.
783 @return 1 if successful, or if no search target is specified as a parameter; -1 if unable
784 to find a pointer to the application.
786 Traverse the list of methods, and report on each one whose name starts with the specified
787 search target. In effect, the search target ends with an implicit wild card.
789 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
791 // Get the name of the method to introspect
792 const char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
793 if( !methodSubstring )
794 return 1; /* respond with no methods */
796 // Get a pointer to the application
797 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
799 return -1; // Oops, no application...
802 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
805 while( (method = osrfHashIteratorNext(itr)) ) {
806 if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
807 if( !strncmp( method->name, methodSubstring, len) ) {
808 jsonObject* resp = jsonNewObject(NULL);
809 _osrfAppSetIntrospectMethod( ctx, method, resp );
810 osrfAppRespond(ctx, resp);
811 jsonObjectFree(resp);
816 osrfHashIteratorFree(itr);
821 @brief Run the implement_all method.
822 @param ctx Pointer to the method context.
823 @return 1 if successful, or -1 if unable to find a pointer to the application.
825 Report on all of the methods of the application.
827 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
828 osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
831 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
833 while( (method = osrfHashIteratorNext(itr)) ) {
834 jsonObject* resp = jsonNewObject(NULL);
835 _osrfAppSetIntrospectMethod( ctx, method, resp );
836 osrfAppRespond(ctx, resp);
837 jsonObjectFree(resp);
839 osrfHashIteratorFree(itr);
846 @brief Run the echo method.
847 @param ctx Pointer to the method context.
848 @return -1 if the method context is invalid or corrupted; otherwise 1.
850 Send the client a copy of each parameter.
852 static int osrfAppEcho( osrfMethodContext* ctx ) {
853 if( osrfMethodVerifyContext( ctx ) < 0 ) {
854 osrfLogError( OSRF_LOG_MARK, "osrfAppEcho: Received invalid method context" );
859 for( i = 0; i < ctx->params->size; i++ ) {
860 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
861 osrfAppRespond(ctx, str);
867 @brief Perform a series of sanity tests on an osrfMethodContext.
868 @param ctx Pointer to the osrfMethodContext to be checked.
869 @return Zero if the osrfMethodContext passes all tests, or -1 if it doesn't.
871 int osrfMethodVerifyContext( osrfMethodContext* ctx )
874 osrfLogError( OSRF_LOG_MARK, "Context is NULL in app request" );
878 if( !ctx->session ) {
879 osrfLogError( OSRF_LOG_MARK, "Session is NULL in app request" );
885 osrfLogError( OSRF_LOG_MARK, "Method is NULL in app request" );
889 if( ctx->method->argc ) {
891 osrfLogError( OSRF_LOG_MARK,
892 "Params is NULL in app request %s", ctx->method->name );
895 if( ctx->params->type != JSON_ARRAY ) {
896 osrfLogError( OSRF_LOG_MARK,
897 "'params' is not a JSON array for method %s", ctx->method->name );
902 if( !ctx->method->name ) {
903 osrfLogError( OSRF_LOG_MARK, "Method name is NULL" );
907 // Log the call, with the method and parameters
908 char* params_str = jsonObjectToJSON( ctx->params );
910 osrfLogInfo( OSRF_LOG_MARK, "CALL:\t%s %s - %s",
911 ctx->session->remote_service, ctx->method->name, params_str );