1 #include "opensrf/osrf_application.h"
2 #include "opensrf/osrf_settings.h"
3 #include "opensrf/utils.h"
4 #include "objson/object.h"
5 #include "opensrf/log.h"
6 #include "oils_utils.h"
7 #include "oils_constants.h"
8 #include "oils_event.h"
14 #include <libxml/globals.h>
15 #include <libxml/xmlerror.h>
16 #include <libxml/parser.h>
17 #include <libxml/tree.h>
18 #include <libxml/debugXML.h>
19 #include <libxml/xmlmemory.h>
21 #define OILS_AUTH_CACHE_PRFX "oils_cstore_"
22 #define MODULENAME "open-ils.cstore"
23 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistance/v1"
24 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
25 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
27 int osrfAppChildInit();
28 int osrfAppInitialize();
30 int dispatchCRUDMethod ( osrfMethodContext* );
31 jsonObject* doCreate ( osrfHash*, jsonObject* );
32 jsonObject* doRetrieve ( osrfHash*, jsonObject* );
33 jsonObject* doUpdate ( osrfHash*, jsonObject* );
34 jsonObject* doDelete ( osrfHash*, jsonObject* );
35 jsonObject* oilsMakeJSONFromResult( dbi_result, osrfHash* );
37 dbi_conn dbhandle; /* our db connection */
38 xmlDocPtr idlDoc = NULL; // parse and store the IDL here
41 /* parse and store the IDL here */
44 int osrfAppInitialize() {
46 idlHash = osrfNewHash();
48 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
49 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
51 char * idl_filename = osrf_settings_host_value("/apps/%s/app_settings/IDL", MODULENAME);
52 osrfLogInfo(OSRF_LOG_MARK, "Found file:");
53 osrfLogInfo(OSRF_LOG_MARK, idl_filename);
55 osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
56 idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
59 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
63 osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
65 osrfStringArray* global_methods = osrfNewStringArray(1);
67 //osrfStringArrayAdd( global_methods, "create" );
68 osrfStringArrayAdd( global_methods, "retrieve" );
69 //osrfStringArrayAdd( global_methods, "update" );
70 //osrfStringArrayAdd( global_methods, "delete" );
72 xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
73 xmlNodePtr kid = docRoot->children;
75 if (!strcmp( (char*)kid->name, "class" )) {
78 while ( (method_type = osrfStringArrayGetString(global_methods, i++)) ) {
80 osrfHash * usrData = osrfNewHash();
81 osrfHashSet( usrData, xmlGetProp(kid, "id"), "classname");
82 osrfHashSet( usrData, xmlGetNsProp(kid, "tablename", PERSIST_NS), "tablename");
83 osrfHashSet( usrData, xmlGetNsProp(kid, "fieldmapper", OBJECT_NS), "fieldmapper");
85 if(!strcmp(method_type, "retrieve"))
86 osrfHashSet( idlHash, usrData, (char*)osrfHashGet(usrData, "classname") );
88 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", osrfHashGet(usrData, "fieldmapper") );
91 osrfHash* links = osrfNewHash();
92 osrfHash* fields = osrfNewHash();
94 osrfHashSet( usrData, fields, "fields" );
95 osrfHashSet( usrData, links, "links" );
97 xmlNodePtr _cur = kid->children;
100 char* string_tmp = NULL;
102 if (!strcmp( (char*)_cur->name, "fields" )) {
104 if( (string_tmp = (char*)xmlGetNsProp(_cur, "primary", PERSIST_NS)) ) {
107 strdup( string_tmp ),
113 xmlNodePtr _f = _cur->children;
116 if (strcmp( (char*)_f->name, "field" )) {
121 _tmp = osrfNewHash();
123 if( (string_tmp = (char*)xmlGetNsProp(_f, "array_position", OBJECT_NS)) ) {
126 strdup( string_tmp ),
132 if( (string_tmp = (char*)xmlGetNsProp(_f, "virtual", PERSIST_NS)) ) {
135 strdup( string_tmp ),
141 if( (string_tmp = (char*)xmlGetProp(_f, "name")) ) {
144 strdup( string_tmp ),
149 osrfLogInfo(OSRF_LOG_MARK, "Found field %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
160 if (!strcmp( (char*)_cur->name, "links" )) {
161 xmlNodePtr _l = _cur->children;
164 if (strcmp( (char*)_l->name, "link" )) {
169 _tmp = osrfNewHash();
171 if( (string_tmp = (char*)xmlGetProp(_l, "reltype")) ) {
174 strdup( string_tmp ),
178 osrfLogInfo(OSRF_LOG_MARK, "Adding link with reltype %s", string_tmp );
181 if( (string_tmp = (char*)xmlGetProp(_l, "key")) ) {
184 strdup( string_tmp ),
188 osrfLogInfo(OSRF_LOG_MARK, "Link fkey is %s", string_tmp );
191 if( (string_tmp = (char*)xmlGetProp(_l, "class")) ) {
194 strdup( string_tmp ),
198 osrfLogInfo(OSRF_LOG_MARK, "Link fclass is %s", string_tmp );
201 osrfStringArray* map = osrfNewStringArray(0);
203 if( (string_tmp = (char*)xmlGetProp(_l, "map") )) {
204 char* map_list = strdup( string_tmp );
205 osrfLogInfo(OSRF_LOG_MARK, "Link mapping list is %s", string_tmp );
207 if (strlen( map_list ) > 0) {
209 char* _map_class = strtok_r(map_list, " ", &st_tmp);
210 osrfStringArrayAdd(map, strdup(_map_class));
212 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
213 osrfStringArrayAdd(map, strdup(_map_class));
217 osrfHashSet( _tmp, map, "map");
219 if( (string_tmp = (char*)xmlGetProp(_l, "field")) ) {
222 strdup( string_tmp ),
233 osrfLogInfo(OSRF_LOG_MARK, "Found link %s for class %s", string_tmp, osrfHashGet(usrData, "classname") );
245 if (!osrfHashGet(usrData, "fieldmapper")) continue;
247 _fm = strdup( (char*)osrfHashGet(usrData, "fieldmapper") );
248 char* part = strtok_r(_fm, ":", &st_tmp);
250 growing_buffer* method_name = buffer_init(64);
251 buffer_fadd(method_name, "%s.direct.%s", MODULENAME, part);
253 while ((part = strtok_r(NULL, ":", &st_tmp))) {
254 buffer_fadd(method_name, ".%s", part);
256 buffer_fadd(method_name, ".%s", method_type);
258 char* method = buffer_data(method_name);
259 buffer_free(method_name);
261 osrfHashSet( usrData, method, "methodname" );
262 osrfHashSet( usrData, strdup(method_type), "methodtype" );
264 osrfAppRegisterExtendedMethod(
267 "dispatchCRUDMethod",
282 * Connects to the database
284 int osrfAppChildInit() {
286 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
287 dbi_initialize(NULL);
288 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
290 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", MODULENAME);
291 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", MODULENAME);
292 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", MODULENAME);
293 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", MODULENAME);
294 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", MODULENAME);
295 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", MODULENAME);
297 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
298 dbhandle = dbi_conn_new(driver);
301 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
304 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
306 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
307 "port=%s, user=%s, pw=%s, db=%s", MODULENAME, host, port, user, pw, db );
309 if(host) dbi_conn_set_option(dbhandle, "host", host );
310 if(port) dbi_conn_set_option_numeric( dbhandle, "port", atoi(port) );
311 if(user) dbi_conn_set_option(dbhandle, "username", user);
312 if(pw) dbi_conn_set_option(dbhandle, "password", pw );
313 if(db) dbi_conn_set_option(dbhandle, "dbname", db );
322 if (dbi_conn_connect(dbhandle) < 0) {
323 dbi_conn_error(dbhandle, &err);
324 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
328 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", MODULENAME);
334 osrfStringArray* classes = osrfHashKeys( idlHash );
336 while ( (classname = osrfStringArrayGetString(classes, i++)) ) {
337 osrfHash* class = osrfHashGet( idlHash, classname );
338 osrfHash* fields = osrfHashGet( class, "fields" );
340 growing_buffer* sql_buf = buffer_init(32);
341 buffer_fadd( sql_buf, "SELECT * FROM %s WHERE 1=0;", osrfHashGet(class, "tablename") );
343 char* sql = buffer_data(sql_buf);
344 buffer_free(sql_buf);
345 osrfLogDebug(OSRF_LOG_MARK, "%s Investigatory SQL = %s", MODULENAME, sql);
347 dbi_result result = dbi_conn_query(dbhandle, sql);
353 const char* columnName;
355 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
357 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
359 /* fetch the fieldmapper index */
360 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
362 osrfLogDebug(OSRF_LOG_MARK, "Found [%s] in IDL hash...", (char*)columnName);
364 /* determine the field type and storage attributes */
365 type = dbi_result_get_field_type(result, columnName);
366 attr = dbi_result_get_field_attribs(result, columnName);
370 case DBI_TYPE_INTEGER :
372 osrfHashSet(_f,"number", "primitive");
374 if( attr & DBI_INTEGER_SIZE8 )
375 osrfHashSet(_f,"INT8", "datatype");
377 osrfHashSet(_f,"INT", "datatype");
380 case DBI_TYPE_DECIMAL :
381 osrfHashSet(_f,"number", "primitive");
382 osrfHashSet(_f,"NUMERIC", "datatype");
385 case DBI_TYPE_STRING :
386 osrfHashSet(_f,"string", "primitive");
387 osrfHashSet(_f,"TEXT", "datatype");
390 case DBI_TYPE_DATETIME :
391 osrfHashSet(_f,"string", "primitive");
392 osrfHashSet(_f,"TIMESTAMP", "datatype");
395 case DBI_TYPE_BINARY :
396 osrfHashSet(_f,"string", "primitive");
397 osrfHashSet(_f,"BYTEA", "datatype");
402 "Setting [%s] to primitive [%s] and datatype [%s]...",
404 osrfHashGet(_f, "primitive"),
405 osrfHashGet(_f, "datatype")
409 dbi_result_free(result);
411 osrfLogDebug(OSRF_LOG_MARK, "No data found for class [%s]...", (char*)classname);
415 osrfStringArrayFree(classes);
421 int dispatchCRUDMethod ( osrfMethodContext* ctx ) {
422 OSRF_METHOD_VERIFY_CONTEXT(ctx);
424 osrfHash* meta = (osrfHash*) ctx->method->userData;
426 jsonObject * obj = NULL;
427 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "create"))
428 obj = doCreate(meta, ctx->params);
430 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "retrieve"))
431 obj = doRetrieve(meta, ctx->params);
433 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "update"))
434 obj = doUpdate(meta, ctx->params);
436 if (!strcmp( (char*)osrfHashGet(meta, "methodtype"), "delete"))
437 obj = doDelete(meta, ctx->params);
439 osrfAppRespondComplete( ctx, obj );
446 jsonObject* doCreate( osrfHash* meta, jsonObject* params ) { return NULL; }
448 jsonObject* doRetrieve( osrfHash* meta, jsonObject* params ) {
451 char* id = jsonObjectToSimpleString(jsonObjectGetIndex(params, 0));
455 "%s retrieving %s object with id %s",
457 osrfHashGet(meta, "fieldmapper"),
461 char* pkey = osrfHashGet(meta, "primarykey");
462 osrfHash* field = osrfHashGet( osrfHashGet(meta, "fields"), pkey );
465 growing_buffer* sql_buf = buffer_init(128);
466 if ( !strcmp(osrfHashGet(field, "primitive"), "number") ) {
469 "SELECT * FROM %s WHERE %s = %d;",
470 osrfHashGet(meta, "tablename"),
471 osrfHashGet(meta, "primarykey"),
476 if ( dbi_conn_quote_string_copy(dbhandle, id, &key_string) ) {
479 "SELECT * FROM %s WHERE %s = %s;",
480 osrfHashGet(meta, "tablename"),
481 osrfHashGet(meta, "primarykey"),
485 obj = jsonNewObject(NULL);
486 osrfLogDebug(OSRF_LOG_MARK, "%s: Error quoting key string [%s]", MODULENAME, id);
490 char* sql = buffer_data(sql_buf);
491 buffer_free(sql_buf);
493 osrfLogDebug(OSRF_LOG_MARK, "%s SQL = %s", MODULENAME, sql);
495 dbi_result result = dbi_conn_query(dbhandle, sql);
498 osrfLogDebug(OSRF_LOG_MARK, "Query returned with no errors");
500 /* there should be one row at the most */
501 if (dbi_result_first_row(result)) {
502 /* JSONify the result */
503 osrfLogDebug(OSRF_LOG_MARK, "Query returned at least one row");
504 obj = oilsMakeJSONFromResult( result, meta );
506 osrfLogDebug(OSRF_LOG_MARK, "%s returned no results for query %s", MODULENAME, sql);
507 obj = jsonNewObject(NULL);
510 /* clean up the query */
511 dbi_result_free(result);
514 obj = jsonNewObject(NULL);
515 osrfLogDebug(OSRF_LOG_MARK, "%s: Error retrieving %s with key %s", MODULENAME, osrfHashGet(meta, "fieldmapper"), id);
525 jsonObject* doUpdate( osrfHash* meta, jsonObject* params ) { return NULL; }
527 jsonObject* doDelete( osrfHash* meta, jsonObject* params ) { return NULL; }
530 jsonObject* oilsMakeJSONFromResult( dbi_result result, osrfHash* meta) {
531 if(!(result && meta)) return NULL;
533 jsonObject* object = jsonParseString("[]");
534 jsonObjectSetClass(object, osrfHashGet(meta, "classname"));
536 osrfHash* fields = osrfHashGet(meta, "fields");
538 osrfLogDebug(OSRF_LOG_MARK, "Setting object class to %s ", object->classname);
549 const char* columnName;
551 /* cycle through the column list */
552 while( (columnName = dbi_result_get_field_name(result, columnIndex++)) ) {
554 osrfLogDebug(OSRF_LOG_MARK, "Looking for column named [%s]...", (char*)columnName);
556 fmIndex = -1; // reset the position
558 /* determine the field type and storage attributes */
559 type = dbi_result_get_field_type(result, columnName);
560 attr = dbi_result_get_field_attribs(result, columnName);
562 /* fetch the fieldmapper index */
563 if( (_f = osrfHashGet(fields, (char*)columnName)) ) {
564 char* virt = (char*)osrfHashGet(_f, "virtual");
565 char* pos = (char*)osrfHashGet(_f, "array_position");
567 if ( !virt || !pos || !(strcmp( virt, "true" )) ) continue;
569 fmIndex = atoi( pos );
570 osrfLogDebug(OSRF_LOG_MARK, "... Found column at position [%s]...", pos);
573 if (dbi_result_field_is_null(result, columnName)) {
574 jsonObjectSetIndex( object, fmIndex, jsonNewObject(NULL) );
579 case DBI_TYPE_INTEGER :
581 if( attr & DBI_INTEGER_SIZE8 )
582 jsonObjectSetIndex( object, fmIndex,
583 jsonNewNumberObject(dbi_result_get_longlong(result, columnName)));
585 jsonObjectSetIndex( object, fmIndex,
586 jsonNewNumberObject(dbi_result_get_long(result, columnName)));
590 case DBI_TYPE_DECIMAL :
591 jsonObjectSetIndex( object, fmIndex,
592 jsonNewNumberObject(dbi_result_get_double(result, columnName)));
595 case DBI_TYPE_STRING :
601 jsonNewObject( dbi_result_get_string(result, columnName) )
606 case DBI_TYPE_DATETIME :
608 memset(dt_string, '\0', 256);
609 memset(&gmdt, '\0', sizeof(gmdt));
610 memset(&_tmp_dt, '\0', sizeof(_tmp_dt));
612 _tmp_dt = dbi_result_get_datetime(result, columnName);
614 localtime_r( &_tmp_dt, &gmdt );
616 if (!(attr & DBI_DATETIME_DATE)) {
617 strftime(dt_string, 255, "%T", &gmdt);
618 } else if (!(attr & DBI_DATETIME_TIME)) {
619 strftime(dt_string, 255, "%F", &gmdt);
621 /* XXX ARG! My eyes! The goggles, they do nothing! */
623 strftime(dt_string, 255, "%FT%T%z", &gmdt);
626 jsonObjectSetIndex( object, fmIndex, jsonNewObject(dt_string) );
630 case DBI_TYPE_BINARY :
631 osrfLogError( OSRF_LOG_MARK,
632 "Can't do binary at column %s : index %d", columnName, columnIndex - 1);