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 const int enforce_pcrud = 0; // Boolean
21 static const char modulename[] = "open-ils.cstore";
24 @brief Disconnect from the database.
26 This function is called when the server drone is about to terminate.
28 void osrfAppChildExit( void ) {
29 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
32 if (writehandle == dbhandle)
36 dbi_conn_query(writehandle, "ROLLBACK;");
37 dbi_conn_close(writehandle);
40 if (dbhandle && !same)
41 dbi_conn_close(dbhandle);
43 // XXX add cleanup of readHandles whenever that gets used
49 @brief Initialize the application.
50 @return Zero if successful, or non-zero if not.
52 Load the IDL file into an internal data structure for future reference. Each non-virtual
53 class in the IDL corresponds to a table or view in the database, or to a subquery defined
54 in the IDL. Ignore all virtual tables and virtual fields.
56 Register a number of methods, some of them general-purpose and others specific for
59 The name of the application is given by the MODULENAME macro, whose value depends on
60 conditional compilation. The method names also incorporate MODULENAME, followed by a
63 The general-purpose methods are as follows (minus their MODULENAME prefixes):
68 - transaction.rollback
73 For each non-virtual class, create up to eight class-specific methods:
75 - create (not for readonly classes)
77 - update (not for readonly classes)
78 - delete (not for readonly classes
79 - search (atomic and non-atomic versions)
80 - id_list (atomic and non-atomic versions)
82 The full method names follow the pattern "MODULENAME.direct.XXX.method_type", where XXX
83 is the fieldmapper name from the IDL, with every run of one or more consecutive colons
84 replaced by a period. In addition, the names of atomic methods have a suffix of ".atomic".
86 This function is called when the registering the application, and is executed by the
87 listener before spawning the drones.
89 int osrfAppInitialize( void ) {
91 osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
92 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
94 // Load the IDL into memory
95 if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
96 return 1; /* return non-zero to indicate error */
98 // Open the database temporarily. Look up the datatypes of all
99 // the non-virtual fields and record them with the IDL data.
100 dbi_conn handle = oilsConnectDB( modulename );
103 else if( oilsExtendIDL( handle )) {
104 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
107 dbi_conn_close( handle );
109 // Get the maximum flesh depth from the settings
110 char* md = osrf_settings_host_value(
111 "/apps/%s/app_settings/max_query_recursion", modulename );
112 int max_flesh_depth = 100;
114 max_flesh_depth = atoi( md );
115 if( max_flesh_depth < 0 )
117 else if( max_flesh_depth > 1000 )
118 max_flesh_depth = 1000;
120 oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
122 // Now register all the methods
123 growing_buffer* method_name = buffer_init(64);
125 // Generic search thingy
126 buffer_add( method_name, modulename );
127 buffer_add( method_name, ".json_query" );
128 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
129 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
131 // Next we register all the transaction and savepoint methods
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, modulename );
134 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
135 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
136 "beginTransaction", "", 0, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, modulename );
140 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
141 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
142 "commitTransaction", "", 0, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, modulename );
146 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
147 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
148 "rollbackTransaction", "", 0, 0 );
150 buffer_reset(method_name);
151 OSRF_BUFFER_ADD(method_name, modulename );
152 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
153 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
154 "setSavepoint", "", 1, 0 );
156 buffer_reset(method_name);
157 OSRF_BUFFER_ADD(method_name, modulename );
158 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
159 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
160 "releaseSavepoint", "", 1, 0 );
162 buffer_reset(method_name);
163 OSRF_BUFFER_ADD(method_name, modulename );
164 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
165 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
166 "rollbackSavepoint", "", 1, 0 );
168 static const char* global_method[] = {
176 const int global_method_count
177 = sizeof( global_method ) / sizeof ( global_method[0] );
179 unsigned long class_count = osrfHashGetCount( oilsIDL() );
180 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
181 osrfLogDebug(OSRF_LOG_MARK,
182 "At most %lu methods will be generated",
183 (unsigned long) (class_count * global_method_count) );
185 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
186 osrfHash* idlClass = NULL;
188 // For each class in the IDL...
189 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
191 const char* classname = osrfHashIteratorKey( class_itr );
192 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
194 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
195 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
196 modulename, classname);
200 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
201 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
205 // Look up some other attributes of the current class
206 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
207 if( !idlClass_fieldmapper ) {
208 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
213 const char* readonly = osrfHashGet(idlClass, "readonly");
216 for( i = 0; i < global_method_count; ++i ) { // for each global method
217 const char* method_type = global_method[ i ];
218 osrfLogDebug(OSRF_LOG_MARK,
219 "Using files to build %s class methods for %s", method_type, classname);
221 // No create, update, or delete methods for a readonly class
222 if ( str_is_true( readonly )
223 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
226 buffer_reset( method_name );
228 // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
229 // where XXX is the fieldmapper name from the IDL, with every run of
230 // one or more consecutive colons replaced by a period.
233 char* _fm = strdup( idlClass_fieldmapper );
234 part = strtok_r(_fm, ":", &st_tmp);
236 buffer_fadd(method_name, "%s.direct.%s", modulename, part);
238 while ((part = strtok_r(NULL, ":", &st_tmp))) {
239 OSRF_BUFFER_ADD_CHAR(method_name, '.');
240 OSRF_BUFFER_ADD(method_name, part);
242 OSRF_BUFFER_ADD_CHAR(method_name, '.');
243 OSRF_BUFFER_ADD(method_name, method_type);
246 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
247 // The consequence is that we implicitly create an atomic method in addition to
248 // the usual non-atomic method.
250 if (*method_type == 'i' || *method_type == 's') { // id_list or search
251 flags = flags | OSRF_METHOD_STREAMING;
254 osrfHash* method_meta = osrfNewHash();
255 osrfHashSet( method_meta, idlClass, "class");
256 osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
257 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
259 // Register the method, with a pointer to an osrfHash to tell the method
260 // its name, type, and class.
261 osrfAppRegisterExtendedMethod(
263 OSRF_BUFFER_C_STR( method_name ),
264 "dispatchCRUDMethod",
271 } // end for each global method
272 } // end for each class in IDL
274 buffer_free( method_name );
275 osrfHashIteratorFree( class_itr );
281 @brief Initialize a server drone.
282 @return Zero if successful, -1 if not.
284 Connect to the database.
286 This function is called by a server drone shortly after it is spawned by the listener.
288 int osrfAppChildInit( void ) {
290 writehandle = oilsConnectDB( modulename );
294 oilsSetDBConnection( writehandle );
300 @brief Implement the class-specific methods.
301 @param ctx Pointer to the method context.
302 @return Zero if successful, or -1 if not.
304 Branch on the method type: create, retrieve, update, delete, search, or id_list.
306 The method parameters and the type of value returned to the client depend on the method
309 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
311 // Get the method type, then can branch on it
312 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
313 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
315 if( !strcmp( methodtype, "create" ))
316 return doCreate( ctx );
317 else if( !strcmp(methodtype, "retrieve" ))
318 return doRetrieve( ctx );
319 else if( !strcmp(methodtype, "update" ))
320 return doUpdate( ctx );
321 else if( !strcmp(methodtype, "delete" ))
322 return doDelete( ctx );
323 else if( !strcmp(methodtype, "search" ))
324 return doSearch( ctx );
325 else if( !strcmp(methodtype, "id_list" ))
326 return doIdList( ctx );
328 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...