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
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( void ) {
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 // Open the database temporarily. Look up the datatypes of all
100 // the non-virtual fields and record them with the IDL data.
101 dbi_conn handle = oilsConnectDB( modulename );
104 else if( oilsExtendIDL( handle )) {
105 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
108 dbi_conn_close( handle );
110 // Get the maximum flesh depth from the settings
111 char* md = osrf_settings_host_value(
112 "/apps/%s/app_settings/max_query_recursion", modulename );
113 int max_flesh_depth = 100;
115 max_flesh_depth = atoi( md );
116 if( max_flesh_depth < 0 )
118 else if( max_flesh_depth > 1000 )
119 max_flesh_depth = 1000;
121 oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
123 // Now register all the methods
124 growing_buffer* method_name = buffer_init(64);
126 // first we register all the transaction and savepoint methods
127 buffer_reset(method_name);
128 OSRF_BUFFER_ADD(method_name, modulename );
129 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
130 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
131 "beginTransaction", "", 0, 0 );
133 buffer_reset(method_name);
134 OSRF_BUFFER_ADD(method_name, modulename );
135 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
136 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
137 "commitTransaction", "", 0, 0 );
139 buffer_reset(method_name);
140 OSRF_BUFFER_ADD(method_name, modulename );
141 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
142 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
143 "rollbackTransaction", "", 0, 0 );
145 buffer_reset(method_name);
146 OSRF_BUFFER_ADD(method_name, modulename );
147 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
148 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
149 "setSavepoint", "", 1, 0 );
151 buffer_reset(method_name);
152 OSRF_BUFFER_ADD(method_name, modulename );
153 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
154 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
155 "releaseSavepoint", "", 1, 0 );
157 buffer_reset(method_name);
158 OSRF_BUFFER_ADD(method_name, modulename );
159 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
160 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
161 "rollbackSavepoint", "", 1, 0 );
163 static const char* global_method[] = {
171 const int global_method_count
172 = sizeof( global_method ) / sizeof ( global_method[0] );
174 unsigned long class_count = osrfHashGetCount( oilsIDL() );
175 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
176 osrfLogDebug(OSRF_LOG_MARK,
177 "At most %lu methods will be generated",
178 (unsigned long) (class_count * global_method_count) );
180 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
181 osrfHash* idlClass = NULL;
183 // For each class in the IDL...
184 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
186 const char* classname = osrfHashIteratorKey( class_itr );
187 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
189 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
190 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
191 modulename, classname);
195 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
196 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
200 // Look up some other attributes of the current class
201 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
202 if( !idlClass_fieldmapper ) {
203 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
208 osrfHash* idlClass_permacrud = NULL;
210 // Ignore classes with no permacrud section
211 idlClass_permacrud = osrfHashGet( idlClass, "permacrud" );
212 if( !idlClass_permacrud ) {
213 osrfLogDebug( OSRF_LOG_MARK,
214 "Skipping class \"%s\"; no permacrud in IDL", classname );
218 const char* readonly = osrfHashGet( idlClass, "readonly" );
221 for( i = 0; i < global_method_count; ++i ) { // for each global method
222 const char* method_type = global_method[ i ];
223 osrfLogDebug(OSRF_LOG_MARK,
224 "Using files to build %s class methods for %s", method_type, classname);
226 // Treat "id_list" or "search" as forms of "retrieve"
227 const char* tmp_method = method_type;
228 if ( *tmp_method == 'i' || *tmp_method == 's') { // "id_list" or "search"
229 tmp_method = "retrieve";
231 // Skip this method if there is no permacrud entry for it
232 if (!osrfHashGet( idlClass_permacrud, tmp_method ))
235 // No create, update, or delete methods for a readonly class
236 if ( str_is_true( readonly )
237 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
240 buffer_reset( method_name );
242 // Build the method name: MODULENAME.method_type.classname
243 buffer_fadd(method_name, "%s.%s.%s", modulename, method_type, classname);
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
306 type. However, for all PCRUD methods, the first method parameter is an authkey.
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...