Payload Converters and Technology-Neutral Proxies in CDB

Igor Gaponenko

March 21, 2005

Table Of Contents

1 A justification for converters

    1.1 Persistent technology dependencies in the current Framework applications

    1.2 Why is this a problem?

        1.2.1 Subsets of the full production CDB

        1.2.2 Migrating CDB to the ROOT I/O

    1.3 A proposed solution

2 Technology-neutral transaction management

    2.1 The CdbTransaction class

    2.2 Migrating existing clients

        2.2.1 Transaction management in proxies

        2.2.2 Transaction management in loaders and standalone applications

3 Payload converters

    3.1 Technology specific converters

        3.1.1 Converters for the "Roo" technology

            3.1.1.1 Trivial conversion: the CdbRooObjectConverterR2T class

        3.1.2 Converters for the "Bdb" technology

            3.1.2.1 Trivial conversion: the CdbBdbObjectConverterP2T class

    3.2 Using converters

4 Technology-neutral proxies

4.1 Environment proxy: the CdbEnvProxy class

4.2 Proxies with a non-trivial communication with its converter(-s)

4.3 Many-to-one proxies

4.4 Many-to-one proxies with a non-trivial communication with its converter(-s)

5 Sample code snippets used throughout the document

1 A justification for converters

For well known historical reasons, the large amount of CDB client code implemented in present Framework-based applications still has either direct or indirect dependency on the Objectivity/DB API [OBJECTIVITY/C++ API]. In the past, when Objectivity/DB was essentially the only persistent technology adopted by the BaBar Experiment, this dependency wasn't considered a big problem (even though some might say that this had to be predicted long time ago or, that a way the clients' code is designed and implemented might not be well justified from an architectural point of view). However, in a view of recent dramatic changes in a landscape of the BaBar software [CM2] as well as due to emerging requirements to CDB, the dependency has become a  serious limiting factor for improving Framework applications and making them ready for other persistent technologies.

This document is discussing various aspects of the above stated problem and proposing a solution which is considered the most optimal one in the current context.

ATTENTION: Readers should know that all relevant CDB API support classes and interfaces mentioned below have already been implemented as described in the document. Though, the design and the implementation of this part of the CDB API is yet to be finalized. They may change in a course of gaining a better understanding of how to proceed with this project.

Also keep in mind that he document will only discuss read-only clients of the CDB implemented using the BaBar Framework.

1.1 Persistent technology dependencies in the current Framework applications

One of the cornerstone concepts in the foundation of CDB is a separation of metadata from payload:

DEFINITIONS: We define the payload as (1) a user defined schema of the database, and (2) as a user applications' generated contents of the database. The metadata represents an additional information describing the payload. It includes so called intervals of validity, revisions, conditions, views, origins, etc. The metadata implemented in a form of persistent data structures provides a sort of a framework where the user defined/generated payload gets stored in.

This separation has also been reflected in the current design of CDB API. Most of the API in its part dealing with the metadata is fully technology neutral (at least in terms of direct dependencies). Meanwhile, when it comes to storing or getting the user defined payload then there is a direct and controllable (through specially designed "technological" CDB API extensions) breach to a persistent technology specific API. This decision has been made quite consciously in order to cope with a variety of user defined classes (as well as over 1 million stored objects) inherited by CDB from the previous generation Condition/DB. This dependency is unavoidable - somehow the CDB API has to provide a way to deal with user defined persistent classes (of a whatever persistent technology being used) and payload objects.

Primary sources of the persistent technology specific dependency for Objectivity/DB:

Direct clients affected by the dependency are:

A schematic representation of the resulting architecture is shown at a figure below.

[FIGURE ]

1.2 Why is this a problem?

During the past year we've seen two major developments directly affecting the use CDB and potential trends in its evolution (as a software and as a persistent store). A significant "resistance" of existing clients' code when pushing forward these changes has triggered an analysis of the current model of CDB proxies.

1.2.1 Subsets of the full production CDB

In an attempt to reduce a size of a database installation for a specific use of the CDB, the so called "beta-mini" subset [HOW-TO-SUBSET] has been successfully developed and deployed. The amount of data in the subset is nearly 20 time less than 32 GB found in the today's full production CDB. The subset includes enough conditions (a specific CDB term for: detector alignments, constants, calibrations, etc.) to run a properly configured BetaMiniApp analysis application for a full range of events acquired or modeled in a context of the Experiment. A desire to eliminate difficulties imposed by managing and importing the full size CDB were initial motivations behind this development. Another not-to-be-ignored driving force was a desire expressed by many physicists to be able to run their analysis jobs on notebooks. Clearly, the 32 GB of databases size is "too much" even for hard drives of contemporary notebook computers (which typically have from 40 to 80 GB). Besides, the database keeps growing as new calibrations (both in ONLINE and OFFLINE) are put into it.

A natural solution, on a way of reducing the size of the subset, was to "stripe down" its contents to leave only those conditions which are really needed to run the BetaMiniApp. The application itself also had to be configured not to use conditions beyond the subset. This goal had required to conduct a special investigation to figure out which conditions are relevant in this context and which aren't. In a course of this development it was "(re-)discovered" that we have no fine-grain control over which conditions are really needed for certain applications. That's because there is a thick layer of code in a form of so called "proxies" and "environment modules" laying in between the CDB API and actual recipients/users of the data fetched from CDB.

Another issue which is relevant in the context of the current document is related to the persistent schema. Even though the behavior of the BetaMiniApp has been "striped down" (in terms of used conditions), its code hasn't practically changed. It still depends on code (and persistent classes) of all conditions. This dependency alone is not a big problem on its own, what can be more serious is a questionable maintainability of this code when another persistent technology will be deployed in production.

In addition, we seem to face a strong push toward producing a subset of CDB to run Simulation Production jobs. With a lack of well defined fine-grain control over which conditions and which persistent classes are used by an application it would be hard to move forward of utilizing benefits of the "subsets".

1.2.2 Migrating CDB to the ROOT I/O

A far more serious limitation of the current architecture of the (CDB) client code was realized when we started moving CDB away from Objectivity/DB to ROOT I/O. A major complication of this migration is that both persistent technologies will co-exist in production applications for at least 1 year (a personal estimate of the author of the document). That's because (due to known restrictions of the ROOT I/O, which isn't really a "database" technology) the migrated CDB will initially be deployed in a read-only form. At the mean time, all critical production CDB installations, where new data will be put into, will still be using CDB in the Objectivity/DB format. As it's been explained in the "dependency" section 1.1 performing the migration in the client code in the "straightforward" way is going to create enormous amount of duplicated code. So, for example, for each condition there will be two proxies, two versions of the corresponding module instantiating the proxy and two versions of a sequence making sure that related module is linked and engaged by an application. Maintaining this code even in a mid-term is going to be a non-trivial and definitively a error prone task.

1.3 A proposed solution

A general idea is to make proxies technology neutral by generalizing or eliminating the following persistent technology specific operations:

For the transaction management, a problem has been solved with a new CdbTransaction class recently added to the core CDB API. See section 2 of the current document for more details.

For the "persistent-to-transient" conversion function of proxies, this function is moved out of proxies into a separate conversion facility (including a dictionary of converters) managed via extended core CDB API. The API extension is designed in a way allowing to invoke the (technology-specific) user defined converters by technology-neutral interfaces. It's also used to register the converters in the facility. Proxies are still responsible for triggering the conversion in order to produce a valid transient product. In order to get to the right converter, proxies communicate with the facility by mean of two keys:

Find more details on converters in the section 3 of the document.

The proposed architecture of new proxies and converters is shown on the following picture:

[FIGURE]

2 Technology-neutral transaction management

2.1 The CdbTransaction class

This class acts as a front-end to the transaction management performed in a context of either the current or explicitly specified CDB API implementation. The transaction services provided by this class are based on the resource-acquisition-is-initialization paradigm. The class will ensure a proper transaction upon the class's instantiation (when any of its public constructors is called). A new transaction may be started if none existed before, and it will committed when the class's destructor is being executed in case if the transaction was started by the constructor. The constructor has an optional parameter allowing to specify a desired mode of the transaction. By default it will be the read-only transaction. In addition to the regular start/commit operations, the transaction management class can also be used to invoke the commit-and-hold operation (see the corresponding method in the example below), which can be called to flush  results (modified/created persistent object) out of a process's cache (an actual semantic of this operation will depend on an underlying persistent technology and CDB API implementation).

Here is an example of the class's use:

 
#include "CdbBase/CdbTransaction.hh"
...
{
  // Here my block of code begins. It may also be a body of a method or function,
  // or a loop.
  //
  // Let's begin an UPDATE mode transaction. REMEMBER: the transaction
  // mode is an optional parameter of the constructor.
 
    CdbTransaction transaction( CdbTransaction::Update );
 
    ...<do whatever you want with the persistent store>
    ...<leave the current block at any time>...
    ...<don't worry about finishing transaction anymore>...
 
    transaction.commitAndHold( );    // Flush modifications back to the persistent store
 
    ..<keep modifying the database>...
 
}  // At this point the previously started transaction is guaranteed to be
   // automatically committed. This happens because when a control flow leaves
   // a block then destructors for every single variable allocated
   // within a block (except the ones assigned to local plain C pointers) will
   // be executed.

The basic model of the class is very similar to the one found in the Objectivity/DB specific implementations of the CDB API:

CdbBdb/CdbBdbTransaction.hh

The main advantage of the new class is that it doesn't expose any persistent technology-specific API-s at a compilation time. That extra flexibility comes comes with a price - now a developer of the proxy should be concerned about a context in which the class is going to be used. By default (it's exactly how it's shown in the above shown example) the transaction will be started n a context of the current CDB API implementation. This implementation is obtained with the following simple operation:

 
#include "CdbBase/Cdb.hh"
...
// Get a smart pointer onto the top-level CDB API object. This object
// opens a path to the rest of services provided by this implementation
// in a technology-neutral way.
 
  CdbPtr topLevelApiPtr = Cdb::instance( );
  ..
 

In order to cope with multiple CDB API implementations the CdbTransaction class has two special forms of its constructor. They're both shown on the excerpt from the class's interface (see a header file of the class for more details):

 
// File: CdbBase/CdbTransaction.hh
 
class CdbTransaction ... {
public:
 
  // Transaction modes
 
    enum Mode { Read, Update };
 
  // Start transaction in the current context in the specified mode
 
    CdbTransaction( CdbTransaction::Mode theMode = CdbTransaction::Read );
 
  // Start transaction in an explicitly specified context
 
    CdbTransaction( const CdbPtr&        theTopLevelApiPtr,
                    CdbTransaction::Mode theMode = CdbTransaction::Read );
 
  // Here is another way to specify the context
 
    CdbTransaction( const char*          theTechnologyName,
                    const char*          theImplementationName,
                    CdbTransaction::Mode theMode = CdbTransaction::Read );
 
  ... 
};
 

Here is another example illustrating a use of the transaction management class in a specific context:

 
#include "CdbBase/CdbTransaction.hh"
...
{
  // Start a READ mode transaction in a context of the "Readonly" implementation
  // of the CDB API based on the "Roo" technology (meaning that user defined objects
  // are serialzied into a persistent form using ROOT/CINT streamers).
 
    CdbTransaction transaction( "Roo", "Readonly" );
    ...

It's also obvious that the above described interface of the CdbTransaction class will also allow managing two or more (one transaction per context) independent transactions simultaneously.

2.2 Migrating existing clients

For the most of CDB clients, including proxies, the migration is really trivial.

2.2.1 Transaction management in proxies

A way proxies are used in Framework application doesn't assume a presence of two (or more) persistent technologies at a time. An application is normally built with one or another technology, and there is always a concept of the default implementation of the CDB API for that application. This is how all existing proxies are written. This observation simplifies the migration of proxies (and similar applications, which do not want/need to deal with a specific persistent technology) to the following simple rules:

If, for some reason, the context in which a transaction is supposed to be used won't match the one which is actually being used then the problem would be seen as a missing (or improper) transaction, followed by self-explaining complains from the CDB API and (quite possible) an application's crash. Usually this situation has never been considered as a dangerous (critical to a loss of data) one when it comes to reading conditions from the database. Therefore no special reinforcement (at the level of CDB API implementations) beyond what already exists is needed.

2.2.2 Transaction management in loaders and standalone applications

The migration rules for this class of applications are in general the same ones as for the Framework proxies. The only obvious exception would be a case of dual-technology applications. Here are possible candidates:

Should this be considered a problem it's suggested to use one of the additional constructors of the CdbTransaction class to tell it which CDB API implementation is meant to be used. The corresponding example of the client's code can be found at the sectin 2.1.

3 Payload converters

Converters are special classes extending CDB API and serving as placeholders for the actual code performing the persistent-to-transient transformation for technology-specific persistent objects stored in CDB. Converters are supposed to be registered with CDB API implementations during an application configuration stage (which is inevitably technology specific) and are used in the rest of the application, which now can be made technology-neutral.

The present design of converters is based on the following assumptions/requirements:

Converters are registered with CDB API using the following interface:

 
// File: CdbBase/Cdb.hh
 
class Cdb ... {
 
public:
 
  /// Register a user defined "persistent-to-transient" converter
  /**
   * A non-zero pointer is expected as a value of the method's parameter.
   * Also, note that the method will take an ownership of the passed object.
   *
   * @see class CdbObjectConverter
   */
    virtual CdbStatus registerConverter( CdbObjectConverter* theConverterPtr ); 
};
 

That CdbObjectConverter is a very base class of all converters. In addition to this class there is an infrastructure of derived converter classes providing foundation for technology specific converters. At a time of writing this document CDB is implemented in two technologies "Bdb" and "Roo". Each converter object also "knows" which persistent technology it's associated with. Any attempts to register it against an improper CDB API implementation will be stopped by the above shown Cdb::registerConverter method and signaled by the corresponding error status returned by the method. An object of the CdbObjectConverter class would also carry the information about a persistent and a transient types this object is associated with. These two types would be translated into the following keys:

The keys will be used to resolve the right converter when the persistent-to-transient conversion would be requested for the found metadata object (CdbObjectPtr). These keys will also be used by the Cdb::registerConverter method to prevent attempts to register more the one converter for a unique combination of keys. See more details in the class's interface:

CdbBase/CdbObjectConverter.hh

An interface of this class on its own doesn't represent any particular interest neither for developers of new converters nor for clients of converters. However this class is and essential part of the extended CDB API to have converters in the right place in the API's class hierarchy. What developers are supposed to be interested at are technology specific base classes (or complete solutions) for converters. The former are discussed in subsequent sections.

To check if the desired converter already exists the following method can be used:

 
// File: CdbBase/Cdb.hh
 
class Cdb ... {
 
public:
 
  /// Find a user defined "persistent-to-transient" converter
  /**
    * The method will look for the converter matching the specified transient
    * type identifier and the persistent type. The converter is supposed to be
    * registered for the current technology/implementation of the CDB API using
    * the Cdb::registerConverter() method.
    *
    * @see class CdbType2Id
    * @see Cdb::registerConverter()
    */
    virtual CdbStatus findConverter( CdbCPtr<CdbObjectConverter>& theConverterPtr,
                                     unsigned int                 theTransientTypeId,
                                     const std::string&           thePersistentTypeName ) const;
};

Note that a value of the transient type identifier is obtained using a special CdbType2Id class template. Here is an example:

#include "CdbBase/Cdb.hh"
#include "CdbBase/CdbType2Id.hh"
 
#include "MyPackage/MyTransientClass.hh"
 
  // Try to find the converter
 
    CdbCPtr<CdbObjectConverter> converterPtr;
    CdbStatus status = Cdb::instance( )->findConverter( converterPtr,
                                                        CdbType2Id< MyTransientClass >::id( ),
                                                        "MyPersistentClassR" );
 
    if(      CdbStatus::Success  == status ) cout << "Found"     << endl;
    else if( CdbStatus::NotFound == status ) cout << "Not found" << endl;
    else                                     cout << "Error"     << endl;
    ...

The next section will discuss persistent technology specific converter classes. They're all deriving from the very base one which has been described above. Then there will be a section on using converters for the persistent-to-transient transformation.

3.1 Technology specific converters

3.1.1 Converters for the "Roo" technology

All converters for the "Roo" technology should derive from the following abstract base class:

CdbRoo/CdbRooObjectConverterRT.hh

It's a class template defined as follows (only relevant parts of the class's contract are shown):

 
template< class R,
          class T >
class CdbRooObjectConverterRT ... {
 
protected:

  /// Translate into a transient object (persistent & transient type specific translation)
  /**
    * The method is supposed to be implemented by subclasses. The method is guaranteed
    * to be called with the correct parameters.
    */
    virtual CdbStatus toTransientRT( T*&               theTransientPtr,  /**< the pointer to be initialized with the result */
                                     const CdbCPtr<R>& theObjectPtr      /**< the input persistent object */
                                   ) const = 0;
};

The template has two parameters:

R - an input persistent type. Note that all persistent types in the "Roo" technology must derive from the CdbRooObjectR class. The type compatibility will be checkedt a compilation time.

T - a transient type of the the (output) product. Converters force no restrictions on this type.

An actual user-defined converter should implement the CdbRooObjectConverterRT::toTransientRT() method, return CdbStatus::Success if the conversion was successful and set up the pointer to point onto a newly created transient object.

3.1.1.1 Trivial conversion: the CdbRooObjectConverterR2T class

For those cases when a trivial conversion between a persistent and a transient types would exist as it's shown below:

 
class TRANSIENT;
 
class PERSISTENT : public CdbRooObjectR {
public:
 
  /// Produce a transient object out of the persistent one
 
    TRANSIENT* transient( ) const;
};

there is a special class template:

CdbRoo/CdbRooObjectConverterR2T.hh

Here is an illustration of how this class should be used for:

 
#include "CdbBase/Cdb.hh"
 
#include "CdbRoo/CdbRooObjectConverterR2T.hh"
 
#include "MyTransientPackage/MyTransientClass.hh"
#include "MyPersistentPackage/MyPersistentClassR.hh"
 
  // Register the converter
  //
  // MEMORY MANAGEMENT NOTES:
  //
  //   - if the converter is accepted then its ownership is also passed
  //     from the client's code down to the CDB API
  //
  //   - if the converter is turned down then it's up to the client's code
  //     to destroy the object.
 
    CdbObjectConverter* converterPtr =
        new CdbRooObjectConverterR2T< MyPersistentClassR, MyTransientClass >( );
 
    if( CdbStatus::Success Cdb::instance( )->registerConverter( converterPtr )) {
 
        cout << "Error: failed to registere the converter for" << endl
                "    PersistentType : MyPersistentClassR" << endl
                "    TransientType  : MyTransientClass" << endl;
 
        delete converterPtr ;
        ...
 

Note, that if the converter is turned down by the CDB API then must be properly disposed to avoid memory leaks.

3.1.2 Converters for the "Bdb" technology

All converters for the "Bdb" technology should derive from the following abstract base class:

CdbBdb/CdbBdbObjectConverterPT.hh

It's a class template defined as follows (only relevant parts of the class's contract are shown):

 
template< class P,
          class T >
class CdbBdbObjectConverterPT ... {
 
protected:

  /// Translate into a transient object (persistent & transient type specific translation)
  /**
    * The method is supposed to be implemented by subclasses. The method is guaranteed
    * to be called with the correct parameters.
    */
    virtual CdbStatus toTransientPT( T*&              theTransientPtr,  /**< the pointer to be initialized with the result */
                                     const BdbRef(P)& theObjectRef      /**< the input persistent object */
                                   ) const = 0;
};

The template has two parameters:

P - an input persistent type. Note that all persistent types in the "Bdb" technology must derive from the BdbObject class. The type compatibility will be checked at compilation time.

T - a transient type of the the (output) product. Converters force no restrictions on this type.

An actual user-defined converter should implement the CdbBdbObjectConverterPT::toTransientPT() method, return CdbStatus::Success if the conversion was successful and set up the pointer to point onto a newly created transient object.

3.1.2.1 Trivial conversion: the CdbBdbObjectConverterP2T class

For those cases when a trivial conversion between a persistent and a transient types would exist as it's shown below:

 
class TRANSIENT;
 
class PERSISTENT : public BdbObject {
public:
 
  /// Produce a transient object out of the persistent one
 
    TRANSIENT* transient( ) const;
};

there is a special class template:

CdbBdb/CdbBdbObjectConverterP2T.hh

Here is an illustration of how this class should be used for:

 
#include "CdbBase/Cdb.hh"
 
#include "CdbBdb/CdbBdbObjectConverterP2T.hh"
 
#include "MyTransientPackage/MyTransientClass.hh"
#include "MyPersistentPackage/MyPersistentClassP.hh"
 
  // Register the converter
  //
  // MEMORY MANAGEMENT NOTES:
  //
  //   - if the converter is accepted then its ownership is also passed
  //     from the client's code down to the CDB API
  //
  //   - if the converter is turned down then it's up to the client's code
  //     to destroy the object.
 
    CdbObjectConverter* converterPtr =
        new CdbBdbObjectConverterP2T< MyPersistentClassP, MyTransientClass >( );
 
    if( CdbStatus::Success Cdb::instance( )->registerConverter( converterPtr )) {
 
        cout << "Error: failed to registere the converter for" << endl
                "    PersistentType : MyPersistentClassP" << endl
                "    TransientType  : MyTransientClass" << endl;
 
        delete converterPtr ;
        ...

Note, that if the converter is turned down by the CDB API then must be properly disposed to avoid memory leaks.

3.2 Using converters

In order to use a converter, a user should have the following three things ready:

Here is an example:

 
#include "CdbBase/CdbTransaction.hh"
#include "CdbBase/CdbObject.hh"
 
#include "MyPackage/MyTransientClass.hh"
 
  ...
 
  // STEP I: Make sure there is a proper transaction
 
    CdbTransaction readOnlyTransaction;
 
  // STEP II: Find a persistent object (details of how it's done are omitted)
 
    CdbObjectPtr objectPtr = ...;
 
  // STEP III: Turn the above found persistent object into a transient one.
  //
  // In case of the successful completion of the operation the transient
  // pointer will be initialized to point onto the resulting object.
  //
  //   NOTE: The ownership will also be returned with the pointer!
 
    MyTransientClass* transientPtr = 0;
 
    if( CdbStatus::Success != CdbObject::transient( transientPtr,
                                                    persistentObjectPtr )) {
 
        cerr << "The persistent-to-transient conversion failed. Your job may" << endl
             << "not be properly configured for the following types" << endl
             << "  PERSISTENT : " << persistentObjectPtr->type( ) << endl
             << "  TRANSIENT  : MyTransientClass" << endl;
        ...
     }
 
  // Use the transient object & don't forget to dispose it
 
    ...
    delete transientPtr;

Note, that for proxies, finding the metadata objects an providing the transaction will be done by the  proxy base class: CdbProxyBase. For them the only relevant part of the above shown example is the last (third) step. See more details on proxies in the section 4 of the document.

Here is what's happening when the "CdbObject::transient()" method gets called:

3.3 More about converters...

One obvious limitations of the current model of converters is a limited communication between clients and converters. In a few words, this communication is the following: a client defines a transient pointer of the expected transient type and passes a reference onto that pointer along with a metadata object pointer to the CDB API for the conversion. This model leaves behind two potentially interesting use cases (as well as a combination of both of them):

A work on designing converters to satisfy this use cases is not complete yet. However, when (if) this will be done the current interface of converters won't change.

4 Technology-neutral proxies

All technology-neutral proxies derive from the following base class:

CdbBase/CdbProxyBase.hh

This class replaces the similar technology-specific classes:

CdbBdb/CdbBdbProxyBase.hh

CdbRoo/CdbRooProxyBase.hh

A way this new class is designed and implemented covers "almost everything" for what CDB proxies in the BaBar Framework exist. The first missing part, which is up to specific proxy developers, is to establish an appropriate conversion path between found persistent objects (available in a form of technology-neutral CdbObjectPtr objects) and a resulting transient product of the proxy. It's a developers' responsibility to trigger this persistent-to-transient conversion.

NOTE: For many (of not to say - for most) existing cases even this remaining task has an out-of-box solution. Read more on this in the subsection on "Trivial proxies".

Technically speaking, a proxy developer is supposed to derive from the proxy base class and implement the following method:

 
template< class T >
class CdbProxyBase ... {

protected:

  /// The redefined fault handler
  /**
    * This method is to be implemented by derived classes. It does the actual
    * job of constructing the transient product of the corresponding proxy.
    *
    * The method is called whenever the persistent cache of the current class is reloaded.
    */
    virtual T* redefinedFaultHandler( const std::vector<CdbProxyElement>& theListOfElements ) = 0;
};

When this method gets called it receives a list of persistent objects which are supposed to be used to produce a product object of the transient type T (the only parameter of the class template). The list is implemented as a vector of objects of the CdbProxyElement class. For most of existing proxies (the one-to-one proxies with a trivial relation between persistent and transient objects) there will be just one element in the vector. The most essential parts of the element class's contract are shown below:

 
class CdbProxyElement {

public:

  /// Get the full name of a condition including its path in a view

    std::string name( ) const;
 
  /// A metadata object for the found persistent object
 
    CdbObjectPtr objectPtr( ) const;
 
  /// A validity interval for the found persistent object
 
    BdbIntervalBase validity ( ) const;
 
  /// A flag indicating that the object has been just updated
 
    bool updated( ) const;
 
  /// Get the short condition name only (no path in a view)
 
    std::string shortName( ) const;
};

A use (usability) of an information delivered by these methods depends on a particular proxy. In fact, there is no much difference between existing technology-specific and the technology-neutral proxies in this respect. The most important change in the current context (of converters) is about a way persistent objects are made available through this class. For the technology-neutral proxies it's a metadata object pointed via a smart pointer of the CdbObjectPtr type. A developer of custom proxy is supposed to trigger converters for the found metadata object and use the resulting transient objects to produce a transient product of the proxy. In most cases (the above mentioned one-to-one proxies) that transient product is exactly what's returned from the converter.

The second thing to be taken care of by proxy developers is a configuration of proxies. This configuration is essentially done in the same way as for existing technology-specific proxies - through (and by) constructors of proxies. There is one little change though - all so called "strategy" objects are now technology-neutral. Here is a list of those (it shouldn't be too difficult for a reader of the document to find a right one resembling the corresponding technology-specific strategy):

CdbBase/CdbDefBkgFirst.hh
CdbBase/CdbDefBkgFirst.hh
CdbBase/CdbDefEventKey.hh
CdbBase/CdbDefFixTimeStrategy.hh
CdbBase/CdbDefStrategy.hh

A good example of how to build a custom proxy can be found in the out-of-box environment proxy of the one-to-one type discussed in the subsection below.

4.1 Environment proxy: the CdbEnvProxy class

For those one-to-one proxies for which a transient object returned from a converter is the one to be expected from a proxy there is a trivial out-of-box solution:

CdbBase/CdbEnvProxy.hh

Here is an example of its use:

 
#include "AbsEnv/AbsEnv.hh"  // the gblPEnv gloval variable is defined here
 
#include "ProxyDict/Ifd.hh"
#include "ProxyDict/IfdStrKey.hh"
 
#include "CdbBase/CdbEnvProxy.hh"
 
#include "MyPackage/MyTransientClass.hh"
 
  // Create an instance of the proxy which will serve objects of the specified
  // type. The proxy will get its persistent objects from a condition whose name
  // is built out of two string components passed to the constructor.
  // Note, that a default strategy object is assumed in this example.
 
    proxyPtr = new CdbEnvProxy< MyTransientClass >( "emc", "EmcFooClassP" );
 
  // Tell the ProxyDict to associate the above proxy with the transient type
  // and a key (see more detail on this in the ProxyDict package documentation)
  //
  // Memory management notes:
  //
  // - if the operation is successful and the proxy is accepted then the proxy's
  //   ownership will also be transferred to the proxyDict
  //
  // - if the proxy is refused (for example, because there is another proxy
  //   registered with the same values of parameters) then it's a client's code
  //   responsibility to dispose the refused proxy object.
 
    bool result = Ifd< MyTransientClass >::put( gblPEnv,
                                                proxyPtr,
                                                IfdStrKey( "/emc/EmcFooClassP" ));
    if( !result ) {
        cerr << "failed to put the proxy into the dictionary." << endl;
        delete transientPtr;
        ...

Note, that it's still a responsibility of the proxy's user to configure the proxy by passing the components of a condition name and an appropriate strategy object.

4.2 Proxies with a non-trivial communication with its converter(-s)

A solution will depend on the corresponding feature of the converters which is under development. See section 3.3 ("More about converters") for details.

4.3 Many-to-one proxies

A solution will depend on the corresponding feature of the converters which is under development. See section 3.3  ("More about converters") for details.

4.4 Many-to-one proxies with a non-trivial communication with its converter(-s)

A solution will depend on the corresponding feature of the converters which is under development. See section 3.3  ("More about converters") for details.

5 Sample code snippets used throughout the document

This section is for those who wants to find a quick answer to a relevant problem.

Using the CdbTransaction class

Finding the default CDB API implementation: the Cdb class

The public interface of the CdbTransaction class

Using the CdbTransaction class for an explicitly specified CDB API implementation

The public interface for registering converters

The public interface for finding converters

An example of finding a converter

A very base class for "Roo" converters: CdbRooObjectConverterRT

A trivial conversion model between persistent and transient classes in the "Roo" technology

Using the trivial converter in the "Roo" technology

A very base class for "Bdb" converters: CdbBdbObjectConverterPT

A trivial conversion model between persistent and transient classes in the "Bdb" technology

Using the trivial converter in the "Bdb" technology

An example of using the converters facility to convert a found persistent objects into a transient one

A base class for technology-neutral proxies: CdbProxyBase

An element class for technology-neutral proxies: CdbProxyElement

An example of instantiating an instance of the CdbEnvProxy class