]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/c-apps/oils_rstore.c
Add OILS_SIP_MSG_BILL_ERR for when an error occurs getting bills.
[working/Evergreen.git] / Open-ILS / src / c-apps / oils_rstore.c
1 /**
2         @file oils_rstore.c
3         @brief As a server, perform database operations at the request of clients.
4 */
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <dbi/dbi.h>
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"
15
16 static dbi_conn writehandle; /* our MASTER db connection */
17 static dbi_conn dbhandle; /* our CURRENT db connection */
18 //static osrfHash * readHandles;
19
20 static const int enforce_pcrud = 0;     // Boolean
21 static const char modulename[] = "open-ils.reporter-store";
22
23 /**
24         @brief Disconnect from the database.
25
26         This function is called when the server drone is about to terminate.
27 */
28 void osrfAppChildExit( void ) {
29         osrfLogDebug(OSRF_LOG_MARK, "Child is exiting, disconnecting from database...");
30
31         int same = 0;
32         if (writehandle == dbhandle)
33                 same = 1;
34
35         if (writehandle) {
36                 dbi_conn_query(writehandle, "ROLLBACK;");
37                 dbi_conn_close(writehandle);
38                 writehandle = NULL;
39         }
40         if (dbhandle && !same)
41                 dbi_conn_close(dbhandle);
42
43         // XXX add cleanup of readHandles whenever that gets used
44
45         return;
46 }
47
48 /**
49         @brief Initialize the application.
50         @return Zero if successful, or non-zero if not.
51
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.
55
56         Register a number of methods, some of them general-purpose and others specific for
57         particular classes.
58
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
61         dot, as a prefix.
62
63         The general-purpose methods are as follows (minus their MODULENAME prefixes):
64
65         - json_query
66         - transaction.begin
67         - transaction.commit
68         - transaction.rollback
69         - savepoint.set
70         - savepoint.release
71         - savepoint.rollback
72
73         For each non-virtual class, create up to eight class-specific methods:
74
75         - create    (not for readonly classes)
76         - retrieve
77         - update    (not for readonly classes)
78         - delete    (not for readonly classes
79         - search    (atomic and non-atomic versions)
80         - id_list   (atomic and non-atomic versions)
81
82         The full method names follow the pattern "MODULENAME.direct.XXX.method_type", where XXX
83         is the fieldmapper name from the IDL, with every run of one or more consecutive colons
84         replaced by a period.  In addition, the names of atomic methods have a suffix of ".atomic".
85
86         This function is called when the registering the application, and is executed by the
87         listener before spawning the drones.
88 */
89 int osrfAppInitialize( void ) {
90
91         osrfLogInfo(OSRF_LOG_MARK, "Initializing the RStore Server...");
92         osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");
93
94         if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
95                 return 1; /* return non-zero to indicate error */
96
97         // Open the database temporarily.  Look up the datatypes of all
98         // the non-virtual fields and record them with the IDL data.
99         dbi_conn handle = oilsConnectDB( modulename );
100         if( !handle )
101                 return -1;
102         else if( oilsExtendIDL( handle )) {
103                 osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
104                 return -1;
105         }
106         dbi_conn_close( handle );
107
108         // Get the maximum flesh depth from the settings
109         char* md = osrf_settings_host_value(
110                 "/apps/%s/app_settings/max_query_recursion", modulename );
111         int max_flesh_depth = 100;
112         if( md )
113                 max_flesh_depth = atoi( md );
114         if( max_flesh_depth < 0 )
115                 max_flesh_depth = 1;
116         else if( max_flesh_depth > 1000 )
117                 max_flesh_depth = 1000;
118
119         oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );
120
121         // Now register all the methods
122         growing_buffer* method_name = buffer_init(64);
123
124         // Generic search thingy
125         buffer_add( method_name, modulename );
126         buffer_add( method_name, ".json_query" );
127         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
128                 "doJSONSearch", "", 1, OSRF_METHOD_STREAMING );
129
130         // first we register all the transaction and savepoint methods
131         buffer_reset(method_name);
132         OSRF_BUFFER_ADD(method_name, modulename );
133         OSRF_BUFFER_ADD(method_name, ".transaction.begin");
134         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
135                         "beginTransaction", "", 0, 0 );
136
137         buffer_reset(method_name);
138         OSRF_BUFFER_ADD(method_name, modulename );
139         OSRF_BUFFER_ADD(method_name, ".transaction.commit");
140         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
141                         "commitTransaction", "", 0, 0 );
142
143         buffer_reset(method_name);
144         OSRF_BUFFER_ADD(method_name, modulename );
145         OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
146         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
147                         "rollbackTransaction", "", 0, 0 );
148
149         buffer_reset(method_name);
150         OSRF_BUFFER_ADD(method_name, modulename );
151         OSRF_BUFFER_ADD(method_name, ".savepoint.set");
152         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
153                         "setSavepoint", "", 1, 0 );
154
155         buffer_reset(method_name);
156         OSRF_BUFFER_ADD(method_name, modulename );
157         OSRF_BUFFER_ADD(method_name, ".savepoint.release");
158         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
159                         "releaseSavepoint", "", 1, 0 );
160
161         buffer_reset(method_name);
162         OSRF_BUFFER_ADD(method_name, modulename );
163         OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
164         osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
165                         "rollbackSavepoint", "", 1, 0 );
166
167         static const char* global_method[] = {
168                 "create",
169                 "retrieve",
170                 "update",
171                 "delete",
172                 "search",
173                 "id_list"
174         };
175         const int global_method_count
176                 = sizeof( global_method ) / sizeof ( global_method[0] );
177
178         unsigned long class_count = osrfHashGetCount( oilsIDL() );
179         osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
180         osrfLogDebug(OSRF_LOG_MARK,
181                 "At most %lu methods will be generated",
182                 (unsigned long) (class_count * global_method_count) );
183
184         osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
185         osrfHash* idlClass = NULL;
186
187         // For each class in the IDL...
188         while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {
189
190                 const char* classname = osrfHashIteratorKey( class_itr );
191                 osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);
192
193                 if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
194                         osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
195                                         modulename, classname);
196                         continue;
197                 }
198
199                 if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
200                         osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
201                         continue;
202                 }
203
204                 // Look up some other attributes of the current class
205                 const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
206                 if( !idlClass_fieldmapper ) {
207                         osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
208                                         classname );
209                         continue;
210                 }
211
212                 const char* readonly = osrfHashGet(idlClass, "readonly");
213
214                 int i;
215                 for( i = 0; i < global_method_count; ++i ) {  // for each global method
216                         const char* method_type = global_method[ i ];
217                         osrfLogDebug(OSRF_LOG_MARK,
218                                 "Using files to build %s class methods for %s", method_type, classname);
219
220                         // No create, update, or delete methods for a readonly class
221                         if ( str_is_true( readonly )
222                                 && ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
223                                 continue;
224
225                         buffer_reset( method_name );
226
227                         // Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
228                         // where XXX is the fieldmapper name from the IDL, with every run of
229                         // one or more consecutive colons replaced by a period.
230                         char* st_tmp = NULL;
231                         char* part = NULL;
232                         char* _fm = strdup( idlClass_fieldmapper );
233                         part = strtok_r(_fm, ":", &st_tmp);
234
235                         buffer_fadd(method_name, "%s.direct.%s", modulename, part);
236
237                         while ((part = strtok_r(NULL, ":", &st_tmp))) {
238                                 OSRF_BUFFER_ADD_CHAR(method_name, '.');
239                                 OSRF_BUFFER_ADD(method_name, part);
240                         }
241                         OSRF_BUFFER_ADD_CHAR(method_name, '.');
242                         OSRF_BUFFER_ADD(method_name, method_type);
243                         free(_fm);
244
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.
248                         int flags = 0;
249                         if (*method_type == 'i' || *method_type == 's') {  // id_list or search
250                                 flags = flags | OSRF_METHOD_STREAMING;
251                         }
252
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" );
257
258                         // Register the method, with a pointer to an osrfHash to tell the method
259                         // its name, type, and class.
260                         osrfAppRegisterExtendedMethod(
261                                 modulename,
262                                 OSRF_BUFFER_C_STR( method_name ),
263                                 "dispatchCRUDMethod",
264                                 "",
265                                 1,
266                                 flags,
267                                 (void*)method_meta
268                         );
269
270                 } // end for each global method
271         } // end for each class in IDL
272
273         buffer_free( method_name );
274         osrfHashIteratorFree( class_itr );
275
276         return 0;
277 }
278
279 /**
280         @brief Initialize a server drone.
281         @return Zero if successful, -1 if not.
282
283         Connect to the database.
284
285         This function is called by a server drone shortly after it is spawned by the listener.
286 */
287 int osrfAppChildInit( void ) {
288
289         writehandle = oilsConnectDB( modulename );
290         if( !writehandle )
291                 return -1;
292
293         oilsSetDBConnection( writehandle );
294
295         return 0;
296 }
297
298 /**
299         @brief Implement the class-specific methods.
300         @param ctx Pointer to the method context.
301         @return Zero if successful, or -1 if not.
302
303         Branch on the method type: create, retrieve, update, delete, search, or id_list.
304
305         The method parameters and the type of value returned to the client depend on the method
306         type.
307 */
308 int dispatchCRUDMethod( osrfMethodContext* ctx ) {
309
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" );
313
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 );
326         else {
327                 osrfAppRespondComplete( ctx, NULL );      // should be unreachable...
328                 return 0;
329         }
330 }