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.cstore";
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 CStore Server...");
93 osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
95 // Load the IDL into memory
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 = osrf_buffer_init(64);
126 // Generic search thingy
127 osrf_buffer_add( method_name, modulename );
128 osrf_buffer_add( method_name, ".json_query" );
129 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
130 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
132 // Next we register all the transaction and savepoint methods
133 osrf_buffer_reset(method_name);
134 OSRF_BUFFER_ADD(method_name, modulename );
135 OSRF_BUFFER_ADD(method_name, ".transaction.begin");
136 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
137 "beginTransaction", "", 0, 0 );
139 osrf_buffer_reset(method_name);
140 OSRF_BUFFER_ADD(method_name, modulename );
141 OSRF_BUFFER_ADD(method_name, ".transaction.commit");
142 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
143 "commitTransaction", "", 0, 0 );
145 osrf_buffer_reset(method_name);
146 OSRF_BUFFER_ADD(method_name, modulename );
147 OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
148 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
149 "rollbackTransaction", "", 0, 0 );
151 osrf_buffer_reset(method_name);
152 OSRF_BUFFER_ADD(method_name, modulename );
153 OSRF_BUFFER_ADD(method_name, ".savepoint.set");
154 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
155 "setSavepoint", "", 1, 0 );
157 osrf_buffer_reset(method_name);
158 OSRF_BUFFER_ADD(method_name, modulename );
159 OSRF_BUFFER_ADD(method_name, ".savepoint.release");
160 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
161 "releaseSavepoint", "", 1, 0 );
163 osrf_buffer_reset(method_name);
164 OSRF_BUFFER_ADD(method_name, modulename );
165 OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
166 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
167 "rollbackSavepoint", "", 1, 0 );
169 osrf_buffer_reset(method_name);
170 OSRF_BUFFER_ADD(method_name, modulename );
171 OSRF_BUFFER_ADD(method_name, ".set_audit_info");
172 osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
173 "setAuditInfo", "", 3, 0 );
175 static const char* global_method[] = {
183 const int global_method_count
184 = sizeof( global_method ) / sizeof ( global_method[0] );
186 unsigned long class_count = osrfHashGetCount( oilsIDL() );
187 osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
188 osrfLogDebug(OSRF_LOG_MARK,
189 "At most %lu methods will be generated",
190 (unsigned long) (class_count * global_method_count) );
192 osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
193 osrfHash* idlClass = NULL;
195 // For each class in the IDL...
196 while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
198 const char* classname = osrfHashIteratorKey( class_itr );
199 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
201 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
202 osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
203 modulename, classname);
207 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
208 osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
212 // Look up some other attributes of the current class
213 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
214 if( !idlClass_fieldmapper ) {
215 osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
220 const char* readonly = osrfHashGet(idlClass, "readonly");
223 for( i = 0; i < global_method_count; ++i ) { // for each global method
224 const char* method_type = global_method[ i ];
225 osrfLogDebug(OSRF_LOG_MARK,
226 "Using files to build %s class methods for %s", method_type, classname);
228 // No create, update, or delete methods for a readonly class
229 if ( str_is_true( readonly )
230 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
233 osrf_buffer_reset( method_name );
235 // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
236 // where XXX is the fieldmapper name from the IDL, with every run of
237 // one or more consecutive colons replaced by a period.
240 char* _fm = strdup( idlClass_fieldmapper );
241 part = strtok_r(_fm, ":", &st_tmp);
243 osrf_buffer_fadd(method_name, "%s.direct.%s", modulename, part);
245 while ((part = strtok_r(NULL, ":", &st_tmp))) {
246 OSRF_BUFFER_ADD_CHAR(method_name, '.');
247 OSRF_BUFFER_ADD(method_name, part);
249 OSRF_BUFFER_ADD_CHAR(method_name, '.');
250 OSRF_BUFFER_ADD(method_name, method_type);
253 // For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
254 // The consequence is that we implicitly create an atomic method in addition to
255 // the usual non-atomic method.
257 if (*method_type == 'i' || *method_type == 's') { // id_list or search
258 flags = flags | OSRF_METHOD_STREAMING;
261 osrfHash* method_meta = osrfNewHash();
262 osrfHashSet( method_meta, idlClass, "class");
263 osrfHashSet( method_meta, osrf_buffer_data( method_name ), "methodname" );
264 osrfHashSet( method_meta, strdup(method_type), "methodtype" );
266 // Register the method, with a pointer to an osrfHash to tell the method
267 // its name, type, and class.
268 osrfAppRegisterExtendedMethod(
270 OSRF_BUFFER_C_STR( method_name ),
271 "dispatchCRUDMethod",
278 } // end for each global method
279 } // end for each class in IDL
281 osrf_buffer_free( method_name );
282 osrfHashIteratorFree( class_itr );
288 @brief Initialize a server drone.
289 @return Zero if successful, -1 if not.
291 Connect to the database.
293 This function is called by a server drone shortly after it is spawned by the listener.
295 int osrfAppChildInit( void ) {
297 writehandle = oilsConnectDB( modulename );
301 oilsSetDBConnection( writehandle );
307 @brief Implement the class-specific methods.
308 @param ctx Pointer to the method context.
309 @return Zero if successful, or -1 if not.
311 Branch on the method type: create, retrieve, update, delete, search, or id_list.
313 The method parameters and the type of value returned to the client depend on the method
316 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
318 // Get the method type, then can branch on it
319 osrfHash* method_meta = (osrfHash*) ctx->method->userData;
320 const char* methodtype = osrfHashGet( method_meta, "methodtype" );
322 if( !strcmp( methodtype, "create" ))
323 return doCreate( ctx );
324 else if( !strcmp(methodtype, "retrieve" ))
325 return doRetrieve( ctx );
326 else if( !strcmp(methodtype, "update" ))
327 return doUpdate( ctx );
328 else if( !strcmp(methodtype, "delete" ))
329 return doDelete( ctx );
330 else if( !strcmp(methodtype, "search" ))
331 return doSearch( ctx );
332 else if( !strcmp(methodtype, "id_list" ))
333 return doIdList( ctx );
335 osrfAppRespondComplete( ctx, NULL ); // should be unreachable...