next up previous
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 servergif:

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 idgif 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 FALSEgif.

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 up previous
Next: Migrating Objects Up: BOA Previous: Activation Mode Library

MICO
Tue Nov 10 11:04:45 CET 1998