/**
	@brief Run the introspect method for a specified method or group of methods.
	@param ctx Pointer to the method context.
	@return 1 if successful, or if no search target is specified as a parameter; -1 if unable
	to find a pointer to the application.

	Traverse the list of methods, and report on each one whose name starts with the specified
	search target.  In effect, the search target ends with an implicit wild card.
*/
static int osrfAppIntrospect( osrfMethodContext* ctx ) {

	// Get the name of the method to introspect
	const char* methodSubstring = jsonObjectGetString( jsonObjectGetIndex(ctx->params, 0) );
	if( !methodSubstring )
		return 1; /* respond with no methods */

	// Get a pointer to the application
	osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );
	if( !app )
		return -1;   // Oops, no application...

	int len = 0;
	osrfHashIterator* itr = osrfNewHashIterator(app->methods);
	osrfMethod* method;

	while( (method = osrfHashIteratorNext(itr)) ) {
		if( (len = strlen(methodSubstring)) <= strlen(method->name) ) {
			if( !strncmp( method->name, methodSubstring, len) ) {
				jsonObject* resp = jsonNewObject(NULL);
				_osrfAppSetIntrospectMethod( ctx, method, resp );
				osrfAppRespond(ctx, resp);
				jsonObjectFree(resp);
			}
		}
	}

	osrfHashIteratorFree(itr);
	return 1;
}
/**
	@brief Call the exit handler for every application that has one.

	Normally a server's child process (a so-called "drone") calls this function just before
	shutting down.
*/
void osrfAppRunExitCode( void ) {
	osrfHashIterator* itr = osrfNewHashIterator(_osrfAppHash);
	osrfApplication* app;
	while( (app = osrfHashIteratorNext(itr)) ) {
		if( app->onExit ) {
			osrfLogInfo(OSRF_LOG_MARK, "Running onExit handler for app %s",
				osrfHashIteratorKey(itr) );
			app->onExit();
		}
	}
	osrfHashIteratorFree(itr);
}
/**
	@brief Run the implement_all method.
	@param ctx Pointer to the method context.
	@return 1 if successful, or -1 if unable to find a pointer to the application.

	Report on all of the methods of the application.
*/
static int osrfAppIntrospectAll( osrfMethodContext* ctx ) {
	osrfApplication* app = _osrfAppFindApplication( ctx->session->remote_service );

	if(app) {
		osrfHashIterator* itr = osrfNewHashIterator(app->methods);
		osrfMethod* method;
		while( (method = osrfHashIteratorNext(itr)) ) {
			jsonObject* resp = jsonNewObject(NULL);
			_osrfAppSetIntrospectMethod( ctx, method, resp );
			osrfAppRespond(ctx, resp);
			jsonObjectFree(resp);
		}
		osrfHashIteratorFree(itr);
		return 1;
	} else
		return -1;
}
Exemple #4
0
// For a given class: return a copy of the name of the field 
// at a specified array_position (or NULL if there is none)
char * oilsIDL_pton (const char* classname, int pos) {
	osrfHash* fields_hash = oilsIDL_fields( classname );
	if( !fields_hash )
		return NULL;     // No such class, or no fields for it

	char* ret = NULL;
	osrfHash* field_def_hash = NULL;
	osrfHashIterator* iter = osrfNewHashIterator( fields_hash );

	while ( ( field_def_hash = osrfHashIteratorNext( iter ) ) ) {
		if ( atoi( osrfHashGet( field_def_hash, "array_position" ) ) == pos ) {
			ret = strdup( osrfHashIteratorKey( iter ) );
			break;
		}
	}

	osrfHashIteratorFree( iter );

	return ret;
}
/**
	@brief Initialize the application.
	@return Zero if successful, or non-zero if not.

	Load the IDL file into an internal data structure for future reference.  Each non-virtual
	class in the IDL corresponds to a table or view in the database, or to a subquery defined
	in the IDL.  Ignore all virtual tables and virtual fields.

	Register a number of methods, some of them general-purpose and others specific for
	particular classes.

	The name of the application is given by the MODULENAME macro, whose value depends on
	conditional compilation.  The method names also incorporate MODULENAME, followed by a
	dot, as a prefix.

	The general-purpose methods are as follows (minus their MODULENAME prefixes):

	- json_query
	- transaction.begin
	- transaction.commit
	- transaction.rollback
	- savepoint.set
	- savepoint.release
	- savepoint.rollback

	For each non-virtual class, create up to eight class-specific methods:

	- create    (not for readonly classes)
	- retrieve
	- update    (not for readonly classes)
	- delete    (not for readonly classes
	- search    (atomic and non-atomic versions)
	- id_list   (atomic and non-atomic versions)

	The full method names follow the pattern "MODULENAME.direct.XXX.method_type", where XXX
	is the fieldmapper name from the IDL, with every run of one or more consecutive colons
	replaced by a period.  In addition, the names of atomic methods have a suffix of ".atomic".

	This function is called when the registering the application, and is executed by the
	listener before spawning the drones.
*/
int osrfAppInitialize( void ) {

	osrfLogInfo(OSRF_LOG_MARK, "Initializing the CStore Server...");
	osrfLogInfo(OSRF_LOG_MARK, "Finding XML file...");

	// Load the IDL into memory
	if ( !oilsIDLInit( osrf_settings_host_value( "/IDL" )))
		return 1; /* return non-zero to indicate error */

	// Open the database temporarily.  Look up the datatypes of all
	// the non-virtual fields and record them with the IDL data.
	dbi_conn handle = oilsConnectDB( modulename );
	if( !handle )
		return -1;
	else if( oilsExtendIDL( handle )) {
		osrfLogError( OSRF_LOG_MARK, "Error extending the IDL" );
		return -1;
	}
	dbi_conn_close( handle );

	// Get the maximum flesh depth from the settings
	char* md = osrf_settings_host_value(
		"/apps/%s/app_settings/max_query_recursion", modulename );
	int max_flesh_depth = 100;
	if( md )
		max_flesh_depth = atoi( md );
	if( max_flesh_depth < 0 )
		max_flesh_depth = 1;
	else if( max_flesh_depth > 1000 )
		max_flesh_depth = 1000;

	oilsSetSQLOptions( modulename, enforce_pcrud, max_flesh_depth );

	// Now register all the methods
	growing_buffer* method_name = buffer_init(64);

	// Generic search thingy
	buffer_add( method_name, modulename );
	buffer_add( method_name, ".json_query" );
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
		"doJSONSearch", "", 1, OSRF_METHOD_STREAMING );

	// Next we register all the transaction and savepoint methods
	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".transaction.begin");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR( method_name ),
			"beginTransaction", "", 0, 0 );

	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".transaction.commit");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
			"commitTransaction", "", 0, 0 );

	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".transaction.rollback");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
			"rollbackTransaction", "", 0, 0 );

	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".savepoint.set");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
			"setSavepoint", "", 1, 0 );

	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".savepoint.release");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
			"releaseSavepoint", "", 1, 0 );

	buffer_reset(method_name);
	OSRF_BUFFER_ADD(method_name, modulename );
	OSRF_BUFFER_ADD(method_name, ".savepoint.rollback");
	osrfAppRegisterMethod( modulename, OSRF_BUFFER_C_STR(method_name),
			"rollbackSavepoint", "", 1, 0 );

	static const char* global_method[] = {
		"create",
		"retrieve",
		"update",
		"delete",
		"search",
		"id_list"
	};
	const int global_method_count
		= sizeof( global_method ) / sizeof ( global_method[0] );

	unsigned long class_count = osrfHashGetCount( oilsIDL() );
	osrfLogDebug(OSRF_LOG_MARK, "%lu classes loaded", class_count );
	osrfLogDebug(OSRF_LOG_MARK,
		"At most %lu methods will be generated",
		(unsigned long) (class_count * global_method_count) );

	osrfHashIterator* class_itr = osrfNewHashIterator( oilsIDL() );
	osrfHash* idlClass = NULL;

	// For each class in the IDL...
	while( (idlClass = osrfHashIteratorNext( class_itr ) ) ) {

		const char* classname = osrfHashIteratorKey( class_itr );
		osrfLogInfo(OSRF_LOG_MARK, "Generating class methods for %s", classname);

		if (!osrfStringArrayContains( osrfHashGet(idlClass, "controller"), modulename )) {
			osrfLogInfo(OSRF_LOG_MARK, "%s is not listed as a controller for %s, moving on",
					modulename, classname);
			continue;
		}

		if ( str_is_true( osrfHashGet(idlClass, "virtual") ) ) {
			osrfLogDebug(OSRF_LOG_MARK, "Class %s is virtual, skipping", classname );
			continue;
		}

		// Look up some other attributes of the current class
		const char* idlClass_fieldmapper = osrfHashGet(idlClass, "fieldmapper");
		if( !idlClass_fieldmapper ) {
			osrfLogDebug( OSRF_LOG_MARK, "Skipping class \"%s\"; no fieldmapper in IDL",
					classname );
			continue;
		}

		const char* readonly = osrfHashGet(idlClass, "readonly");

		int i;
		for( i = 0; i < global_method_count; ++i ) {  // for each global method
			const char* method_type = global_method[ i ];
			osrfLogDebug(OSRF_LOG_MARK,
				"Using files to build %s class methods for %s", method_type, classname);

			// No create, update, or delete methods for a readonly class
			if ( str_is_true( readonly )
				&& ( *method_type == 'c' || *method_type == 'u' || *method_type == 'd') )
				continue;

			buffer_reset( method_name );

			// Build the method name: MODULENAME.MODULENAME.direct.XXX.method_type
			// where XXX is the fieldmapper name from the IDL, with every run of
			// one or more consecutive colons replaced by a period.
			char* st_tmp = NULL;
			char* part = NULL;
			char* _fm = strdup( idlClass_fieldmapper );
			part = strtok_r(_fm, ":", &st_tmp);

			buffer_fadd(method_name, "%s.direct.%s", modulename, part);

			while ((part = strtok_r(NULL, ":", &st_tmp))) {
				OSRF_BUFFER_ADD_CHAR(method_name, '.');
				OSRF_BUFFER_ADD(method_name, part);
			}
			OSRF_BUFFER_ADD_CHAR(method_name, '.');
			OSRF_BUFFER_ADD(method_name, method_type);
			free(_fm);

			// For an id_list or search method we specify the OSRF_METHOD_STREAMING option.
			// The consequence is that we implicitly create an atomic method in addition to
			// the usual non-atomic method.
			int flags = 0;
			if (*method_type == 'i' || *method_type == 's') {  // id_list or search
				flags = flags | OSRF_METHOD_STREAMING;
			}

			osrfHash* method_meta = osrfNewHash();
			osrfHashSet( method_meta, idlClass, "class");
			osrfHashSet( method_meta, buffer_data( method_name ), "methodname" );
			osrfHashSet( method_meta, strdup(method_type), "methodtype" );

			// Register the method, with a pointer to an osrfHash to tell the method
			// its name, type, and class.
			osrfAppRegisterExtendedMethod(
				modulename,
				OSRF_BUFFER_C_STR( method_name ),
				"dispatchCRUDMethod",
				"",
				1,
				flags,
				(void*)method_meta
			);

		} // end for each global method
	} // end for each class in IDL

	buffer_free( method_name );
	osrfHashIteratorFree( class_itr );

	return 0;
}