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() {
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
76 For each non-virtual class, create up to eight class-specific methods:
78 - create (not for readonly classes)
80 - update (not for readonly classes)
81 - delete (not for readonly classes
82 - search (atomic and non-atomic versions)
83 - id_list (atomic and non-atomic versions)
85 The full method names follow the pattern "MODULENAME.method_type.classname".
86 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 PCRUD 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 char* md = osrf_settings_host_value(
100 "/apps/%s/app_settings/max_query_recursion", modulename );
101 int max_flesh_depth = 100;
103 max_flesh_depth = atoi( md );
104 if( max_flesh_depth < 0 )
106 else if( max_flesh_depth > 1000 )
107 max_flesh_depth = 1000;
109 oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
111 growing_buffer* method_name = buffer_init(64);
113 // first we register all the transaction and savepoint methods
114 buffer_reset(method_name);
115 OSRF_BUFFER_ADD(method_name, modulename );
116 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
117 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
118 "beginTransaction", "", 0, 0 );
120 buffer_reset(method_name);
121 OSRF_BUFFER_ADD(method_name, modulename );
122 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
123 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
124 "commitTransaction", "", 0, 0 );
126 buffer_reset(method_name);
127 OSRF_BUFFER_ADD(method_name, modulename );
128 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
129 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
130 "rollbackTransaction", "", 0, 0 );
132 buffer_reset(method_name);
133 OSRF_BUFFER_ADD(method_name, modulename );
134 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
135 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
136 "setSavepoint", "", 1, 0 );
138 buffer_reset(method_name);
139 OSRF_BUFFER_ADD(method_name, modulename );
140 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
141 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
142 "releaseSavepoint", "", 1, 0 );
144 buffer_reset(method_name);
145 OSRF_BUFFER_ADD(method_name, modulename );
146 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
147 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
148 "rollbackSavepoint", "", 1, 0 );
150 static const char* global_method[] = {
158 const int global_method_count
159 = sizeof( global_method ) / sizeof ( global_method[0] );
161 unsigned long class_count = osrfHashGetCount( oilsIDL() );
162 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
163 osrfLogDebug(OSRF_LOG_MARK,
164 "At most %lu methods will be generated",
165 (unsigned long) (class_count * global_method_count) );
167 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
168 osrfHash* idlClass = NULL;
170 // For each class in the IDL...
171 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
173 const char* classname = osrfHashIteratorKey( class_itr );
174 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
176 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
177 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
178 modulename, classname);
182 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
183 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
187 // Look up some other attributes of the current class
188 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
189 if( !idlClass_fieldmapper ) {
190 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
195 osrfHash* idlClass_permacrud = NULL;
197 // Ignore classes with no permacrud section
198 idlClass_permacrud = osrfHashGet( idlClass, "permacrud" );
199 if( !idlClass_permacrud ) {
200 osrfLogDebug( OSRF_LOG_MARK,
201 "Skipping class \"%s\"; no permacrud in IDL", classname );
205 const char* readonly = osrfHashGet( idlClass, "readonly" );
208 for( i = 0; i < global_method_count; ++i ) { // for each global method
209 const char* method_type = global_method[ i ];
210 osrfLogDebug(OSRF_LOG_MARK,
211 "Using files to build %s class methods for %s", method_type, classname);
213 // Treat "id_list" or "search" as forms of "retrieve"
214 const char* tmp_method = method_type;
215 if ( *tmp_method == 'i' || *tmp_method == 's') { // "id_list" or "search"
216 tmp_method = "retrieve";
218 // Skip this method if there is no permacrud entry for it
219 if (!osrfHashGet( idlClass_permacrud, tmp_method ))
222 // No create, update, or delete methods for a readonly class
223 if ( str_is_true( readonly )
224 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
227 buffer_reset( method_name );
229 // Build the method name: MODULENAME.method_type.classname
230 buffer_fadd(method_name, "%s.%s.%s", modulename, method_type, classname);
232 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
233 // The consequence is that we implicitly create an atomic method in addition to
234 // the usual non-atomic method.
236 if (*method_type == 'i' || *method_type == 's') { // id_list or search
237 flags = flags | OSRF_METHOD_STREAMING;
240 osrfHash* method_meta = osrfNewHash();
241 osrfHashSet( method_meta, idlClass, "class");
242 osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
243 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
245 // Register the method, with a pointer to an osrfHash to tell the method
246 // its name, type, and class.
247 osrfAppRegisterExtendedMethod(
249 OSRF_BUFFER_C_STR( method_name ),
250 "dispatchCRUDMethod",
257 } // end for each global method
258 } // end for each class in IDL
260 buffer_free( method_name );
261 osrfHashIteratorFree( class_itr );
267 @brief Initialize a server drone.
268 @return Zero if successful, -1 if not.
270 Connect to the database. For each non-virtual class in the IDL, execute a dummy "SELECT * "
271 query to get the datatype of each column. Record the datatypes in the loaded IDL.
273 This function is called by a server drone shortly after it is spawned by the listener.
275 int osrfAppChildInit() {
277 writehandle = oilsConnectDB( modulename );
281 oilsSetDBConnection( writehandle );
283 // Add datatypes from database to the fields in the IDL
284 if( oilsExtendIDL() ) {
285 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
293 @brief Implement the class-specific methods.
294 @param ctx Pointer to the method context.
295 @return Zero if successful, or -1 if not.
297 Branch on the method type: create, retrieve, update, delete, search, or id_list.
299 The method parameters and the type of value returned to the client depend on the method
300 type. However, for all PCRUD methods, the first method parameter is an authkey.
302 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
304 // Get the method type, then can branch on it
305 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
306 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
308 if( !strcmp( methodtype, "create" ))
309 return doCreate( ctx );
310 else if( !strcmp(methodtype, "retrieve" ))
311 return doRetrieve( ctx );
312 else if( !strcmp(methodtype, "update" ))
313 return doUpdate( ctx );
314 else if( !strcmp(methodtype, "delete" ))
315 return doDelete( ctx );
316 else if( !strcmp(methodtype, "search" ))
317 return doSearch( ctx );
318 else if( !strcmp(methodtype, "id_list" ))
319 return doIdList( ctx );
321 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...