3 @brief As a server, perform database operations at the request of clients.
5 This server is similar to the cstore and reporter-store servers,
6 except that it enforces a permissions scheme.
13 #include "opensrf/utils.h"
14 #include "opensrf/log.h"
15 #include "opensrf/osrf_application.h"
16 #include "openils/oils_utils.h"
17 #include "openils/oils_sql.h"
19 static dbi_conn writehandle; /* our MASTER db connection */
20 static dbi_conn dbhandle; /* our CURRENT db connection */
21 //static osrfHash * readHandles;
23 static const int enforce_pcrud = 1; // Boolean
24 static const char modulename[] = "open-ils.pcrud";
27 @brief Disconnect from the database.
29 This function is called when the server drone is about to terminate.
31 void osrfAppChildExit( void ) {
32 osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
35 if (writehandle == dbhandle)
39 dbi_conn_query(writehandle, "ROLLBACK;");
40 dbi_conn_close(writehandle);
43 if (dbhandle && !same)
44 dbi_conn_close(dbhandle);
46 // XXX add cleanup of readHandles whenever that gets used
52 @brief Initialize the application.
53 @return Zero if successful, or non-zero if not.
55 Load the IDL file into an internal data structure for future reference. Each non-virtual
56 class in the IDL corresponds to a table or view in the database, or to a subquery defined
57 in the IDL. Ignore all virtual tables and virtual fields.
59 Register a number of methods, some of them general-purpose and others specific for
62 The name of the application is given by the MODULENAME macro, whose value depends on
63 conditional compilation. The method names also incorporate MODULENAME, followed by a
64 dot, as a prefix. Some methods are registered or not registered depending on whether
65 the IDL includes permacrud entries for the class and method.
67 The general-purpose methods are as follows (minus their MODULENAME prefixes):
71 - transaction.rollback
77 For each non-virtual class, create up to eight class-specific methods:
79 - create (not for readonly classes)
81 - update (not for readonly classes)
82 - delete (not for readonly classes
83 - search (atomic and non-atomic versions)
84 - id_list (atomic and non-atomic versions)
86 The full method names follow the pattern "MODULENAME.method_type.classname".
87 In addition, the names of atomic methods have a suffix of ".atomic".
89 This function is called when the registering the application, and is executed by the
90 listener before spawning the drones.
92 int osrfAppInitialize( void ) {
94 osrfLogInfo(OSRF_LOG_MARK, "Initializing the PCRUD Server...");
95 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
97 if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
98 return 1; /* return non-zero to indicate error */
100 // Open the database temporarily. Look up the datatypes of all
101 // the non-virtual fields and record them with the IDL data.
102 dbi_conn handle = oilsConnectDB( modulename );
105 else if( oilsExtendIDL( handle )) {
106 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
109 dbi_conn_close( handle );
111 // Get the maximum flesh depth from the settings
112 char* md = osrf_settings_host_value(
113 "/apps/%s/app_settings/max_query_recursion", modulename );
114 int max_flesh_depth = 100;
116 max_flesh_depth = atoi( md );
117 if( max_flesh_depth < 0 )
119 else if( max_flesh_depth > 1000 )
120 max_flesh_depth = 1000;
122 oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
124 // Now register all the methods
125 growing_buffer* method_name = buffer_init(64);
127 // first we register all the transaction and savepoint methods
128 buffer_reset(method_name);
129 OSRF_BUFFER_ADD(method_name, modulename );
130 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
131 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
132 "beginTransaction", "", 0, 0 );
134 buffer_reset(method_name);
135 OSRF_BUFFER_ADD(method_name, modulename );
136 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
137 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
138 "commitTransaction", "", 0, 0 );
140 buffer_reset(method_name);
141 OSRF_BUFFER_ADD(method_name, modulename );
142 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
143 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
144 "rollbackTransaction", "", 0, 0 );
146 buffer_reset(method_name);
147 OSRF_BUFFER_ADD(method_name, modulename );
148 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
149 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
150 "setSavepoint", "", 1, 0 );
152 buffer_reset(method_name);
153 OSRF_BUFFER_ADD(method_name, modulename );
154 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
155 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
156 "releaseSavepoint", "", 1, 0 );
158 buffer_reset(method_name);
159 OSRF_BUFFER_ADD(method_name, modulename );
160 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
161 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
162 "rollbackSavepoint", "", 1, 0 );
164 buffer_reset(method_name);
165 OSRF_BUFFER_ADD(method_name, modulename );
166 OSRF_BUFFER_ADD(method_name, ".set_audit_info");
167 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
168 "setAuditInfo", "", 3, 0 );
170 static const char* global_method[] = {
178 const int global_method_count
179 = sizeof( global_method ) / sizeof ( global_method[0] );
181 unsigned long class_count = osrfHashGetCount( oilsIDL() );
182 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
183 osrfLogDebug(OSRF_LOG_MARK,
184 "At most %lu methods will be generated",
185 (unsigned long) (class_count * global_method_count) );
187 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
188 osrfHash* idlClass = NULL;
190 // For each class in the IDL...
191 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
193 const char* classname = osrfHashIteratorKey( class_itr );
194 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
196 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
197 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
198 modulename, classname);
202 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
203 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
207 // Look up some other attributes of the current class
208 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
209 if( !idlClass_fieldmapper ) {
210 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
215 osrfHash* idlClass_permacrud = NULL;
217 // Ignore classes with no permacrud section
218 idlClass_permacrud = osrfHashGet( idlClass, "permacrud" );
219 if( !idlClass_permacrud ) {
220 osrfLogDebug( OSRF_LOG_MARK,
221 "Skipping class \"%s\"; no permacrud in IDL", classname );
225 const char* readonly = osrfHashGet( idlClass, "readonly" );
228 for( i = 0; i < global_method_count; ++i ) { // for each global method
229 const char* method_type = global_method[ i ];
230 osrfLogDebug(OSRF_LOG_MARK,
231 "Using files to build %s class methods for %s", method_type, classname);
233 // Treat "id_list" or "search" as forms of "retrieve"
234 const char* tmp_method = method_type;
235 if ( *tmp_method == 'i' || *tmp_method == 's') { // "id_list" or "search"
236 tmp_method = "retrieve";
238 // Skip this method if there is no permacrud entry for it
239 if (!osrfHashGet( idlClass_permacrud, tmp_method ))
242 // No create, update, or delete methods for a readonly class
243 if ( str_is_true( readonly )
244 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
247 buffer_reset( method_name );
249 // Build the method name: MODULENAME.method_type.classname
250 buffer_fadd(method_name, "%s.%s.%s", modulename, method_type, classname);
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
313 type. However, for all PCRUD methods, the first method parameter is an authkey.
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...