]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_application.c
This update boosts the performance of the jsonFormatString function.
[OpenSRF.git] / src / libopensrf / osrf_application.c
1 #include <opensrf/osrf_application.h>
2
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 );
15
16 static osrfHash* _osrfAppHash = NULL;
17
18 int osrfAppRegisterApplication( const char* appName, const char* soFile ) {
19         if(!appName || ! soFile) return -1;
20         char* error;
21
22         if(!_osrfAppHash) _osrfAppHash = osrfNewHash();
23
24         osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
25
26         osrfApplication* app = safe_malloc(sizeof(osrfApplication));
27         app->handle = dlopen (soFile, RTLD_NOW);
28         app->onExit = NULL;
29
30         if(!app->handle) {
31                 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
32                 dlerror(); /* clear the error */
33                 free(app);
34                 return -1;
35         }
36
37         app->methods = osrfNewHash();
38         osrfHashSet( _osrfAppHash, app, appName );
39
40         /* see if we can run the initialize method */
41         int (*init) (void);
42         *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
43
44         if( (error = dlerror()) != NULL ) {
45                 osrfLogWarning( OSRF_LOG_MARK, 
46                         "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
47
48         } else {
49
50                 /* run the method */
51                 int ret;
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 */
56                         //free(app);
57                         return ret;
58                 }
59         }
60
61         _osrfAppRegisterSysMethods(appName);
62
63         osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
64
65         osrfLogSetAppname(appName);
66
67         osrfAppSetOnExit(app, appName);
68
69         return 0;
70 }
71
72
73 static void osrfAppSetOnExit(osrfApplication* app, const char* appName) {
74         if(!(app && appName)) return;
75
76         /* see if we can run the initialize method */
77         char* error;
78         void (*onExit) (void);
79         *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
80
81         if( (error = dlerror()) != NULL ) {
82                 osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
83                 return;
84         }
85
86         osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
87         app->onExit = (*onExit);
88 }
89
90
91 int osrfAppRunChildInit(const char* appname) {
92         osrfApplication* app = _osrfAppFindApplication(appname);
93         if(!app) return -1;
94
95         char* error;
96         int ret;
97         int (*childInit) (void);
98
99         *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
100
101         if( (error = dlerror()) != NULL ) {
102                 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
103                 return 0;
104         }
105
106         if( (ret = (*childInit)()) ) {
107                 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
108                 return -1;
109         }
110
111         osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
112         return 0;
113 }
114
115
116 void osrfAppRunExitCode() { 
117         osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
118         osrfApplication* app;
119         while( (app = osrfHashIteratorNext(itr)) ) {
120                 if( app->onExit ) {
121                         osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
122                                 osrfHashIteratorKey(itr) );
123                         app->onExit();
124                 }
125         }
126         osrfHashIteratorFree(itr);
127 }
128
129
130 int osrfAppRegisterMethod( const char* appName, const char* methodName, 
131                 const char* symbolName, const char* notes, int argc, int options ) {
132
133         return osrfAppRegisterExtendedMethod(
134                         appName,
135                         methodName,
136                         symbolName,
137                         notes,
138                         argc,
139                         options,
140                         NULL
141         );
142
143 }
144
145 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName, 
146                 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
147
148         if( !appName || ! methodName  ) return -1;
149
150         osrfApplication* app = _osrfAppFindApplication(appName);
151         if(!app) {
152                 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
153                 return -1;
154         }
155
156         osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
157
158         osrfMethod* method = _osrfAppBuildMethod(
159                 methodName, symbolName, notes, argc, options, user_data );              
160         method->options = options;
161
162         /* plug the method into the list of methods */
163         osrfHashSet( app->methods, method, method->name );
164
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;
171         }
172
173         return 0;
174 }
175
176
177
178 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
179                 const char* notes, int argc, int options, void* user_data ) {
180
181         osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
182
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;
190
191         method->argc                                                    = argc;
192         method->options                                         = options;
193
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);
197                 free(method->name);
198                 method->name = strdup(mb);
199                 method->options |= OSRF_METHOD_STREAMING;
200         }
201
202         return method;
203 }
204
205
206 /**
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 ) {
210
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 );
216
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 );
221
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 );
226
227         return 0;
228 }
229
230 /**
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
234  */
235 static osrfApplication* _osrfAppFindApplication( const char* name ) {
236         if(!name) return NULL;
237         return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
238 }
239
240 /**
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
246  */
247 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
248         if(!app || ! methodName) return NULL;
249         return (osrfMethod*) osrfHashGet( app->methods, methodName );
250 }
251
252 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
253         if(!appName || ! methodName) return NULL;
254         return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
255 }
256
257
258 int osrfAppRunMethod( const char* appName, const char* methodName, 
259                 osrfAppSession* ses, int reqId, jsonObject* params ) {
260
261         if( !(appName && methodName && ses) ) return -1;
262
263         char* error;
264         osrfApplication* app;
265         osrfMethod* method;
266         osrfMethodContext context;
267
268         context.session = ses;
269         context.params = params;
270         context.request = reqId;
271         context.responses = NULL;
272
273         /* this is the method we're gonna run */
274         int (*meth) (osrfMethodContext*);       
275
276         if( !(app = _osrfAppFindApplication(appName)) )
277                 return osrfAppRequestRespondException( ses, 
278                                 reqId, "Application not found: %s", appName );
279         
280         if( !(method = osrfAppFindMethod( app, methodName )) )
281                 return osrfAppRequestRespondException( ses, reqId, 
282                                 "Method [%s] not found for service %s", methodName, appName );
283
284         context.method = method;
285
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 );
291         }
292         #endif
293
294         int retcode = 0;
295
296         if( method->options & OSRF_METHOD_SYSTEM ) {
297                 retcode = _osrfAppRunSystemMethod(&context);
298
299         } else {
300
301                 /* open and now run the method */
302                 *(void **) (&meth) = dlsym(app->handle, method->symbol);
303
304                 if( (error = dlerror()) != NULL ) {
305                         return osrfAppRequestRespondException( ses, reqId, 
306                                 "Unable to execute method [%s]  for service %s", methodName, appName );
307                 }
308
309                 retcode = (*meth) (&context);
310         }
311
312         if(retcode < 0) 
313                 return osrfAppRequestRespondException( 
314                                 ses, reqId, "An unknown server error occurred" );
315
316         return _osrfAppPostProcess( &context, retcode );
317
318 }
319
320
321 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
322         return _osrfAppRespond( ctx, data, 0 );
323 }
324
325 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
326         return _osrfAppRespond( context, data, 1 );
327 }
328
329 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
330         if(!(ctx && ctx->method)) return -1;
331
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 );
335
336                 if( ctx->responses == NULL )                                                                                            
337                         ctx->responses = jsonParseString("[]");                                                 
338
339                 if ( data != NULL )
340                         jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
341         }
342
343
344         if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
345                         !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
346
347                 if(complete) 
348                         osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
349                 else
350                         osrfAppRequestRespond( ctx->session, ctx->request, data );
351                 return 0;
352         }
353
354         return 0;
355 }
356
357
358
359
360 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
361         if(!(ctx && ctx->method)) return -1;
362
363         osrfLogDebug( OSRF_LOG_MARK,  "Postprocessing method %s with retcode %d",
364                         ctx->method->name, retcode );
365
366         if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
367
368                 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
369                 jsonObjectFree(ctx->responses);
370                 ctx->responses = NULL;
371
372         } else {
373
374                 if( retcode > 0 ) 
375                         osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
376                                         "osrfConnectStatus", ctx->request, "Request Complete" );
377         }
378
379         return 0;
380 }
381
382 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
383         if(!ses) return -1;
384         if(!msg) 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 );
388         return 0;
389 }
390
391
392 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method, jsonObject* resp ) {
393         if(!(ctx && resp)) return;
394
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));
400
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 ));
407
408         jsonObjectSetClass(resp, "method");
409 }
410
411 /**
412   Trys to run the requested method as a system method.
413   A system method is a well known method that all
414   servers implement.  
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' 
419   message
420  */
421 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
422         OSRF_METHOD_VERIFY_CONTEXT(ctx);
423
424         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
425                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
426
427                 return osrfAppIntrospectAll(ctx);
428         }
429
430
431         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
432                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
433
434                 return osrfAppIntrospect(ctx);
435         }
436
437         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
438                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
439
440                 return osrfAppEcho(ctx);
441         }
442
443
444         osrfAppRequestRespondException( ctx->session, 
445                         ctx->request, "System method implementation not found");
446
447         return 0;
448 }
449
450
451 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
452
453         jsonObject* resp = NULL;
454         char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
455         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
456         int len = 0;
457
458         if(!methodSubstring) return 1; /* respond with no methods */
459
460         if(app) {
461
462                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
463                 osrfMethod* method;
464
465                 while( (method = osrfHashIteratorNext(itr)) ) {
466                         if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
467                                 if( !strncmp( method->name, methodSubstring, len) ) {
468                                         resp = jsonNewObject(NULL);
469                                         _osrfAppSetIntrospectMethod( ctx, method, resp );
470                                         osrfAppRespond(ctx, resp);
471                                         jsonObjectFree(resp);
472                                 }
473                         }
474                 }
475                 osrfHashIteratorFree(itr);
476                 return 1;
477         }
478
479         return -1;
480
481 }
482
483
484 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
485         jsonObject* resp = NULL;
486         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
487
488         if(app) {
489                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
490                 osrfMethod* method;
491                 while( (method = osrfHashIteratorNext(itr)) ) {
492                         resp = jsonNewObject(NULL);
493                         _osrfAppSetIntrospectMethod( ctx, method, resp );
494                         osrfAppRespond(ctx, resp);
495                         jsonObjectFree(resp);
496                 }
497                 osrfHashIteratorFree(itr);
498                 return 1;
499         }
500
501         return -1;
502 }
503
504 static int osrfAppEcho( osrfMethodContext* ctx ) {
505         OSRF_METHOD_VERIFY_CONTEXT(ctx);
506         int i;
507         for( i = 0; i < ctx->params->size; i++ ) {
508                 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
509                 osrfAppRespond(ctx, str);
510         }
511         return 1;
512 }
513