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