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.reporter-store";
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 RStore Server...");
92 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
94 if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
95 return 1; /* return non-zero to indicate error */
97 // Open the database temporarily. Look up the datatypes of all
98 // the non-virtual fields and record them with the IDL data.
99 dbi_conn handle = oilsConnectDB( modulename );
102 else if( oilsExtendIDL( handle )) {
103 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
106 dbi_conn_close( handle );
108 // Get the maximum flesh depth from the settings
109 char* md = osrf_settings_host_value(
110 "/apps/%s/app_settings/max_query_recursion", modulename );
111 int max_flesh_depth = 100;
113 max_flesh_depth = atoi( md );
114 if( max_flesh_depth < 0 )
116 else if( max_flesh_depth > 1000 )
117 max_flesh_depth = 1000;
119 oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
121 // Now register all the methods
122 growing_buffer* method_name = buffer_init(64);
124 // Generic search thingy
125 buffer_add( method_name, modulename );
126 buffer_add( method_name, ".json_query" );
127 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
128 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
130 // first we register all the transaction and savepoint methods
131 buffer_reset(method_name);
132 OSRF_BUFFER_ADD(method_name, modulename );
133 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
134 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
135 "beginTransaction", "", 0, 0 );
137 buffer_reset(method_name);
138 OSRF_BUFFER_ADD(method_name, modulename );
139 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
140 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
141 "commitTransaction", "", 0, 0 );
143 buffer_reset(method_name);
144 OSRF_BUFFER_ADD(method_name, modulename );
145 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
146 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
147 "rollbackTransaction", "", 0, 0 );
149 buffer_reset(method_name);
150 OSRF_BUFFER_ADD(method_name, modulename );
151 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
152 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
153 "setSavepoint", "", 1, 0 );
155 buffer_reset(method_name);
156 OSRF_BUFFER_ADD(method_name, modulename );
157 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
158 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
159 "releaseSavepoint", "", 1, 0 );
161 buffer_reset(method_name);
162 OSRF_BUFFER_ADD(method_name, modulename );
163 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
164 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
165 "rollbackSavepoint", "", 1, 0 );
167 static const char* global_method[] = {
175 const int global_method_count
176 = sizeof( global_method ) / sizeof ( global_method[0] );
178 unsigned long class_count = osrfHashGetCount( oilsIDL() );
179 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
180 osrfLogDebug(OSRF_LOG_MARK,
181 "At most %lu methods will be generated",
182 (unsigned long) (class_count * global_method_count) );
184 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
185 osrfHash* idlClass = NULL;
187 // For each class in the IDL...
188 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
190 const char* classname = osrfHashIteratorKey( class_itr );
191 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
193 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
194 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
195 modulename, classname);
199 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
200 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
204 // Look up some other attributes of the current class
205 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
206 if( !idlClass_fieldmapper ) {
207 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
212 const char* readonly = osrfHashGet(idlClass, "readonly");
215 for( i = 0; i < global_method_count; ++i ) { // for each global method
216 const char* method_type = global_method[ i ];
217 osrfLogDebug(OSRF_LOG_MARK,
218 "Using files to build %s class methods for %s", method_type, classname);
220 // No create, update, or delete methods for a readonly class
221 if ( str_is_true( readonly )
222 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
225 buffer_reset( method_name );
227 // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
228 // where XXX is the fieldmapper name from the IDL, with every run of
229 // one or more consecutive colons replaced by a period.
232 char* _fm = strdup( idlClass_fieldmapper );
233 part = strtok_r(_fm, ":", &st_tmp);
235 buffer_fadd(method_name, "%s.direct.%s", modulename, part);
237 while ((part = strtok_r(NULL, ":", &st_tmp))) {
238 OSRF_BUFFER_ADD_CHAR(method_name, '.');
239 OSRF_BUFFER_ADD(method_name, part);
241 OSRF_BUFFER_ADD_CHAR(method_name, '.');
242 OSRF_BUFFER_ADD(method_name, method_type);
245 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
246 // The consequence is that we implicitly create an atomic method in addition to
247 // the usual non-atomic method.
249 if (*method_type == 'i' || *method_type == 's') { // id_list or search
250 flags = flags | OSRF_METHOD_STREAMING;
253 osrfHash* method_meta = osrfNewHash();
254 osrfHashSet( method_meta, idlClass, "class");
255 osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
256 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
258 // Register the method, with a pointer to an osrfHash to tell the method
259 // its name, type, and class.
260 osrfAppRegisterExtendedMethod(
262 OSRF_BUFFER_C_STR( method_name ),
263 "dispatchCRUDMethod",
270 } // end for each global method
271 } // end for each class in IDL
273 buffer_free( method_name );
274 osrfHashIteratorFree( class_itr );
280 @brief Initialize a server drone.
281 @return Zero if successful, -1 if not.
283 Connect to the database.
285 This function is called by a server drone shortly after it is spawned by the listener.
287 int osrfAppChildInit( void ) {
289 writehandle = oilsConnectDB( modulename );
293 oilsSetDBConnection( writehandle );
299 @brief Implement the class-specific methods.
300 @param ctx Pointer to the method context.
301 @return Zero if successful, or -1 if not.
303 Branch on the method type: create, retrieve, update, delete, search, or id_list.
305 The method parameters and the type of value returned to the client depend on the method
308 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
310 // Get the method type, then can branch on it
311 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
312 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
314 if( !strcmp( methodtype, "create" ))
315 return doCreate( ctx );
316 else if( !strcmp(methodtype, "retrieve" ))
317 return doRetrieve( ctx );
318 else if( !strcmp(methodtype, "update" ))
319 return doUpdate( ctx );
320 else if( !strcmp(methodtype, "delete" ))
321 return doDelete( ctx );
322 else if( !strcmp(methodtype, "search" ))
323 return doSearch( ctx );
324 else if( !strcmp(methodtype, "id_list" ))
325 return doIdList( ctx );
327 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...