Configuration Database
Application Programmer Interface
Yury Kolomensky, George Zioulas
28 January 1998
Last update: Sept. 16, 1998
Table of Contents
The Configuration Database is one of the BaBar Online databases that provides access to the configuration information of the various subsystems of the detector. The design of the
Configuration Database is in many respects similar to the Conditions Database. The key features of the system are:
- Configuration objects are uniquely identified by the subsystem name, class name, secondary key, and a 32-bit (unsigned int, d_ULong) Configuration Key.
- The objects are clustered in containers that are identified by subsystem name, class name, and the secondary key.
- All objects for a given subsystem are located in a single database (one or more files on disk in a separate directory). A database is identified by subsystem's TLA. By
convention, the first letter of the TLA is capitalized.
- The "number space" for the Configuration Key is a container; that is, two objects of can share a Configuration Key as long as they are in different containers.
- Every new object in a container is assigned a unique Configuration Key. The Keys are consecutive starting at 1.
- In the online environment, the primary method for accessing the configuration data is by using a Configuration Key generated by the RunControl. The RunControl Key is global; a Configuration Tree is created to do the
translation to the container-specific Keys.
- An Alias Database contains the mapping of the run types onto the RunControl Keys at a given time.
- The configuration objects are created in a "write-once" mode. I.e., once the transaction which created an object is committed, the object cannot be changed or deleted.
- The Configuration objects can be stored directly in the Conditions database.
Back to the Table of Contents
Classes that go into the configuration database must inherit from the base class, BdbConfig/BdbConfigObject.
The following pure virtual method has to be provided:
- virtual BdbStatus copy(const BdbHandle(BdbConfigContObj)& containerH, BdbHandle(BdbConfigObject)& newObjectH);
Method to copy a persistent object. Objectivity only provides shallow copy, so one has to take special care of composite objects that contain object references or associations (see, for example, BdbConfigMap) In most simple cases (non-composite objects), one can use shallow_copy() method provided in the base class.
By convention, configuration database classes provide the following static method:
- static BdbHandle(className) create(BdbConfigWriter* theDatabase,...);
Serves as a factory method for creation of the persistent objects
An sample configuration database class can be found in BdbConfigTests/BdbConfigSampleObject.[ddl|cc].
Back to the Table of Contents
By convention, the persistent objects are created and stored in the Configuration Database by the static className::create() method. Below is an example:
BdbHandle(BdbConfigSampleObject)
BdbConfigSampleObject::create( BdbConfigWriter* theDatabase)
{
assert( 0 != theDatabase );
BdbHandle(BdbConfigSampleObject) outHandle=0;
// create a temporary object in a scratch container
BdbHandle(BdbConfigSampleObject) inHandle =
BdbConfigNew(theDatabase) BdbConfigSampleObject;
//
// store this object and get a handle to the persistent object back
//
char* secondaryKey = "Default";
outHandle = ( BdbHandle(BdbConfigSampleObject)& )
theDatabase->store( inHandle, secondaryKey);
//
// don't forget to delete the temporary object !
//
BdbDelete(inHandle);
return outHandle;
}
There are three steps in the process:
- Create a temporary object. For security reasons, the objects are first created in a scratch container of the subsystem database (the scratch container is deleted when the database is closed). A macro BdbConfigNew is provided for
convenience.
- Store the temporary object in the database. Method BdbConfigWriter::store() copies the temporary object into the appropriate container and returns a handle to the new persistent object.
- Delete the temporary object. This is a good practice although not absolutely necessary since the scratch container is deleted when the database closes.
The persistent object can be modified once the handle to the new persistent object is obtained (but before the transaction is committed). Thus, if your class has an embedded ooVArray, you can first create an object with an empty array,
store it in the database, and then add elements to the array. See example in BdbConfigTests/BdbConfigWriteTest.cc. Important: once the transaction is committed,
the configuration object cannot be modified.
Class BdbConfig/BdbAbsIndex provides a facility for creating (almost) arbitrary indices over the containetrs in the Configuration Database. It has a single member
function:
virtual bool createIndex(const BdbHandle(BdbConfigContObj)& containerH) = 0;
The virtual method className::getIndex() should return a non-zero pointer to the object of class BdbAbsIndex if the objects of class className are to be indexed. Once the indices are created in the database, they can be
used to fetch the configuration objects via an abstract BdbAbsQuery interface.
Back to the Table of Contents
In the online environment, the primary method for accessing the configuration data is by using a Configuration Key generated by the RunControl. The DataFlow delivers a single 32-bit Key on a configure transition; this RunControl Key is thus global.
A Configuration Tree is created to do the translation to the container-specific Keys. The intermediate nodes of the Tree are objects of class BdbConfigMap while the
leaf nodes are the Configuration objects. The BdbConfigMap class is a hash table that associates character labels (subsystem names, sub-subsystem labels, class names) to the persistent objects.
The top level maps are indexed by the RunControl Configuration Key and contain links to the subsystem maps. The subsystem maps point to the Configuration objects, or, in turn, to the sub-subsystem maps, if a hierarchical structure within the subsystem
is needed. Thus, given a RunControl Key and a path to a configuration container, the Tree can be navigated to find the appropriate object.
A tty menu-based application BdbConfig/BdbConfigCreateMaps.cc and a GUI BdbConfigBrw (package
BdbConfigBrowser) exist to create the Configuration Tree.
Back to the Table of Contents
The standard mechanism for accessing the objects from the Configuration database is ProxyDict. The proxy is used to provide a type-safe access to the objects, and to maintain a cache. The proxies are stored in the global dictionary
gblPEnv. The templated class BdbConfigProxy provides the interface. The proxies are stored with a secondary key which normally corresponds to the secondary key
with which the object was stored (see "Storing Objects in the Configuration Database") or the Configuration Tree path. The Configuration Key is passed to the proxy via AbsArg. The proxy owns a cache object
of class BdbConfigCache. The latter is an abstract interface; the access mode and the caching mechanism. There are three types of access modes:
- With the Configuration Key via the Configuration Tree. The Key is this case is the RunControl Key. This is the main mode for the online. (Remains to be written.)
- With the Configuration Key directly via the class name and the secondary key. The Key in this case is local to the container in which the objects reside. This is useful for debugging, offline or standalone environment, and for creating the
Configuration Tree. This mode is provided by BdbConfigDirectCache class.
- Via the class name and the secondary key, using a user-defined BdbAbsQuery class. The derived class provides the following methods:
- virtual const ooLookupKey* getLookupKey()
Returns a non-zero pointer to the lookup key if the query uses an index
- virtual bool evaluate( const BdbHandle(BdbConfigObject)& ) const
Returns true if the object matches the query
(Example remains to be written).
Back to the Table of Contents
It is a very good idea to decouple the clients of the database data from the database interface and the database itself. Such architecture is used in the Event Store and in the online calibration system. In can also be implemented for the configuration
objects. The client accesses the transient object via ProxyDict, and the conversion from the persistent to transient object happens behind the scenes in a proxy. Such proxy is provided as a doubly templated class
BdbConfigTransientProxy. It is templated on the transient class T and the persistent class P. The proxy uses BdbConfigCache object to look the persisent object up. It then creates a transient object of class T
using P::transient() method. This method has to be provided in the class P. An example can be found in OepConfig package: OepConfigFile and OepBdbConfigFile classes.
Similar proxies are used to convert the persistent objects to subclasses of odfXTC that can be transported via the Reverse DataFlow to the ROMs. Class BdbConfigTCProxy is the base class for such proxies. faultHandler()
of this proxy is implemented; the subclasses have to provide
virtual YTC* fillXTC(const BdbHandle(Persistent)& pObject,
odfArena& theArena)=0;
method. An example of BdbConfigTCProxy subclass and its use can be found in CalConfig and DrcConfig packages.
Back to the Table of Contents
A system similar to AppUserBuild in the offline is set up to help build the browser and database server applications. Such applications typically access the persistent objects via their base classes. One common problem is making sure that the
virtual table for a class is linked in the application. The server application also needs the proxies for the XTCs to be loaded.
By convention, each detector subsystem would provide an XxxConfigInstantiate class, a subclass of BdbConfigInstantiate base class. Two virtual methods can be overridden:
virtual bool vtables();
virtual bool proxies(IfdProxyDict* dict);
The former is to instantiate all persistent classes for a subsystem, e.g.
bool
DrcConfigInstantiate::vtables( )
{
bool rstatus = true;
if ( ! _vtabled ) {
DrcSimpleCycleFactory simpleFactory;
_vtabled = true;
}
return rstatus;
}
The latter method is used by the server to load the proxies for the subsystem:
bool
DrcConfigInstantiate::proxies(IfdProxyDict* theDict )
{
bool rstatus = true;
if ( ! _proxied ) {
// Proxy for DrcSimpleCycleFactory
BdbConfigCache<DrcSimpleCycleFactory>* theCache2 =
new BdbConfigTreeCache<DrcSimpleCycleFactory>("Drc/CalCycles");
CalCycleProxy<DrcSimpleCycleFactory>* theProxy2 =
new CalCycleProxy<DrcSimpleCycleFactory>(theCache2);
rstatus = BdbTCDictFE< CalCycleTC >::put( theDict, theProxy2,
IfdStrKey("DrcSimpleCycles") );
_proxied = true;
}
return rstatus;
}
A package that builds a browser or server application than needs to have a BdbConfigAppBuild.cc file and register all XxxConfigInstantiate "modules" there. See DrcConfig and BdbTCServer packages for examples.
Back to the Table of Contents
The session of the Configuration database is controlled by the BdbConfig class. In the Framework environment, the session is started by a dedicated module (see BdbConfig/BdbConfigModule for example). This modules also loads the default Configuration Key into the gblPEnv dictionary. In the offline environment,
BdbConfig/BdbConfigModule starts the session and loads the Key in the begin() method, it must be added to the path before any other Framework module that accesses the Configuration database (see BdbConfigTests/AppUserNuild.cc). In the OEP environment, the session is started by an OepFramework module in the configure method.
Back to the Table of Contents
|