]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_application.c
ece61a2a3f16341607a788023cd203975c1e62c8
[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", itr->current);
122                         app->onExit();
123                 }
124         }
125         osrfHashIteratorFree(itr);
126 }
127
128
129 int osrfAppRegisterMethod( const char* appName, const char* methodName, 
130                 const char* symbolName, const char* notes, int argc, int options ) {
131
132         return osrfAppRegisterExtendedMethod(
133                         appName,
134                         methodName,
135                         symbolName,
136                         notes,
137                         argc,
138                         options,
139                         NULL
140         );
141
142 }
143
144 int osrfAppRegisterExtendedMethod( const char* appName, const char* methodName, 
145                 const char* symbolName, const char* notes, int argc, int options, void * user_data ) {
146
147         if( !appName || ! methodName  ) return -1;
148
149         osrfApplication* app = _osrfAppFindApplication(appName);
150         if(!app) {
151                 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
152                 return -1;
153         }
154
155         osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
156
157         osrfMethod* method = _osrfAppBuildMethod(
158                 methodName, symbolName, notes, argc, options, user_data );              
159         method->options = options;
160
161         /* plug the method into the list of methods */
162         osrfHashSet( app->methods, method, method->name );
163
164         if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
165                 int newops = options | OSRF_METHOD_ATOMIC;
166                 osrfMethod* atomicMethod = _osrfAppBuildMethod(
167                         methodName, symbolName, notes, argc, newops, NULL );            
168                 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
169                 atomicMethod->userData = method->userData;
170         }
171
172         return 0;
173 }
174
175
176
177 static osrfMethod* _osrfAppBuildMethod( const char* methodName, const char* symbolName,
178                 const char* notes, int argc, int options, void* user_data ) {
179
180         osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
181
182         if(methodName) method->name             = strdup(methodName);
183         else method->name    = NULL;
184         if(symbolName) method->symbol           = strdup(symbolName);
185         else method->symbol  = NULL;
186         if(notes) method->notes                         = strdup(notes);
187         else method->notes   = NULL;
188         if(user_data) method->userData  = user_data;
189
190         method->argc                                                    = argc;
191         method->options                                         = options;
192
193         if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
194                 char mb[strlen(method->name) + 8];
195                 sprintf(mb, "%s.atomic", method->name);
196                 free(method->name);
197                 method->name = strdup(mb);
198                 method->options |= OSRF_METHOD_STREAMING;
199         }
200
201         return method;
202 }
203
204
205 /**
206   Registers all of the system methods for this app so that they may be
207   treated the same as other methods */
208 static int _osrfAppRegisterSysMethods( const char* app ) {
209
210         osrfAppRegisterMethod( 
211                         app, OSRF_SYSMETHOD_INTROSPECT, NULL, 
212                         "Return a list of methods whose names have the same initial "
213                         "substring as that of the provided method name PARAMS( methodNameSubstring )", 
214                         1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
215
216         osrfAppRegisterMethod( 
217                         app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL, 
218                         "Returns a complete list of methods. PARAMS()", 0, 
219                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
220
221         osrfAppRegisterMethod( 
222                         app, OSRF_SYSMETHOD_ECHO, NULL, 
223                         "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0, 
224                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
225
226         return 0;
227 }
228
229 /**
230   Finds the given app in the list of apps
231   @param name The name of the application
232   @return The application pointer or NULL if there is no such application
233  */
234 static osrfApplication* _osrfAppFindApplication( const char* name ) {
235         if(!name) return NULL;
236         return (osrfApplication*) osrfHashGet(_osrfAppHash, name);
237 }
238
239 /**
240   Finds the given method for the given app
241   @param app The application object
242   @param methodName The method to find
243   @return A method pointer or NULL if no such method 
244   exists for the given application
245  */
246 static osrfMethod* osrfAppFindMethod( osrfApplication* app, const char* methodName ) {
247         if(!app || ! methodName) return NULL;
248         return (osrfMethod*) osrfHashGet( app->methods, methodName );
249 }
250
251 osrfMethod* _osrfAppFindMethod( const char* appName, const char* methodName ) {
252         if(!appName || ! methodName) return NULL;
253         return osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
254 }
255
256
257 int osrfAppRunMethod( const char* appName, const char* methodName, 
258                 osrfAppSession* ses, int reqId, jsonObject* params ) {
259
260         if( !(appName && methodName && ses) ) return -1;
261
262         char* error;
263         osrfApplication* app;
264         osrfMethod* method;
265         osrfMethodContext context;
266
267         context.session = ses;
268         context.params = params;
269         context.request = reqId;
270         context.responses = NULL;
271
272         /* this is the method we're gonna run */
273         int (*meth) (osrfMethodContext*);       
274
275         if( !(app = _osrfAppFindApplication(appName)) )
276                 return osrfAppRequestRespondException( ses, 
277                                 reqId, "Application not found: %s", appName );
278         
279         if( !(method = osrfAppFindMethod( app, methodName )) )
280                 return osrfAppRequestRespondException( ses, reqId, 
281                                 "Method [%s] not found for service %s", methodName, appName );
282
283         context.method = method;
284
285         #ifdef OSRF_STRICT_PARAMS
286         if( method->argc > 0 ) {
287                 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
288                         return osrfAppRequestRespondException( ses, reqId, 
289                                 "Not enough params for method %s / service %s", methodName, appName );
290         }
291         #endif
292
293         int retcode = 0;
294
295         if( method->options & OSRF_METHOD_SYSTEM ) {
296                 retcode = _osrfAppRunSystemMethod(&context);
297
298         } else {
299
300                 /* open and now run the method */
301                 *(void **) (&meth) = dlsym(app->handle, method->symbol);
302
303                 if( (error = dlerror()) != NULL ) {
304                         return osrfAppRequestRespondException( ses, reqId, 
305                                 "Unable to execute method [%s]  for service %s", methodName, appName );
306                 }
307
308                 retcode = (*meth) (&context);
309         }
310
311         if(retcode < 0) 
312                 return osrfAppRequestRespondException( 
313                                 ses, reqId, "An unknown server error occurred" );
314
315         return _osrfAppPostProcess( &context, retcode );
316
317 }
318
319
320 int osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data ) {
321         return _osrfAppRespond( ctx, data, 0 );
322 }
323
324 int osrfAppRespondComplete( osrfMethodContext* context, const jsonObject* data ) {
325         return _osrfAppRespond( context, data, 1 );
326 }
327
328 static int _osrfAppRespond( osrfMethodContext* ctx, const jsonObject* data, int complete ) {
329         if(!(ctx && ctx->method)) return -1;
330
331         if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
332                 osrfLogDebug( OSRF_LOG_MARK,   
333                         "Adding responses to stash for atomic method %s", ctx->method->name );
334
335                 if( ctx->responses == NULL )                                                                                            
336                         ctx->responses = jsonParseString("[]");                                                 
337
338                 if ( data != NULL )
339                         jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
340         }
341
342
343         if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
344                         !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
345
346                 if(complete) 
347                         osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
348                 else
349                         osrfAppRequestRespond( ctx->session, ctx->request, data );
350                 return 0;
351         }
352
353         return 0;
354 }
355
356
357
358
359 static int _osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
360         if(!(ctx && ctx->method)) return -1;
361
362         osrfLogDebug( OSRF_LOG_MARK,  "Postprocessing method %s with retcode %d",
363                         ctx->method->name, retcode );
364
365         if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
366
367                 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
368                 jsonObjectFree(ctx->responses);
369                 ctx->responses = NULL;
370
371         } else {
372
373                 if( retcode > 0 ) 
374                         osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
375                                         "osrfConnectStatus", ctx->request, "Request Complete" );
376         }
377
378         return 0;
379 }
380
381 int osrfAppRequestRespondException( osrfAppSession* ses, int request, const char* msg, ... ) {
382         if(!ses) return -1;
383         if(!msg) msg = "";
384         VA_LIST_TO_STRING(msg);
385         osrfLogWarning( OSRF_LOG_MARK,  "Returning method exception with message: %s", VA_BUF );
386         osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request,  VA_BUF );
387         return 0;
388 }
389
390
391 static void _osrfAppSetIntrospectMethod( osrfMethodContext* ctx, const osrfMethod* method, jsonObject* resp ) {
392         if(!(ctx && resp)) return;
393
394         jsonObjectSetKey(resp, "api_name",      jsonNewObject(method->name));
395         jsonObjectSetKey(resp, "method",        jsonNewObject(method->symbol));
396         jsonObjectSetKey(resp, "service",       jsonNewObject(ctx->session->remote_service));
397         jsonObjectSetKey(resp, "notes",         jsonNewObject(method->notes));
398         jsonObjectSetKey(resp, "argc",          jsonNewNumberObject(method->argc));
399
400         jsonObjectSetKey(resp, "sysmethod", 
401                         jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
402         jsonObjectSetKey(resp, "atomic",                
403                         jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
404         jsonObjectSetKey(resp, "cachable",      
405                         jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
406
407         jsonObjectSetClass(resp, "method");
408 }
409
410 /**
411   Trys to run the requested method as a system method.
412   A system method is a well known method that all
413   servers implement.  
414   @param context The current method context
415   @return 0 if the method is run successfully, return < 0 means
416   the method was not run, return > 0 means the method was run
417   and the application code now needs to send a 'request complete' 
418   message
419  */
420 static int _osrfAppRunSystemMethod(osrfMethodContext* ctx) {
421         OSRF_METHOD_VERIFY_CONTEXT(ctx);
422
423         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
424                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
425
426                 return osrfAppIntrospectAll(ctx);
427         }
428
429
430         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
431                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
432
433                 return osrfAppIntrospect(ctx);
434         }
435
436         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
437                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
438
439                 return osrfAppEcho(ctx);
440         }
441
442
443         osrfAppRequestRespondException( ctx->session, 
444                         ctx->request, "System method implementation not found");
445
446         return 0;
447 }
448
449
450 static int osrfAppIntrospect( osrfMethodContext* ctx ) {
451
452         jsonObject* resp = NULL;
453         char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
454         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
455         int len = 0;
456
457         if(!methodSubstring) return 1; /* respond with no methods */
458
459         if(app) {
460
461                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
462                 osrfMethod* method;
463
464                 while( (method = osrfHashIteratorNext(itr)) ) {
465                         if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
466                                 if( !strncmp( method->name, methodSubstring, len) ) {
467                                         resp = jsonNewObject(NULL);
468                                         _osrfAppSetIntrospectMethod( ctx, method, resp );
469                                         osrfAppRespond(ctx, resp);
470                                         jsonObjectFree(resp);
471                                 }
472                         }
473                 }
474                 osrfHashIteratorFree(itr);
475                 return 1;
476         }
477
478         return -1;
479
480 }
481
482
483 static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
484         jsonObject* resp = NULL;
485         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
486
487         if(app) {
488                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
489                 osrfMethod* method;
490                 while( (method = osrfHashIteratorNext(itr)) ) {
491                         resp = jsonNewObject(NULL);
492                         _osrfAppSetIntrospectMethod( ctx, method, resp );
493                         osrfAppRespond(ctx, resp);
494                         jsonObjectFree(resp);
495                 }
496                 osrfHashIteratorFree(itr);
497                 return 1;
498         }
499
500         return -1;
501 }
502
503 static int osrfAppEcho( osrfMethodContext* ctx ) {
504         OSRF_METHOD_VERIFY_CONTEXT(ctx);
505         int i;
506         for( i = 0; i < ctx->params->size; i++ ) {
507                 const jsonObject* str = jsonObjectGetIndex(ctx->params,i);
508                 osrfAppRespond(ctx, str);
509         }
510         return 1;
511 }
512