3 @brief As a server, perform database operations at the request of clients.
10 #include "opensrf/utils.h"
11 #include "opensrf/log.h"
12 #include "opensrf/osrf_application.h"
13 #include "openils/oils_utils.h"
14 #include "openils/oils_sql.h"
16 static dbi_conn writehandle; /* our MASTER db connection */
17 static dbi_conn dbhandle; /* our CURRENT db connection */
18 //static osrfHash * readHandles;
20 static int max_flesh_depth = 100;
22 static const int enforce_pcrud = 0; // Boolean
23 static const char modulename[] = "open-ils.cstore";
26 @brief Disconnect from the database.
28 This function is called when the server drone is about to terminate.
30 void osrfAppChildExit() {
31 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
34 if (writehandle == dbhandle)
38 dbi_conn_query(writehandle, "ROLLBACK;");
39 dbi_conn_close(writehandle);
42 if (dbhandle && !same)
43 dbi_conn_close(dbhandle);
45 // XXX add cleanup of readHandles whenever that gets used
51 @brief Initialize the application.
52 @return Zero if successful, or non-zero if not.
54 Load the IDL file into an internal data structure for future reference. Each non-virtual
55 class in the IDL corresponds to a table or view in the database, or to a subquery defined
56 in the IDL. Ignore all virtual tables and virtual fields.
58 Register a number of methods, some of them general-purpose and others specific for
61 The name of the application is given by the MODULENAME macro, whose value depends on
62 conditional compilation. The method names also incorporate MODULENAME, followed by a
65 The general-purpose methods are as follows (minus their MODULENAME prefixes):
70 - transaction.rollback
75 For each non-virtual class, create up to eight class-specific methods:
77 - create (not for readonly classes)
79 - update (not for readonly classes)
80 - delete (not for readonly classes
81 - search (atomic and non-atomic versions)
82 - id_list (atomic and non-atomic versions)
84 The full method names follow the pattern "MODULENAME.direct.XXX.method_type", where XXX
85 is the fieldmapper name from the IDL, with every run of one or more consecutive colons
86 replaced by a period. In addition, the names of atomic methods have a suffix of ".atomic".
88 This function is called when the registering the application, and is executed by the
89 listener before spawning the drones.
91 int osrfAppInitialize() {
93 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
94 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
96 if (!oilsIDLInit( osrf_settings_host_value("/IDL") ))
97 return 1; /* return non-zero to indicate error */
99 oilsSetSQLOptions( modulename, enforce_pcrud );
101 growing_buffer* method_name = buffer_init(64);
103 // Generic search thingy
104 buffer_add( method_name, modulename );
105 buffer_add( method_name, ".json_query" );
106 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
107 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
109 // first we register all the transaction and savepoint methods
110 buffer_reset(method_name);
111 OSRF_BUFFER_ADD(method_name, modulename );
112 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
113 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
114 "beginTransaction", "", 0, 0 );
116 buffer_reset(method_name);
117 OSRF_BUFFER_ADD(method_name, modulename );
118 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
119 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
120 "commitTransaction", "", 0, 0 );
122 buffer_reset(method_name);
123 OSRF_BUFFER_ADD(method_name, modulename );
124 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
125 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
126 "rollbackTransaction", "", 0, 0 );
128 buffer_reset(method_name);
129 OSRF_BUFFER_ADD(method_name, modulename );
130 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
131 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
132 "setSavepoint", "", 1, 0 );
134 buffer_reset(method_name);
135 OSRF_BUFFER_ADD(method_name, modulename );
136 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
137 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
138 "releaseSavepoint", "", 1, 0 );
140 buffer_reset(method_name);
141 OSRF_BUFFER_ADD(method_name, modulename );
142 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
143 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
144 "rollbackSavepoint", "", 1, 0 );
146 static const char* global_method[] = {
154 const int global_method_count
155 = sizeof( global_method ) / sizeof ( global_method[0] );
157 unsigned long class_count = osrfHashGetCount( oilsIDL() );
158 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
159 osrfLogDebug(OSRF_LOG_MARK,
160 "At most %lu methods will be generated",
161 (unsigned long) (class_count * global_method_count) );
163 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
164 osrfHash* idlClass = NULL;
166 // For each class in the IDL...
167 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
169 const char* classname = osrfHashIteratorKey( class_itr );
170 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
172 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
173 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
174 modulename, classname);
178 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
179 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
183 // Look up some other attributes of the current class
184 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
185 if( !idlClass_fieldmapper ) {
186 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
191 const char* readonly = osrfHashGet(idlClass, "readonly");
194 for( i = 0; i < global_method_count; ++i ) { // for each global method
195 const char* method_type = global_method[ i ];
196 osrfLogDebug(OSRF_LOG_MARK,
197 "Using files to build %s class methods for %s", method_type, classname);
199 // No create, update, or delete methods for a readonly class
200 if ( str_is_true( readonly )
201 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
204 buffer_reset( method_name );
206 // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
207 // where XXX is the fieldmapper name from the IDL, with every run of
208 // one or more consecutive colons replaced by a period.
211 char* _fm = strdup( idlClass_fieldmapper );
212 part = strtok_r(_fm, ":", &st_tmp);
214 buffer_fadd(method_name, "%s.direct.%s", modulename, part);
216 while ((part = strtok_r(NULL, ":", &st_tmp))) {
217 OSRF_BUFFER_ADD_CHAR(method_name, '.');
218 OSRF_BUFFER_ADD(method_name, part);
220 OSRF_BUFFER_ADD_CHAR(method_name, '.');
221 OSRF_BUFFER_ADD(method_name, method_type);
224 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
225 // The consequence is that we implicitly create an atomic method in addition to
226 // the usual non-atomic method.
228 if (*method_type == 'i' || *method_type == 's') { // id_list or search
229 flags = flags | OSRF_METHOD_STREAMING;
232 osrfHash* method_meta = osrfNewHash();
233 osrfHashSet( method_meta, idlClass, "class");
234 osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
235 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
237 // Register the method, with a pointer to an osrfHash to tell the method
238 // its name, type, and class.
239 osrfAppRegisterExtendedMethod(
241 OSRF_BUFFER_C_STR( method_name ),
242 "dispatchCRUDMethod",
249 } // end for each global method
250 } // end for each class in IDL
252 buffer_free( method_name );
253 osrfHashIteratorFree( class_itr );
259 @brief Initialize a server drone.
260 @return Zero if successful, -1 if not.
262 Connect to the database. For each non-virtual class in the IDL, execute a dummy "SELECT * "
263 query to get the datatype of each column. Record the datatypes in the loaded IDL.
265 This function is called by a server drone shortly after it is spawned by the listener.
267 int osrfAppChildInit() {
269 osrfLogDebug(OSRF_LOG_MARK, "Attempting to initialize libdbi...");
270 dbi_initialize(NULL);
271 osrfLogDebug(OSRF_LOG_MARK, "... libdbi initialized.");
273 char* driver = osrf_settings_host_value("/apps/%s/app_settings/driver", modulename );
274 char* user = osrf_settings_host_value("/apps/%s/app_settings/database/user", modulename );
275 char* host = osrf_settings_host_value("/apps/%s/app_settings/database/host", modulename );
276 char* port = osrf_settings_host_value("/apps/%s/app_settings/database/port", modulename );
277 char* db = osrf_settings_host_value("/apps/%s/app_settings/database/db", modulename );
278 char* pw = osrf_settings_host_value("/apps/%s/app_settings/database/pw", modulename );
279 char* md = osrf_settings_host_value("/apps/%s/app_settings/max_query_recursion",
282 osrfLogDebug(OSRF_LOG_MARK, "Attempting to load the database driver [%s]...", driver);
283 writehandle = dbi_conn_new(driver);
286 osrfLogError(OSRF_LOG_MARK, "Error loading database driver [%s]", driver);
289 osrfLogDebug(OSRF_LOG_MARK, "Database driver [%s] seems OK", driver);
291 osrfLogInfo(OSRF_LOG_MARK, "%s connecting to database. host=%s, "
292 "port=%s, user=%s, db=%s", modulename, host, port, user, db );
294 if(host) dbi_conn_set_option(writehandle, "host", host );
295 if(port) dbi_conn_set_option_numeric( writehandle, "port", atoi(port) );
296 if(user) dbi_conn_set_option(writehandle, "username", user);
297 if(pw) dbi_conn_set_option(writehandle, "password", pw );
298 if(db) dbi_conn_set_option(writehandle, "dbname", db );
300 if(md) max_flesh_depth = atoi(md);
301 if(max_flesh_depth < 0) max_flesh_depth = 1;
302 if(max_flesh_depth > 1000) max_flesh_depth = 1000;
311 if (dbi_conn_connect(writehandle) < 0) {
313 if (dbi_conn_connect(writehandle) < 0) {
314 dbi_conn_error(writehandle, &err);
315 osrfLogError( OSRF_LOG_MARK, "Error connecting to database: %s", err);
320 oilsSetDBConnection( writehandle );
321 osrfLogInfo(OSRF_LOG_MARK, "%s successfully connected to the database", modulename );
323 // Add datatypes from database to the fields in the IDL
324 if( oilsExtendIDL() ) {
325 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
333 @brief Implement the class-specific methods.
334 @param ctx Pointer to the method context.
335 @return Zero if successful, or -1 if not.
337 Branch on the method type: create, retrieve, update, delete, search, or id_list.
339 The method parameters and the type of value returned to the client depend on the method
342 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
344 // Get the method type, then can branch on it
345 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
346 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
348 if( !strcmp( methodtype, "create" ))
349 return doCreate( ctx );
350 else if( !strcmp(methodtype, "retrieve" ))
351 return doRetrieve( ctx );
352 else if( !strcmp(methodtype, "update" ))
353 return doUpdate( ctx );
354 else if( !strcmp(methodtype, "delete" ))
355 return doDelete( ctx );
356 else if( !strcmp(methodtype, "search" ))
357 return doSearch( ctx );
358 else if( !strcmp(methodtype, "id_list" ))
359 return doIdList( ctx );
361 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...