]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_application.c
Merged libopensrf source directories (libtransport, libstack, and utils) into a singl...
[OpenSRF.git] / src / libopensrf / osrf_application.c
1 #include <opensrf/osrf_application.h>
2 #include <objson/object.h>
3
4 //osrfApplication* __osrfAppList = NULL; 
5
6 osrfHash* __osrfAppHash = NULL;
7
8
9 int osrfAppRegisterApplication( char* appName, char* soFile ) {
10         if(!appName || ! soFile) return -1;
11         char* error;
12
13         if(!__osrfAppHash) __osrfAppHash = osrfNewHash();
14
15         osrfLogInfo( OSRF_LOG_MARK, "Registering application %s with file %s", appName, soFile );
16
17         osrfApplication* app = safe_malloc(sizeof(osrfApplication));
18         app->handle = dlopen (soFile, RTLD_NOW);
19    app->onExit = NULL;
20
21         if(!app->handle) {
22                 osrfLogWarning( OSRF_LOG_MARK, "Failed to dlopen library file %s: %s", soFile, dlerror() );
23                 dlerror(); /* clear the error */
24                 free(app);
25                 return -1;
26         }
27
28         app->methods = osrfNewHash();
29         osrfHashSet( __osrfAppHash, app, appName );
30
31         /* see if we can run the initialize method */
32         int (*init) (void);
33         *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
34
35         if( (error = dlerror()) != NULL ) {
36                 osrfLogWarning( OSRF_LOG_MARK, 
37                         "! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
38
39         } else {
40
41                 /* run the method */
42                 int ret;
43                 if( (ret = (*init)()) ) {
44                         osrfLogWarning( OSRF_LOG_MARK, "Application %s returned non-zero value from "
45                                         "'osrfAppInitialize', not registering...", appName );
46                         //free(app->name); /* need a method to remove an application from the list */
47                         //free(app);
48                         return ret;
49                 }
50         }
51
52         __osrfAppRegisterSysMethods(appName);
53
54         osrfLogInfo( OSRF_LOG_MARK, "Application %s registered successfully", appName );
55
56         osrfLogSetAppname(appName);
57
58    osrfAppSetOnExit(app, appName);
59
60         return 0;
61 }
62
63
64 void osrfAppSetOnExit(osrfApplication* app, char* appName) {
65    if(!(app && appName)) return;
66
67         /* see if we can run the initialize method */
68    char* error;
69         void (*onExit) (void);
70         *(void **) (&onExit) = dlsym(app->handle, "osrfAppChildExit");
71
72         if( (error = dlerror()) != NULL ) {
73       osrfLogDebug(OSRF_LOG_MARK, "No exit handler defined for %s", appName);
74       return;
75    }
76
77    osrfLogInfo(OSRF_LOG_MARK, "registering exit handler for %s", appName);
78    app->onExit = (*onExit);
79    //if( (ret = (*onExit)()) ) {
80 }
81
82
83 int osrfAppRunChildInit(char* appname) {
84         osrfApplication* app = _osrfAppFindApplication(appname);
85         if(!app) return -1;
86
87         char* error;
88         int ret;
89         int (*childInit) (void);
90
91         *(void**) (&childInit) = dlsym(app->handle, "osrfAppChildInit");
92
93         if( (error = dlerror()) != NULL ) {
94                 osrfLogInfo( OSRF_LOG_MARK, "No child init defined for app %s : %s", appname, error);
95                 return 0;
96         }
97
98         if( (ret = (*childInit)()) ) {
99                 osrfLogError(OSRF_LOG_MARK, "App %s child init failed", appname);
100                 return -1;
101         }
102
103         osrfLogInfo(OSRF_LOG_MARK, "%s child init succeeded", appname);
104         return 0;
105 }
106
107
108 void osrfAppRunExitCode() { 
109    osrfHashIterator* itr = osrfNewHashIterator(__osrfAppHash);
110    osrfApplication* app;
111    while( (app = osrfHashIteratorNext(itr)) ) {
112       if( app->onExit ) {
113          osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s", itr->current);
114          app->onExit();
115       }
116    }
117 }
118
119
120 int osrfAppRegisterMethod( char* appName, char* methodName, 
121                 char* symbolName, char* notes, int argc, int options ) {
122
123         return osrfAppRegisterExtendedMethod(
124                         appName,
125                         methodName,
126                         symbolName,
127                         notes,
128                         argc,
129                         options,
130                         NULL
131         );
132
133 }
134
135 int osrfAppRegisterExtendedMethod( char* appName, char* methodName, 
136                 char* symbolName, char* notes, int argc, int options, void * user_data ) {
137
138         if( !appName || ! methodName  ) return -1;
139
140         osrfApplication* app = _osrfAppFindApplication(appName);
141         if(!app) {
142                 osrfLogWarning( OSRF_LOG_MARK, "Unable to locate application %s", appName );
143                 return -1;
144         }
145
146         osrfLogDebug( OSRF_LOG_MARK, "Registering method %s for app %s", methodName, appName );
147
148         osrfMethod* method = _osrfAppBuildMethod(
149                 methodName, symbolName, notes, argc, options, user_data );              
150         method->options = options;
151
152         /* plug the method into the list of methods */
153         osrfHashSet( app->methods, method, method->name );
154
155         if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
156                 int newops = options | OSRF_METHOD_ATOMIC;
157                 osrfMethod* atomicMethod = _osrfAppBuildMethod(
158                         methodName, symbolName, notes, argc, newops, NULL );            
159                 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
160                 atomicMethod->userData = method->userData;
161         }
162
163         return 0;
164 }
165
166
167
168 osrfMethod* _osrfAppBuildMethod( char* methodName, 
169         char* symbolName, char* notes, int argc, int options, void* user_data ) {
170
171         osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
172
173         if(methodName) method->name             = strdup(methodName);
174         if(symbolName) method->symbol           = strdup(symbolName);
175         if(notes) method->notes                         = strdup(notes);
176         if(user_data) method->userData  = user_data;
177
178         method->argc                                                    = argc;
179         method->options                                         = options;
180
181         if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
182                 char mb[strlen(method->name) + 8];
183                 sprintf(mb, "%s.atomic", method->name);
184                 free(method->name);
185                 method->name = strdup(mb);
186                 method->options |= OSRF_METHOD_STREAMING;
187         }
188
189         return method;
190 }
191
192
193 int __osrfAppRegisterSysMethods( char* app ) {
194
195         osrfAppRegisterMethod( 
196                         app, OSRF_SYSMETHOD_INTROSPECT, NULL, 
197                         "Return a list of methods whose names have the same initial "
198                         "substring as that of the provided method name PARAMS( methodNameSubstring )", 
199                         1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
200
201         osrfAppRegisterMethod( 
202                         app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL, 
203                         "Returns a complete list of methods. PARAMS()", 0, 
204                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
205
206         osrfAppRegisterMethod( 
207                         app, OSRF_SYSMETHOD_ECHO, NULL, 
208                         "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0, 
209                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
210
211         return 0;
212 }
213
214 osrfApplication* _osrfAppFindApplication( char* name ) {
215         if(!name) return NULL;
216         return (osrfApplication*) osrfHashGet(__osrfAppHash, name);
217 }
218
219 osrfMethod* __osrfAppFindMethod( osrfApplication* app, char* methodName ) {
220         if(!app || ! methodName) return NULL;
221         return (osrfMethod*) osrfHashGet( app->methods, methodName );
222 }
223
224 osrfMethod* _osrfAppFindMethod( char* appName, char* methodName ) {
225         if(!appName || ! methodName) return NULL;
226         return __osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
227 }
228
229
230 int osrfAppRunMethod( char* appName, char* methodName, 
231                 osrfAppSession* ses, int reqId, jsonObject* params ) {
232
233         if( !(appName && methodName && ses) ) return -1;
234
235         char* error;
236         osrfApplication* app;
237         osrfMethod* method;
238         osrfMethodContext context;
239
240         context.session = ses;
241         context.params = params;
242         context.request = reqId;
243         context.responses = NULL;
244
245         /* this is the method we're gonna run */
246         int (*meth) (osrfMethodContext*);       
247
248         if( !(app = _osrfAppFindApplication(appName)) )
249                 return osrfAppRequestRespondException( ses, 
250                                 reqId, "Application not found: %s", appName );
251         
252         if( !(method = __osrfAppFindMethod( app, methodName )) ) 
253                 return osrfAppRequestRespondException( ses, reqId, 
254                                 "Method [%s] not found for service %s", methodName, appName );
255
256         context.method = method;
257
258         #ifdef OSRF_STRICT_PARAMS
259         if( method->argc > 0 ) {
260                 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
261                         return osrfAppRequestRespondException( ses, reqId, 
262                                 "Not enough params for method %s / service %s", methodName, appName );
263         }
264         #endif
265
266         int retcode = 0;
267
268         if( method->options & OSRF_METHOD_SYSTEM ) {
269                 retcode = __osrfAppRunSystemMethod(&context);
270
271         } else {
272
273                 /* open and now run the method */
274                 *(void **) (&meth) = dlsym(app->handle, method->symbol);
275
276                 if( (error = dlerror()) != NULL ) {
277                         return osrfAppRequestRespondException( ses, reqId, 
278                                 "Unable to execute method [%s]  for service %s", methodName, appName );
279                 }
280
281                 retcode = (*meth) (&context);
282         }
283
284         if(retcode < 0) 
285                 return osrfAppRequestRespondException( 
286                                 ses, reqId, "An unknown server error occurred" );
287
288         return __osrfAppPostProcess( &context, retcode );
289
290 }
291
292
293 int osrfAppRespond( osrfMethodContext* ctx, jsonObject* data ) {
294         return _osrfAppRespond( ctx, data, 0 );
295 }
296
297 int osrfAppRespondComplete( osrfMethodContext* context, jsonObject* data ) {
298         return _osrfAppRespond( context, data, 1 );
299 }
300
301 int _osrfAppRespond( osrfMethodContext* ctx, jsonObject* data, int complete ) {
302         if(!(ctx && ctx->method)) return -1;
303
304         if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
305                 osrfLogDebug( OSRF_LOG_MARK,   
306                         "Adding responses to stash for atomic method %s", ctx->method->name );
307
308                 if( ctx->responses == NULL )                                                                                            
309                         ctx->responses = jsonParseString("[]");                                                 
310
311                 if ( data != NULL )
312                         jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
313         }
314
315
316         if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
317                         !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
318
319                 if(complete) 
320                         osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
321                 else
322                         osrfAppRequestRespond( ctx->session, ctx->request, data );
323                 return 0;
324         }
325
326         return 0;
327 }
328
329
330
331
332 int __osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
333         if(!(ctx && ctx->method)) return -1;
334
335         osrfLogDebug( OSRF_LOG_MARK,  "Postprocessing method %s with retcode %d",
336                         ctx->method->name, retcode );
337
338         if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
339
340                 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
341                 jsonObjectFree(ctx->responses);
342                 ctx->responses = NULL;
343
344         } else {
345
346                 if( retcode > 0 ) 
347                         osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
348                                         "osrfConnectStatus", ctx->request, "Request Complete" );
349         }
350
351         return 0;
352 }
353
354 int osrfAppRequestRespondException( osrfAppSession* ses, int request, char* msg, ... ) {
355         if(!ses) return -1;
356         if(!msg) msg = "";
357         VA_LIST_TO_STRING(msg);
358         osrfLogWarning( OSRF_LOG_MARK,  "Returning method exception with message: %s", VA_BUF );
359         osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request,  VA_BUF );
360         return 0;
361 }
362
363
364 static void __osrfAppSetIntrospectMethod( osrfMethodContext* ctx, osrfMethod* method, jsonObject* resp ) {
365         if(!(ctx && resp)) return;
366
367         jsonObjectSetKey(resp, "api_name",      jsonNewObject(method->name));
368         jsonObjectSetKey(resp, "method",                jsonNewObject(method->symbol));
369         jsonObjectSetKey(resp, "service",       jsonNewObject(ctx->session->remote_service));
370         jsonObjectSetKey(resp, "notes",         jsonNewObject(method->notes));
371         jsonObjectSetKey(resp, "argc",          jsonNewNumberObject(method->argc));
372
373         jsonObjectSetKey(resp, "sysmethod", 
374                         jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
375         jsonObjectSetKey(resp, "atomic",                
376                         jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
377         jsonObjectSetKey(resp, "cachable",      
378                         jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
379
380         jsonObjectSetClass(resp, "method");
381 }
382
383
384
385 int __osrfAppRunSystemMethod(osrfMethodContext* ctx) {
386         OSRF_METHOD_VERIFY_CONTEXT(ctx);
387
388         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
389                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
390
391                 return osrfAppIntrospectAll(ctx);
392         }
393
394
395         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
396                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
397
398                 return osrfAppIntrospect(ctx);
399         }
400
401         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO ) ||
402                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_ECHO_ATOMIC )) {
403
404                 return osrfAppEcho(ctx);
405         }
406
407
408         osrfAppRequestRespondException( ctx->session, 
409                         ctx->request, "System method implementation not found");
410
411         return 0;
412 }
413
414
415 int osrfAppIntrospect( osrfMethodContext* ctx ) {
416
417         jsonObject* resp = NULL;
418         char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
419         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
420         int len = 0;
421
422         if(!methodSubstring) return 1; /* respond with no methods */
423
424         if(app) {
425
426                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
427                 osrfMethod* method;
428
429                 while( (method = osrfHashIteratorNext(itr)) ) {
430                         if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
431                                 if( !strncmp( method->name, methodSubstring, len) ) {
432                                         resp = jsonNewObject(NULL);
433                                         __osrfAppSetIntrospectMethod( ctx, method, resp );
434                                         osrfAppRespond(ctx, resp);
435                                         jsonObjectFree(resp);
436                                 }
437                         }
438                 }
439                 osrfHashIteratorFree(itr);
440                 return 1;
441         }
442
443         return -1;
444
445 }
446
447
448 int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
449         jsonObject* resp = NULL;
450         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
451
452         if(app) {
453                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
454                 osrfMethod* method;
455                 while( (method = osrfHashIteratorNext(itr)) ) {
456                         resp = jsonNewObject(NULL);
457                         __osrfAppSetIntrospectMethod( ctx, method, resp );
458                         osrfAppRespond(ctx, resp);
459                         jsonObjectFree(resp);
460                 }
461                 osrfHashIteratorFree(itr);
462                 return 1;
463         }
464
465         return -1;
466 }
467
468 int osrfAppEcho( osrfMethodContext* ctx ) {
469         OSRF_METHOD_VERIFY_CONTEXT(ctx);
470         int i;
471         for( i = 0; i < ctx->params->size; i++ ) {
472                 jsonObject* str = jsonObjectGetIndex(ctx->params,i);
473                 osrfAppRespond(ctx, str);
474         }
475         return 1;
476 }
477