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
74 For each non-virtual class, create up to eight class-specific methods:
76 - create (not for readonly classes)
78 - update (not for readonly classes)
79 - delete (not for readonly classes
80 - search (atomic and non-atomic versions)
81 - id_list (atomic and non-atomic versions)
83 The full method names follow the pattern "MODULENAME.direct.XXX.method_type", where XXX
84 is the fieldmapper name from the IDL, with every run of one or more consecutive colons
85 replaced by a period. In addition, the names of atomic methods have a suffix of ".atomic".
87 This function is called when the registering the application, and is executed by the
88 listener before spawning the drones.
90 int osrfAppInitialize( void ) {
92 osrfLogInfo(OSRF_LOG_MARK, "Initializing the RStore Server...");
93 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
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 // first 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 buffer_reset(method_name);
169 OSRF_BUFFER_ADD(method_name, modulename );
170 OSRF_BUFFER_ADD(method_name, ".set_audit_info");
171 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
172 "setAuditInfo", "", 3, 0 );
174 static const char* global_method[] = {
182 const int global_method_count
183 = sizeof( global_method ) / sizeof ( global_method[0] );
185 unsigned long class_count = osrfHashGetCount( oilsIDL() );
186 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
187 osrfLogDebug(OSRF_LOG_MARK,
188 "At most %lu methods will be generated",
189 (unsigned long) (class_count * global_method_count) );
191 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
192 osrfHash* idlClass = NULL;
194 // For each class in the IDL...
195 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
197 const char* classname = osrfHashIteratorKey( class_itr );
198 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
200 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
201 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
202 modulename, classname);
206 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
207 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
211 // Look up some other attributes of the current class
212 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
213 if( !idlClass_fieldmapper ) {
214 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
219 const char* readonly = osrfHashGet(idlClass, "readonly");
222 for( i = 0; i < global_method_count; ++i ) { // for each global method
223 const char* method_type = global_method[ i ];
224 osrfLogDebug(OSRF_LOG_MARK,
225 "Using files to build %s class methods for %s", method_type, classname);
227 // No create, update, or delete methods for a readonly class
228 if ( str_is_true( readonly )
229 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
232 buffer_reset( method_name );
234 // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
235 // where XXX is the fieldmapper name from the IDL, with every run of
236 // one or more consecutive colons replaced by a period.
239 char* _fm = strdup( idlClass_fieldmapper );
240 part = strtok_r(_fm, ":", &st_tmp);
242 buffer_fadd(method_name, "%s.direct.%s", modulename, part);
244 while ((part = strtok_r(NULL, ":", &st_tmp))) {
245 OSRF_BUFFER_ADD_CHAR(method_name, '.');
246 OSRF_BUFFER_ADD(method_name, part);
248 OSRF_BUFFER_ADD_CHAR(method_name, '.');
249 OSRF_BUFFER_ADD(method_name, method_type);
252 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
253 // The consequence is that we implicitly create an atomic method in addition to
254 // the usual non-atomic method.
256 if (*method_type == 'i' || *method_type == 's') { // id_list or search
257 flags = flags | OSRF_METHOD_STREAMING;
260 osrfHash* method_meta = osrfNewHash();
261 osrfHashSet( method_meta, idlClass, "class");
262 osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
263 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
265 // Register the method, with a pointer to an osrfHash to tell the method
266 // its name, type, and class.
267 osrfAppRegisterExtendedMethod(
269 OSRF_BUFFER_C_STR( method_name ),
270 "dispatchCRUDMethod",
277 } // end for each global method
278 } // end for each class in IDL
280 buffer_free( method_name );
281 osrfHashIteratorFree( class_itr );
287 @brief Initialize a server drone.
288 @return Zero if successful, -1 if not.
290 Connect to the database.
292 This function is called by a server drone shortly after it is spawned by the listener.
294 int osrfAppChildInit( void ) {
296 writehandle = oilsConnectDB( modulename );
300 oilsSetDBConnection( writehandle );
306 @brief Implement the class-specific methods.
307 @param ctx Pointer to the method context.
308 @return Zero if successful, or -1 if not.
310 Branch on the method type: create, retrieve, update, delete, search, or id_list.
312 The method parameters and the type of value returned to the client depend on the method
315 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
317 // Get the method type, then can branch on it
318 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
319 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
321 if( !strcmp( methodtype, "create" ))
322 return doCreate( ctx );
323 else if( !strcmp(methodtype, "retrieve" ))
324 return doRetrieve( ctx );
325 else if( !strcmp(methodtype, "update" ))
326 return doUpdate( ctx );
327 else if( !strcmp(methodtype, "delete" ))
328 return doDelete( ctx );
329 else if( !strcmp(methodtype, "search" ))
330 return doSearch( ctx );
331 else if( !strcmp(methodtype, "id_list" ))
332 return doIdList( ctx );
334 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...