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