Keywords: doc

Next: Migrating Objects Up: BOA Previous: Activation Mode Library

## Making Objects Persistent

In the last section we saw two cases where an object had to be ``moved'' between two different instances of a server:

- if an unshared or per method server creates a second object it has to be moved to a new server instance.
- if a server terminates and is restarted later all the objects of the terminated server have to be moved to the restarted server.

In all these cases the state of the moved object has to be saved before and restored after moving. Because the BOA has no information about the internal state of an object the user has to provide code for saving and restoring. However, the BOA offers you some support methods.

Saving is done in the `_save_object()` method of the object implementation. If you do not provide this method for an object, `_save_object()` from the base class will be used, which will cause the object to be treated as transient (i.e., it will not be restored later). Let us again consider the account example. The internal state of an account object consists of the current balance. Here is how to save the state:

     1:  // account_server3.cc
     2:
     3:  #include "account.h"
     4:  #include <iostream.h>
     5:  #include <fstream.h>
     6:
     7:  class Account_impl : virtual public Account_skel {
     8:    CORBA::Long _current_balance;
     9:  public:
    10:    ...
    11:    virtual CORBA::Boolean _save_object ()
    12:    {
    13:       ofstream out (_ident());
    14:       out << _current_balance;
    15:       return TRUE;
    16:    }
    17:  };

Pretty simple, eh? We just open a file and write the balance into it. The only noteworthy thing is the file name, which is obtained by using the `_ident()` method. The returned string is guaranteed to be unique among all objects managed by a single BOA daemon. If you use multiple BOA daemons or use persistent servers that do not register with the BOA you have to make sure no name clashes occur. One way to do this is to create a new directory where all the files are created, in our example `/tmp/account/` would be appropriate. Another way to distinguish different instances (objects) of on interface (class) is to use `BOA::ReferenceData`. See `demo/account2` for an example.

Restoring the state takes a bit more code. You need to subclass the abstract baseclass `CORBA::BOAObjectRestorer` providing an implementation for the `restore()` method:

     1:  // account_server3.cc
     2:
     3:  class AccountLoader : public CORBA::BOAObjectRestorer {
     4:  public:
     5:    CORBA::Boolean restore (CORBA::Object_ptr obj)
     6:    {
     7:       if (!strcmp (obj->_repoid(), "IDL:Account:1.0")) {
     8:         new Account_impl (obj);
     9:         return TRUE;
    10:       }
    11:       // dont know about such objects
    12:       return FALSE;
    14:    }
    15:  };

`restore()` receives an object reference for the object that has to be restored. We use the `_repoid()` method to find out the repository id of the object to be restored. If it is equal to the repository id of account objects (`"IDL:Account:1.0"`) we can go on with restoring, otherwise we just return `FALSE` indicating that we cannot restore the object.

Restoring the object is now just a matter of calling a special `Account_impl` constructor which we still have to define:

     1:  // account_server3.cc
     2:
     3:  class Account_impl : virtual public Account_skel {
     4:    CORBA::Long _current_balance;
     5:  public:
     6:    ...
     7:    Account_impl (CORBA::Object_ptr obj)
     8:      : Account_skel (obj)
     9:    {
    10:      ifstream in (obj->_ident());
    11:      in >> _current_balance;
    12:    }
    13:  };

The constructor is basically the counterpart to `_save_object()`. It uses `_ident()` to obtain the identification string of the object to be restored, opens the associated file and reads in the current balance. Note the invocation of the base class constructor in line 8, which is very important. If you forget this line the code will still compile but will give you strange results, because the default `Account_skel` constructor will be used, which is an error.

Note that we have omitted error handling for the ease of exposition. Usually one would check if the file exists and its contents are valid. If an error is detected you should make `AccountLoader::restore()` return `FALSE`.

Now what is left to do is to create an instance of the `AccountLoader` class. Note that you have to create at least one such instance before you do ORB and BOA initialization, because restoring can already occur during BOA initialization. Of course you can create serveral different `BOAObjectRestorer` subclasses each of which handles special kinds of objects. When an object has to be restored the `restore()` methods of the existing restorer objects are called until eventually one returns `TRUE`. Note that you should not create new objects if any objects are being restored, because otherwise you would get an infinitely growing number of objects over time. The BOA method `restoring()` returns `TRUE` if objects are being restored, `FALSE` otherwise. Here is the `main()` function:

     1:  // account_server3.cc
     2:
     3:  int main (int argc, char *argv[])
     4:  {
     5:    // create loader *before* BOA initialization
     6:    AccountLoader loader;
     7:
     8:    CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb");
     9:    CORBA::BOA_var boa = orb->BOA_init (argc, argv, "mico-local-boa");
    10:
    11:    if (!boa->restoring()) {
    12:      // create new objects only if not restoring
    13:      new Account_impl;
    14:    }
    15:    boa->impl_is_ready (CORBA::ImplementationDef::_nil());
    16:    orb->run ();
    17:    return 0;
    18:  }

In an unshared or per method server you would call

      boa->obj_is_ready (CORBA::Object::_nil(),
                         CORBA::ImplementationDef::_nil());

instead of `impl_is_ready()`. The sources for a complete example can be found in `demo/account2`.

Sometimes it is handy to know when saving of objects can occur. But you cannot rely on this being the only occurences of object saving:

1. Just before a server is exiting all the objects that have not been released are saved. If you do not want an object to be saved you must make its `_save_object()` method return `FALSE` or do not provide a `_save_object()` method at all. The object will then be treated as transient (i.e., it will not outlive the process it was created in).
2. When you call `deactivate_obj()` on an object in an unshared or per method server saving is done during the call to `deactivate_obj()`. Objects saved this way will not be saved again at server exit according to 1.
3. When you call `deactivate_impl()` in a shared or persistent server saving of all currently activate objects is done during the call to `deactivate_impl()`. Objects saved this way will not be saved again at server exit according to 1.
4. When you migrate an object saving of it is done during the call to `change_implementation()`, see section 4.3.6 for details. Objects saved this way will not be saved again at server exit according to 1.

Note that it is quite likely that invocations on objects will occure after a call to `deactivate_obj()`, `deactivate_impl()`, or `change_implementation()` because the server has to execute all (buffered) invocations that arrived up until your call to one of the above mentioned methods. So your code must be prepared to handle this.

Although the actual code for saving and restoring the state of an account object are two-liners each real world applications often require complex code for making objects persistent. Therefore the OMG has specified the Persistent Object Service (POS), an implementation of which is not yet provided by MICO.

Next: Migrating Objects Up: BOA Previous: Activation Mode Library