The IOPC library [04] contains a new API for O/R mapping. This new interface coexists with the old POLiTe-style interface inside one library. The interface is (with few exceptions) clearly divided into two parts - the IOPC and the POLiTe part. The POLiTe part provides backward compatibility with the original POLiTe library.

The IOPC library consist of a number of modules, some of them are standalone applications. The hi-level architecture overview can be best explained on the library workflow displayed in Figure 3.8, “The IOPC library workflow”.


Three main modules can be observed from the figure:

  • IOPC SP uses OpenC++[17] parser and source-to-source translator to modify the source code to add support of the object persistence and generates XML metamodel description of structure of persistent structure.

  • IOPC DBSC generates SQL scripts from the XML metamodel description. The SQL scripts create required database structured needed for the IOPC object-relational mapping.

  • IOPC LIB is a library that provides the object persistence services to a program to which it is linked. It uses the metamodel generated by the IOPC SP module and database structures created using the IPOC DBSC scripts.

  • XML metamodel description loading and storing is performed by two statically-linked libraries / classes - XMLMetadataLoader and XMLMetadataWriter. Both classes use the Xerces[18] parser to handle the XML files. Other type of metamodel storage can be implemented by inheriting from the MetadataLoader and MetadataWriter interfaces.

One of the most interesting aspects of the IOPC library is a new approach to metamodel description retrieval. User-created specification of the class structure is not needed. The source classes are parsed and metamodel description is collected by the IOPC SP module.

IOPC SP is a standalone executable created from patched OpenC++ source code and a OpenC++ metaclass IopcTranslator. The metaclass affects the source-to-source translation of persistent classes done by the OpenC++ by performing the following operations:

In the end, IOPC SP runs compiler and linker on the translated source code.

As you can see in the Example 3.7, “Definition of persistent classes in IOPC”, persistent classes in IOPC does not need any additional descriptive macros for the persistence layer to be able to understand their structure. The only rules are that they need to be descendants of the IopcPersistentObject and that the attribute types need to be supported by IOPC. For example, IOPC does not understand the std::string STL type, only the basic C/C++ string representation char* (or its wide character variant) is supported. If the string is allocated dynamically, it needs to be deallocated in the desctructor.


Processed source code generated by the IOPC SP is deleted immediately after compilation. It is not meant to be modified by developers. Following example displays the processed Student class. It was stripped by the age attribute because the listing was too long:

class Person : public IopcPersistentObject {
public:
  Person() {
    set_name ( __null ) ;
  }
  virtual ~Person() {
    Free();
    if ( m_name != __null ) free ( m_name ) ;
  }
protected:
   char * m_name;
   bool m_name_isValid; 
public:
  virtual char * get_name()
  {
    if (!m_isPersistent || m_name_isValid || !m_classObject->isAttributePersistent(1)) return m_name;
    m_classObject->loadAttribute(1, this);
    if (!m_name_isValid)
      throw IopcExceptionUnexpected();
    return m_name;
  }
public:
  virtual char * set_name(char * _name) {
    m_name =_name;
    m_name_isValid = true;
    if (m_isPersistent) MarkAsDirty();
    return _name;
  } 
protected:
  void iopcInitObject(bool loadingFromDB) {
    if (loadingFromDB) {
      m_name_isValid = false;
    }
    else {
      m_classObject = IopcClassObject::getClassObject(ClassName(), true);
      m_name_isValid = true;
    }
  }
  virtual int iopcExportAttributes(IopcImportExportStruct * data, int dataLen) {
    if (dataLen != 1) return 1;
    data[0].valid = m_age_isValid;
    if (m_age_isValid) data[0].shortVal =  m_age;
    return 0;
  }

  virtual int iopcImportAttributes(IopcImportExportStruct * data, int dataLen) {
    if (dataLen != 1) return 1;
    if (data[0].valid) {
      if ((m_name_isValid) && (m_name)) free(m_name);
      m_name = strdup(data[1].stringVal);
      m_name_isValid = true;
    }
    return 0;
  } 
public:
  static const char * ClassName() {return "Person";} 
public:
  static IopcPersistentObject * iopcCreateInstance() {
    IopcPersistentObject * object = new Person;
    object->iopcInitObject(true);
    return object;
  }
  static RefBase * iopcCreateReference() {
    return new Ref<Person>;
  }
};
static IopcClassRegistrar<Person> Person_IopcClassRegistrar("Person");

Rather larger amount of code was inserted into the class definition. IOPC SP generated setter and getter methods for the attributes, serialisation and deserialisation routines (iopcExportAttributes and iopcImportAttributes) and other methods needed by the persistence layer.

Not only the class code was translated. Code that reads or sets attribute values was changed to use the getter and setter methods (like we would use in the POLiTe library):

// original:
Employee e;
e.age = 45;
// translated assignment operation:
e.set_age(45);

IOPC SP also generates a XML metamodel file describing structure of the classes, see Example 3.8, “XML metamodel description file”.


As mentioned before, persistent attributes can be divided into several groups which are handled by IOPC separately. By default, two groups are generated - a default_fetch_group containing all numeric attributes and a 1st_persistent_group containing attributes of all remaining data types (strings). The XML metamodel file can be customized by developers before running IOPC DBSC.

IOPC DBSC is a standalone executable that generates SQL scripts for various purposes - in particular the scripts to create or delete database structures required by the object-relational mapping. It uses MetadataLoader to load the persistent class metamodel written by the IOPC SP. SQL script created from the previously generated XML metamodel file follows.

First it creates database table for the class list and fills it:

Followed by a table (main table) containing OIDs of all persistent objects in the project:

Then the mapping tables associated with persistent classes are created (we used the default vertical mapping):

Finally, two views are created for each persistent class. Simple views (SV suffix) join all tables needed for loading complete instances of particular persistent classes. Their columns correspond to attributes of the associated classes. Polymorphic views (PV suffix) have same structure as simple views: the difference is that they return not only instances of the associated classes, but also instances of their descendants.

IOPC LIB is a shared library that represents the core of the IOPC project. It is linked to the outputs (object files) of IOPC SP and provides the run-time functionality.

IOPC LIB is built on the POLiTe library, it uses some of its components and exposes its new interface side-by-side with the original POLiTe interface. The result is that the IOPC library can be used almost the same way as its predecessor. It supports the POLiTe-style persistent objects as well as new persistent objects inherited from the IopcPersistentObject class and processed with IOPC SP. Components from the POLiTe library that IOPC LIB uses are displayed in Figure 3.9, “POLiTe library components used in IOPC LIB”.


Because the object cache and the object references are reused from the POLiTe library, persistent object enter same states using same state transitions as in the section called “Persistent object manipulation”. There is, however, one problem with the current implementation in that it does not implement the locking correctly and all persistent objects look like unlocked all the time. So the Locked Local Copy state can be entered only by instances of the POLiTe persistent classes.

The query language and all related classes or templates are also reused, so there is no change in this area either.

IOPC persistent objets can be associated exclusively using references as described at the end of the the section called “Metamodel and object-relational mapping.”. Relations described earlier in that section can be used only for the POLiTe persistent objects. IOPC adds a new RefList<T> template to the standard POLiTe Ref<T> reference representing a persistable list of references. The list is stored into a separate table (one per project) which unfortunately does not have standard many-to-many join table schema. Each instance of the RefList<T> is stored as a linked list of OIDs of its members. The table is completely unusable in SQL queries unless we are using its recursive features (if available) or procedural constructs like cursor iteration. Second issue is that these lists are always persistent. Every change is immediately propagated to the database regardless of the state of its owner, object cache is also bypassed. This renders the usage of RefList<T> in combination with transient objects quite dangerous as orphaned entries are created in the "join" table. Same problem occurs if persistent object with RefList<T> as a attribute is deleted - the associated linked list is not removed from the database.

Figure 3.10, “Structure of the IOPC LIB” displays the structure of the IOPC library and the relationship between new IOPC- and the original POLiTe interface.


Database layer uses slightly modified original interface. Along with the Database, Connection and Cursor classes there is a new interface class - DatabaseSqlStatements. It serves as an interface for database-dependent SQL statement generation. IOPC contains Oracle 8i implementation of these interface classes (using OCI 8).

IOPC persistent classes are created as descendants of the new base class - IopcPersistentObject. The original base class Object and its descendants (ImmutableObject, DatabaseObject and PersistentObject) were preserved and can be used to create POLiTe persistent classes. Note that IopcPersistentObject is derived from the original Object class allowing it to be used with POLiTe components like object cache or references.

For each descendant of the IopcPersistentObject class there is one IopcClassObjectImpl instance. The purpose of this class is very similar to the concept of prototypes described in the the section called “Metamodel and object-relational mapping.” - it contains all data needed for object-relational mapping of the associated class. IopcClassObjectImpl actually performs the mapping process. The class is linked to the POLiTe prototype system using the IopcProtoBaseAdaptor which maps the prototype interface on the IopcClassObjectImpl instance. Calls invoked on its ProtoBase interface are delegated back to IopcClassObjectImpl. Public interface to the IopcClassObjectImpl class is provided by the IopcClassObject class.

As the name suggests, IopcClassObjectContainer is a place where class object instances are stored. Its first responsibility is to load list of classes from the MetadataLoader and compare them with the class list stored in the database. Then it creates and initializes the IopcClassObjectImpl instances. Similarly to ClassRegister, it provides methods allowing us to find the class objects by name or by class id.

The main goal of the IOPC library was to make the usage of the POLiTe library simpler and more transparent. The architecture of the library was redesigned for the sake of these requirements. At first glance, the IOPC library looks like a big improvement over the original library, but if we look further into the source code and used technologies, we come upon several critical issues that may cause the usage and deployment of this library almost impossible.

IOPC uses the OpenC++ as a way to retrieve information about the structure of persistent classes. The OpenC++ project seems to be almost dead, last commit to the project CVS occurred in 2005. OpenC++ cannot handle most of template constructs, processing files that include GCC STL headers (tested on versions 3.4 and newer) produces a lot of errors. Because the source-to-source translation is used, users cannot be sure if any of their code translated with errors or warnings[19] will do what was intended. Probably for this reason the IOPC allows only the C strings (char* and w_char*), not the C++ STL strings (std::string and std::wstring).

Next, IOPC uses its own modified version of OpenC++ (called 2.6.t.0) and integrates it into the IOPC SP utility. This approach renders further maintenance of the OpenC++ code difficult. New changes from the OpenC++ CVS have to be merged manually into the IOPC SP source.

Second point is a question, why there are two parallel interfaces in the IOPC library - one for the new persistent objects and one for the POLiTe objects. If there is no known implementation that uses the POLiTe library, there is no need to be backward-compatible. The POLiTe part of the source code will just remain unmaintained (as no one is supposed to use the POLiTe objects in new applications). Because several IOPC objects inherit from the POLiTe classes, many inherited methods do not make sense any more. This makes the API less comprehensible and can lead to user's confusion. An example of this situation is the IopcProtoBaseAdaptor which contains a number of methods commented as "Fake function". Second example is the mentioned local copy locking problem. Are we supposed to use the locking only on the POLiTe persistent objects or is it just a bug in the IOPC implementation? The presence of two distinct APIs makes usage of the library less clear and confusing.

The library itself remained as a monolithic block with only signs of library configurability. Many parameters are defined as pre-processor macros, enabling other options (like adding a new database driver) results in library source code modification and recompilation. The design of the library even makes impossible to use more than one database driver at a time (the currently used implementation of the DatabaseSqlStatements is stored in a global variable).

Bad design of the program interface. Useful information is hidden in internal structures of the library, these structures are not visible via its API - for example the data retrieved from the MetadataLoader. The library could implement some kind of reflection API to be able to query the metamodel.

The library cannot be used in multithreaded environment. There are shared state-aware data structures that are reused between persistence layer calls. This prevents to make the library multithreading-friendly without major modifications.

Despite good idea behind the IOPC library, the implementation is deeply flawed, unusable and unmaintainable. For these reasons, the author of this thesis decided not to continue development upon the source code of IOPC.



[17] http://opencxx.sourceforge.net/
[18] http://xml.apache.org/xerces-c/