]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libstack/osrf_application.c
moved C code to a unified logging framework which currently supports syslogging and...
[OpenSRF.git] / src / libstack / osrf_application.c
1 #include "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("Registering application %s with file %s", appName, soFile );
16
17         osrfApplication* app = safe_malloc(sizeof(osrfApplication));
18         app->handle = dlopen (soFile, RTLD_NOW);
19
20         if(!app->handle) {
21                 osrfLogWarning("Failed to dlopen library file %s: %s", soFile, dlerror() );
22                 dlerror(); /* clear the error */
23                 free(app);
24                 return -1;
25         }
26
27         app->methods = osrfNewHash();
28         osrfHashSet( __osrfAppHash, app, appName );
29
30         /* see if we can run the initialize method */
31         int (*init) (void);
32         *(void **) (&init) = dlsym(app->handle, "osrfAppInitialize");
33
34         if( (error = dlerror()) != NULL ) {
35                 osrfLogWarning("! Unable to locate method symbol [osrfAppInitialize] for app %s: %s", appName, error );
36
37         } else {
38
39                 /* run the method */
40                 int ret;
41                 if( (ret = (*init)()) ) {
42                         osrfLogWarning("Application %s returned non-zero value from "
43                                         "'osrfAppInitialize', not registering...", appName );
44                         //free(app->name); /* need a method to remove an application from the list */
45                         //free(app);
46                         return ret;
47                 }
48         }
49
50         __osrfAppRegisterSysMethods(appName);
51
52         osrfLogInfo("Application %s registered successfully", appName );
53
54         osrfLogSetAppname(appName);
55
56         return 0;
57 }
58
59
60 int osrfAppRegisterMethod( char* appName, char* methodName, 
61                 char* symbolName, char* notes, int argc, int options ) {
62
63         if( !appName || ! methodName  ) return -1;
64
65         osrfApplication* app = _osrfAppFindApplication(appName);
66         if(!app) {
67                 osrfLogWarning("Unable to locate application %s", appName );
68                 return -1;
69         }
70
71         osrfLogDebug("Registering method %s for app %s", methodName, appName );
72
73         osrfMethod* method = _osrfAppBuildMethod(
74                 methodName, symbolName, notes, argc, options );         
75         method->options = options;
76
77         /* plug the method into the list of methods */
78         osrfHashSet( app->methods, method, method->name );
79
80         if( options & OSRF_METHOD_STREAMING ) { /* build the atomic counterpart */
81                 int newops = options | OSRF_METHOD_ATOMIC;
82                 osrfMethod* atomicMethod = _osrfAppBuildMethod(
83                         methodName, symbolName, notes, argc, newops );          
84                 osrfHashSet( app->methods, atomicMethod, atomicMethod->name );
85         }
86
87         return 0;
88 }
89
90
91
92 osrfMethod* _osrfAppBuildMethod( char* methodName, 
93         char* symbolName, char* notes, int argc, int options ) {
94
95         osrfMethod* method                                      = safe_malloc(sizeof(osrfMethod));
96
97         if(methodName) method->name             = strdup(methodName);
98         if(symbolName) method->symbol           = strdup(symbolName);
99         if(notes) method->notes                         = strdup(notes);
100
101         method->argc                                                    = argc;
102         method->options                                         = options;
103
104         if(options & OSRF_METHOD_ATOMIC) { /* add ".atomic" to the end of the name */
105                 char mb[strlen(method->name) + 8];
106                 sprintf(mb, "%s.atomic", method->name);
107                 free(method->name);
108                 method->name = strdup(mb);
109                 method->options |= OSRF_METHOD_STREAMING;
110         }
111
112         return method;
113 }
114
115
116 int __osrfAppRegisterSysMethods( char* app ) {
117
118         osrfAppRegisterMethod( 
119                         app, OSRF_SYSMETHOD_INTROSPECT, NULL, 
120                         "Return a list of methods whose names have the same initial "
121                         "substring as that of the provided method name PARAMS( methodNameSubstring )", 
122                         1, OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
123
124         osrfAppRegisterMethod( 
125                         app, OSRF_SYSMETHOD_INTROSPECT_ALL, NULL, 
126                         "Returns a complete list of methods. PARAMS()", 0, 
127                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
128
129         osrfAppRegisterMethod( 
130                         app, OSRF_SYSMETHOD_ECHO, NULL, 
131                         "Echos all data sent to the server back to the client. PARAMS([a, b, ...])", 0, 
132                         OSRF_METHOD_SYSTEM | OSRF_METHOD_STREAMING );
133
134         return 0;
135 }
136
137 osrfApplication* _osrfAppFindApplication( char* name ) {
138         if(!name) return NULL;
139         return (osrfApplication*) osrfHashGet(__osrfAppHash, name);
140 }
141
142 osrfMethod* __osrfAppFindMethod( osrfApplication* app, char* methodName ) {
143         if(!app || ! methodName) return NULL;
144         return (osrfMethod*) osrfHashGet( app->methods, methodName );
145 }
146
147 osrfMethod* _osrfAppFindMethod( char* appName, char* methodName ) {
148         if(!appName || ! methodName) return NULL;
149         return __osrfAppFindMethod( _osrfAppFindApplication(appName), methodName );
150 }
151
152
153 int osrfAppRunMethod( char* appName, char* methodName, 
154                 osrfAppSession* ses, int reqId, jsonObject* params ) {
155
156         if( !(appName && methodName && ses) ) return -1;
157
158         char* error;
159         osrfApplication* app;
160         osrfMethod* method;
161         osrfMethodContext context;
162
163         context.session = ses;
164         context.params = params;
165         context.request = reqId;
166         context.responses = NULL;
167
168         /* this is the method we're gonna run */
169         int (*meth) (osrfMethodContext*);       
170
171         osrfLogInfo("Running method [%s] for app [%s] with request id %d and "
172                         "thread trace %s", methodName, appName, reqId, ses->session_id );
173
174         if( !(app = _osrfAppFindApplication(appName)) )
175                 return osrfAppRequestRespondException( ses, 
176                                 reqId, "Application not found: %s", appName );
177         
178         if( !(method = __osrfAppFindMethod( app, methodName )) ) 
179                 return osrfAppRequestRespondException( ses, reqId, 
180                                 "Method [%s] not found for service %s", methodName, appName );
181
182         context.method = method;
183
184         #ifdef OSRF_STRICT_PARAMS
185         if( method->argc > 0 ) {
186                 if(!params || params->type != JSON_ARRAY || params->size < method->argc )
187                         return osrfAppRequestRespondException( ses, reqId, 
188                                 "Not enough params for method %s / service %s", methodName, appName );
189         }
190         #endif
191
192         int retcode = 0;
193
194         if( method->options & OSRF_METHOD_SYSTEM ) {
195                 retcode = __osrfAppRunSystemMethod(&context);
196
197         } else {
198
199                 /* open and now run the method */
200                 *(void **) (&meth) = dlsym(app->handle, method->symbol);
201
202                 if( (error = dlerror()) != NULL ) {
203                         return osrfAppRequestRespondException( ses, reqId, 
204                                 "Unable to execute method [%s]  for service %s", methodName, appName );
205                 }
206
207                 retcode = (*meth) (&context);
208         }
209
210         if(retcode < 0) 
211                 return osrfAppRequestRespondException( 
212                                 ses, reqId, "An unknown server error occurred" );
213
214         return __osrfAppPostProcess( &context, retcode );
215
216 }
217
218
219 int osrfAppRespond( osrfMethodContext* ctx, jsonObject* data ) {
220         return _osrfAppRespond( ctx, data, 0 );
221 }
222
223 int osrfAppRespondComplete( osrfMethodContext* context, jsonObject* data ) {
224         return _osrfAppRespond( context, data, 1 );
225 }
226
227 int _osrfAppRespond( osrfMethodContext* ctx, jsonObject* data, int complete ) {
228         if(!(ctx && ctx->method)) return -1;
229
230         if( ctx->method->options & OSRF_METHOD_ATOMIC ) {
231                 osrfLogDebug(  
232                         "Adding responses to stash for atomic method %s", ctx->method );
233
234                 if( ctx->responses == NULL )                                                                                            
235                         ctx->responses = jsonParseString("[]");                                                 
236                 jsonObjectPush( ctx->responses, jsonObjectClone(data) );        
237         }
238
239
240         if(     !(ctx->method->options & OSRF_METHOD_ATOMIC) && 
241                         !(ctx->method->options & OSRF_METHOD_CACHABLE) ) {
242
243                 if(complete) 
244                         osrfAppRequestRespondComplete( ctx->session, ctx->request, data );
245                 else
246                         osrfAppRequestRespond( ctx->session, ctx->request, data );
247                 return 0;
248         }
249
250         return 0;
251 }
252
253
254
255
256 int __osrfAppPostProcess( osrfMethodContext* ctx, int retcode ) {
257         if(!(ctx && ctx->method)) return -1;
258
259         osrfLogDebug( "Postprocessing method %s with retcode %d",
260                         ctx->method->name, retcode );
261
262         if(ctx->responses) { /* we have cached responses to return (no responses have been sent) */
263
264                 osrfAppRequestRespondComplete( ctx->session, ctx->request, ctx->responses );
265                 jsonObjectFree(ctx->responses);
266                 ctx->responses = NULL;
267
268         } else {
269
270                 if( retcode > 0 ) 
271                         osrfAppSessionStatus( ctx->session, OSRF_STATUS_COMPLETE,  
272                                         "osrfConnectStatus", ctx->request, "Request Complete" );
273         }
274
275         return 0;
276
277 }
278
279 int osrfAppRequestRespondException( osrfAppSession* ses, int request, char* msg, ... ) {
280         if(!ses) return -1;
281         if(!msg) msg = "";
282         VA_LIST_TO_STRING(msg);
283         osrfLogWarning( "Returning method exception with message: %s", VA_BUF );
284         osrfAppSessionStatus( ses, OSRF_STATUS_NOTFOUND, "osrfMethodException", request,  VA_BUF );
285         return 0;
286 }
287
288
289 static void __osrfAppSetIntrospectMethod( osrfMethodContext* ctx, osrfMethod* method, jsonObject* resp ) {
290         if(!(ctx && resp)) return;
291
292         jsonObjectSetKey(resp, "api_name",      jsonNewObject(method->name));
293         jsonObjectSetKey(resp, "method",                jsonNewObject(method->symbol));
294         jsonObjectSetKey(resp, "service",       jsonNewObject(ctx->session->remote_service));
295         jsonObjectSetKey(resp, "notes",         jsonNewObject(method->notes));
296         jsonObjectSetKey(resp, "argc",          jsonNewNumberObject(method->argc));
297
298         jsonObjectSetKey(resp, "sysmethod", 
299                         jsonNewNumberObject( (method->options & OSRF_METHOD_SYSTEM) ? 1 : 0 ));
300         jsonObjectSetKey(resp, "atomic",                
301                         jsonNewNumberObject( (method->options & OSRF_METHOD_ATOMIC) ? 1 : 0 ));
302         jsonObjectSetKey(resp, "cachable",      
303                         jsonNewNumberObject( (method->options & OSRF_METHOD_CACHABLE) ? 1 : 0 ));
304
305         jsonObjectSetClass(resp, "method");
306 }
307
308
309
310 int __osrfAppRunSystemMethod(osrfMethodContext* ctx) {
311         OSRF_METHOD_VERIFY_CONTEXT(ctx);
312
313         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL ) || 
314                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ALL_ATOMIC )) {
315
316                 return osrfAppIntrospectAll(ctx);
317         }
318
319
320         if(     !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT ) ||
321                         !strcmp(ctx->method->name, OSRF_SYSMETHOD_INTROSPECT_ATOMIC )) {
322
323                 return osrfAppIntrospect(ctx);
324         }
325
326         osrfAppRequestRespondException( ctx->session, 
327                         ctx->request, "System method implementation not found");
328
329         return 0;
330 }
331
332
333 int osrfAppIntrospect( osrfMethodContext* ctx ) {
334
335         jsonObject* resp = NULL;
336         char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
337         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
338         int len = 0;
339
340         if(!methodSubstring) return 1; /* respond with no methods */
341
342         if(app) {
343
344                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
345                 osrfMethod* method;
346
347                 while( (method = osrfHashIteratorNext(itr)) ) {
348                         if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
349                                 if( !strncmp( method->name, methodSubstring, len) ) {
350                                         resp = jsonNewObject(NULL);
351                                         __osrfAppSetIntrospectMethod( ctx, method, resp );
352                                         osrfAppRespond(ctx, resp);
353                                         jsonObjectFree(resp);
354                                 }
355                         }
356                 }
357                 osrfHashIteratorFree(itr);
358                 return 1;
359         }
360
361         return -1;
362
363 }
364
365
366 int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
367         jsonObject* resp = NULL;
368         osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
369
370         if(app) {
371                 osrfHashIterator* itr = osrfNewHashIterator(app->methods);
372                 osrfMethod* method;
373                 while( (method = osrfHashIteratorNext(itr)) ) {
374                         resp = jsonNewObject(NULL);
375                         __osrfAppSetIntrospectMethod( ctx, method, resp );
376                         osrfAppRespond(ctx, resp);
377                         jsonObjectFree(resp);
378                 }
379                 osrfHashIteratorFree(itr);
380                 return 1;
381         }
382
383         return -1;
384 }
385
386