Component schemas allow you to incorporate one or more DLLs in an ObjectStore application.
Component Schema and Application Schema
A component schema, also referred to here as a DLL schema, is a schema that is contained within a DLL. It plays the same role for the DLL as an application schema plays for an application. Like a DLL, a DLL schema can be loaded and unloaded dynamically at run time. Unlike an application schema, multiple DLL schemas can be in effect at the same time in a single program. Differences Between an Application Schema and a Component Schema
A component (DLL) schema is a type of application schema with some additional properties. The file name extension .adb is used for both application schemas and DLL schemas. DLL schemas are generated by ossg just as application schemas are.
All ObjectStore utilities that can be used on application schemas can be used for component schemas.
Uses for Component Schemas
Some typical uses of DLL schemas are as follows:
The first example, flights, uses a standard application schema.
The second example, flights2, uses a component schema generated for the DLL flight_cs with the schema source file macro OS_SCHEMA_DLL_ID( ). The component schema is loaded and unloaded automatically.
The third example, flights3, also uses a component schema in the same way as the previous example and additionally illustrates how a DLL can be dynamically loaded with an application. This example uses automatic load reporting, as shown in the following excerpt:
... const char *flight_cs_id = "DLL:flight_cs"; // DLL Identifer ... ... const char *flight_cs_symname = "flight_db_component"; os_DLL_handle dll_handle = os_null_DLL_handle; os_boolean caught_except = false; // Load the flight_cs component. TIX_HANDLE(err_DLL_not_loaded) { // This is equivalent to calling `dlopen' on Solaris2 or // `LoadLibrary' on WIN32 platforms. objectstore::load_DLL // is provided as a convenience for application developers. dll_handle = objectstore::load_DLL(flight_cs_id, true); } TIX_EXCEPTION { caught_except = true; } TIX_END_HANDLE if (caught_except || (dll_handle == os_null_DLL_handle)) { cout << "Error: " << tix_handler::get_report() << `\n'; cout << "Cannot load component: " << flight_cs_id << `\n'; return 2; } // Look up the component's entry point caught_except = false; TIX_HANDLE(err_DLL_symbol_not_found) { // Same as calling `dlsym' on Solaris2 or `GetProcAddress' on // WIN32 platforms. Provided as a convenience for application // developers. cfp = (int(*)(const char*)) objectstore::lookup_DLL_symbol(dll_handle, flight_cs_symname); } TIX_EXCEPTION { caught_except = true; } TIX_END_HANDLE if (caught_except || !cfp) { cout << "Error: " << tix_handler::get_report() << `\n'; cout << Cannot locate symbol: " << flight_cs_symname <<`\n'; return 3; } ...The fourth example, flights4, has the features of the previous example, but does not use automatic loading of the component schema. Instead it shows how to manually control how component schemas are loaded and unloaded. In the schema source file you see
// Component schemas need a DLL identifier so we define // one using this schema macro. OS_SCHEMA_DLL_ID("DLL:flight2") // Shut off automatic load/unload reporting. OS_REPORT_DLL_LOAD_AND_UNLOAD(false) // Tell ossg to use this variable name for the schema info. OS_SCHEMA_INFO_NAME(flight_cs2_dll_schema_info)
... const char *flight_cs_id = "DLL:flight_cs2"; // DLL Identifier os_DLL_schema_info *flight_cs2_sch_inf = NULL; os_schema_handle *cs2_sh; ... ... // Via a macro in the schema source file, we arranged for the // schema generator to place a symbol name that we specified to // be the name of the schema info structure //(os_DLL_schema_info *). // We simply look up the symbol in the DLL to get at it. caught_except = false; TIX_HANDLE(err_DLL_symbol_not_found) { flight_cs2_sch_inf = (os_DLL_schema_info *) objectstore::lookup_DLL_symbol(dll_handle, "flight_cs2_dll_schema_info"); } TIX_EXCEPTION { caught_except = true; } TIX_END_HANDLE if (caught_except || !flight_cs2_sch_inf) { cout << "Error: " << tix_handler::get_report() << `\n'; cout <<"Can't locate symbol: flight_cs2_dll_schema_info\n"; return 4; } // Automatic load reporting has been disabled for this component // schema(flight_cs2). Which means that the schema for this DLL // does not automatically load when put in use. So we must // manually set up the loading process here. // Tell ObjectStore that this DLL has been loaded. cs2_sh = &flight_cs2_sch_inf->DLL_loaded(); ...
... // // Unload the component // // Tell ObjectStore that this DLL is about to be unloaded. flight_cs2_sch_inf->DLL_unloaded(); objectstore::unload_DLL(dll_handle);...
CC foo.cpp foolib.cpp -ldl -o fooTo build an application with dynamic linking, use a command of the following form:
CC -G foolib.cpp -o foolib.so CC foo.cpp foolib.so -ldl -o fooTo build an application with run-time library loading, use a command of the following form:
CC -G foolib.cpp -o foolib.so CC foo.cpp -ldl -o fooTo build with dynamic linking and an explicit call to the library,
CC -G foolib.cpp -o foolib.so CC foo.cpp foolib.so -ldl -o foo
A platform-independent interface:
An interface that enables ObjectStore to automatically load DLLs when a database is put into use:
These check whether the DLL is loaded or queued to load. If not, they call objectstore::load_DLL(), which checks each DLL ID and loads it.
See the ObjectStore C++ API Reference for more information on these interfaces.
In the simplest case, calls to these functions are automatically generated by ossg and inserted into the DLL as initialization and termination functions. The application developer does not have to do anything to implement this reporting. If you want to explicitly control this, specify in the schema source file that the automatic calls should be disabled and then write code that makes the calls. For example, you could set some ObjectStore parameters or the pathname of the DLL schema database before calling DLL_loaded(). This code could be in DLL initialization and termination functions, or could be in entry points to the DLL that are called according to the developer's specific protocol. See the ObjectStore C++ API Reference for further information on these functions:
A DLL identifier is a string of the form prefix:suffix where the prefix is a string that identifies the catalog of DLLs to be used and the suffix is something meaningful to that catalog. A colon can also appear in the suffix, but the first colon in an identifier is always interpreted as a separator.
ObjectStore provides the following built-in DLL identifier prefixes:
See the following for more information on uses of DLL identifiers:
OS_REPORT_DLL_LOAD_AND_UNLOAD( os_boolean)Reports that a DLL has been loaded or unloaded.
OS_SCHEMA_DLL_ID( string)
You must call OS_SCHEMA_DLL_ID at least once in a DLL schema source file to distinguish it from an application schema.
OS_SCHEMA_INFO_NAME( name)
You cannot register a DLL finder from a static constructor that could be called before objectstore::initialize() has been called. You must call objectstore::initialize() before doing anything with DLL finders, even registering them.
The argument to the get() function is a DLL identifier, not a prefix. The function finds the prefix by searching for a colon.
Each subclass of os_DLL_finder must provide an implementation of load_DLL that interprets the suffix part of the DLL identifier and calls the appropriate operating system API (or calls another os_DLL_finder) to load the DLL.
Each subclass of os_DLL_finder must provide an implementation of equal_DLL_identifiers_same_prefix that compares two DLL identifiers that are both implemented by this finder and returns true if they are equal.
To compare two DLL identifier strings, call the static function os_DLL_finder::equal_DLL_identifiers(). It takes care of getting the prefixes, looking up the finder, and calling equal_DLL_identifiers_same_prefix. Looking up objects by DLL identifier calls os_DLL_finder::equal_DLL_identifiers() to compare identifiers that have the same prefix.
The objectstore::load_DLL() functions call os_DLL_finder::load_DLL() for the finder that implements the prefix of the specified DLL_identifier.
See os_DLL_finder in the ObjectStore C++ API Reference for further information.
Compiler Dope Damage
Compiler dope is additional information added to the run-time layout of an object by the compiler, beyond the nonstatic data members of the object. Compilers use compiler dope for several purposes, most notably to point to a table of virtual function implementations for the object's class. When ObjectStore brings a persistent object into memory from the database, it ensures that the object contains the correct compiler dope for the current program, for the compiler with which the program was compiled. The correct compiler dope for an object can change as a result of loading or unloading a DLL schema, for example, because the compiler dope can point to a virtual function implementation contained in a DLL that is being loaded or unloaded.
See the following in Chapter 2 of ObjectStore C++ API Reference for more information: objectstore::enable_damaged_dope_repair() and objectstore::is_damaged_dope_repair_enabled().
Note that dope damage always occurs when unloading a DLL schema and it is always repaired, regardless of the setting of objectstore::enable_damaged_dope_repair().
Schema Evolution
The ossevol utility can be used to evolve DLL (component) schemas.
ossevol <workdb> <schemadb> <evolvedb>+ [keyword_option]+ keyword_option ::= -dll_schema pthnames_of_component_schemaYou can also use the following member functions of the class os_schema_evolution:
static void os_schema_evolution::augment_dll_schema_db_names (const os_charp_collection& dll_schema_db_names);and
static void os_schema_evolution::augment_dll_schema_db_names (const char* c);These two functions add the specified component schema database names to the list of component schema databases to be used for the evolution.
Updated: 04/02/98 14:57:08