[Up: Implementation Overview]
[Previous: Interface Repository] [Next: POA]

Subsections


BOA

The Basic Object Adapter (BOA) is the only object adapter specified by CORBA 2. One of its main features is the ability to activate object implementations4.4 when their service is requested by a client. Using the implementation repository the BOA decides how an object implementation has to be activated4.5.

To fulfill these requirements of the CORBA 2 specification the BOA is implemented partially by a library (libmico2.3.6.a) and partially by a separate program (micod) called the BOA daemon.


BOA Initialization

Similar to the ORB initialization described in section 4.1.1 the BOA has to be initialized like this:

  int main (int argc, char *argv[])
  {
     CORBA::ORB_var orb = CORBA::ORB_init (argc, argv, "mico-local-orb");
     CORBA::BOA_var boa = orb->BOA_init (argc, argv, "mico-local-boa");
     ...
  }

That way it has access to the applications command line arguments. After evaluating them the BOA will remove the command line options it knows about from argv. As for the ORB you can put BOA specific command line options into a file called .micorc in your home directory. Arguments given on the command line override settings from .micorc. Here is a list of command line options the BOA understands:

-OAId <BOA identifier>
 
Specify the BOA identifier, mico-local-boa is the only currently supported BOA identifier.
-OAImplName <name of the object implementation>
 
Tell a server its implementation name. This option must be used when launching a persistent server that should register with the BOA daemon.
-OARestoreIOR <IOR to restore>
 
This options is part of the interface between the BOA daemon and an object implementation. Do not use this option!
-OARemoteIOR <remote BOA IOR>
 
This options is part of the interface between the BOA daemon and an object implementation. Do not use this option!
-OARemoteAddr <remote BOA address>
 
This option tells an object implementation the address of the BOA daemon. You should use this option only when starting persistent servers that should register with the BOA daemon. See section 4.3.4 for details.


BOA Daemon

The BOA daemon (micod) is the part of the basic object adapter that activates object implementations when their service is requested. Moreover micod contains the implementation repository. To make all MICO applications use a single implementation repository you have to take similar actions as for the interface repository as described in section 4.2. That is you have to tell micod an address to bind to using the -ORBIIOPAddr option and tell all MICO applications this address by using the -ORBImplRepoAddr option. For example:

  micod -ORBIIOPAddr inet:<micod-host-name>:9999

Now you can run all MICO applications like this:

  some_mico_application -ORBImplRepoAddr inet:<micod-host-name>:9999

or you can put the option into .micorc and run some_mico_application without arguments.

micod understands the following command line arguments:

-help
 
Show a list of all supported command line arguments and exit.
-forward
 
This option instructs micod to make use of GIOP location forwarding, which results in much better performance (there is nearly no overhead compared to not using micod at all). Unfortunately this requires some client side GIOP features that some ORBs do not support properly although prescribed in the CORBA specification. Therefore you may encounter problems when using clients implemented using such broken ORBs. That is why this feature is off by default.
-db <database file>
 
Specifies the file name where micod should save the contents of the implementation repository when exiting4.6. When micod is restarted afterwards it will read the file given by the --db option to restore the contents of the implementation repository.


Implementation Repository

The implementation repository is the place where information about an object implementation (also known as server) is stored. The CORBA 2 specification gives you only an idea what the implementation repository is for, but does not specify the interface to it. So the design of the implementation repository is MICO specific. Here is the IDL for MICO's implementation repository:

 1:  module CORBA {
 2:    /*
 3:     * Implementation Repository Entry
 4:     */
 5:    interface ImplementationDef {
 6:
 7:      enum ActivationMode {
 8:        ActivateShared, ActivateUnshared,
 9:        ActivatePerMethod,
10:        ActivatePersistent,
11:        ActivateLibrary
12:      };
13:
14:      typedef sequence<string> RepoIdList;
15:
16:      attribute ActivationMode mode;
17:      attribute RepoIdList repoids;
18:      readonly attribute string name;
19:      attribute string command;
20:    };
21:
22:    /*
23:     * Implementation Repository
24:     */
25:    interface ImplRepository {
26:      typedef sequence<ImplementationDef> ImplDefSeq;
27:
28:      ImplementationDef create (...);
29:      void destroy (in ImplementationDef impl_def);
30:      ImplDefSeq find_by_name (in string name);
31:      ImplDefSeq find_by_repoid (in string repoid);
32:      ImplDefSeq find_all ();
33:    };
34:  };

Interface ImplRepository defined in lines 25-33 is the implementation repository itself. It contains methods for creating, destroying and finding entries. An implementation repository entry is defined by interface ImplementationDef in lines 5-20. There is exactly one entry for each server which contains

for the sever. The name uniquely identifies the server. The activation mode tells the BOA whether the server should be activated once (shared server), once for each object instance (unshared server), once for each method invocation (per method server), or not at all (persistent server). See section 4.3.4 for details on activation modes. The shell command is executed by the BOA whenever the server has to be (re)started. Activation mode library is used for loading servers into the same process as the client during runtime. Instead of a shell command you have to specify the path of the loadable server module for library activation mode. Finally there is a repository id for each IDL interface implemented by the server. See section 3.3.3 for details on repository ids.

If you have written a server that should be activated by the BOA daemon when its service is requested you have to create an entry for that server. This can be accomplished by using the program imr. imr can be used to list all entries in the implementation repository, to show detailed information for one entry, to create a new entry, and to delete an entry.

The implementation repository is selected by the -ORBImplRepoAddr or -ORBImplRepoIOR options, which you usually put into your .micorc file.

Listing All Entries

Just issue the following command:

  imr list

and you will get a listing of the names of all entries in the implementation repository.

Details For One Entry

  imr info <name>

will show you detailed information for the entry named <name>.

Creating New Entries

  imr create <name> <mode> <command> <repoid1> <repoid2> ...

will create a new entry with name <name>. <mode> is one of

<command> is the shell command that should be used to start the server. Note that all paths have to be absolute since micod's current directory is probably different from your current directory. Furthermore you have to make sure that the server is located on the same machine as micod, otherwise you have to use rsh; see below for examples. <repoid1>, <repoid2> and so on are the repository ids for the IDL interfaces implemented by the server.

Deleting Entries

  imr delete <name>

will delete the entry named <name>.

Forcing Activation of an Implementation

Registering an implementation in the implementation repository does not automatically activate the implementation. Usually a non-persistent implementation is only activated by the BOA daemon when its service is requested by a client. But sometimes you have to force activation of an implementation, for instance to make the implementation register itself with a naming service.

  imr activate <name> [<micod-address>]

will activate the implementation named <name>. To do this imr needs to know the address of the BOA daemon. Usually this is the same address as for the implementation repository and you do not need to specify <micod-address>. Only if the BOA daemon is bound to an address different from the implementation repository address and different from the addresses specified using the -ORBBindAddr option you have to specify <micod-address> as a command line option to imr.

Examples

Assume we want to register the account server account_server2 from section 3.3.3 as a shared server. Furthermore assume that neither micod nor ird have been started yet, so we have to get them running first. Assuming the hostname is zirkon, you have to do the following:

  # create .micorc (only do that once)
  echo -ORBIfaceRepoAddr inet:zirkon:9000 > ~/.micorc
  echo -ORBImplRepoAddr inet:zirkon:9001 >> ~/.micorc

  # run ird
  ird -ORBIIOPAddr inet:zirkon:9000

  # run micod in a different shell
  micod -ORBIIOPAddr inet:zirkon:9001

Now we are prepared to create the implementation repository entry for account_server2. Recall that this server implemented the interface Account whose repository id is IDL:Account:1.0. Assuming account_server2 has been copied to /usr/bin you can create the implementation repository entry using the following command:

  imr create Account shared /usr/bin/account_server2 IDL:Account:1.0

If account_server2 is located on host diamant (i.e., not on zirkon) you have to use the rsh command. This requires of course that you have entries in your .rhosts file that allow micod to execute programs on diamant. Here is the command to create the implementation repository entry:

  imr create Account shared "rsh diamant /usr/bin/account_server2" \
    IDL:Account:1.0

Now you should change account_client2.cc to bind to the address of micod. Note that you no longer need to know the address of the account server account_server2, you only need to know the address of micod. Here is the part of account_client2.cc that has to be changed:

  // account_client2.cc
  ...
    CORBA::Object_var obj =
      orb->bind ("IDL:Account:1.0", "inet:zirkon:9001");
  ...

Running the recompiled client will automatically activate account_server2.

Creating an entry for a loadable module (library activation mode) looks like this if /usr/local/lib/module.so is the path to the module:

  imr create Account library /usr/local/lib/module.so IDL:Account:1.0

Note that you have to make sure that a loadable module and a client that wants to make use of the module reside on the same machine.


Activation Modes

As mentioned in the previous section the BOA supports several activation modes. Using them is not simply a matter of creating an implementation repository entry, instead an object implementation has to use special BOA functionality according to the selected activation mode. This section gives you some details on this topic.

Activation Mode Shared

Shared servers can serve any number of object instances, which is probably the most widely used approach. The account server from section 3.3.3 is an example for a shared server. Lets look at the code again:

 1: // file account_server2.cc
 2:
 3: #include "account.h"
 4:
 5: class Account_impl : virtual public Account_skel
 6: {
 7:   // unchanged, see section "MICO Application"
 8:   // ...
 9: };
10: 
11: 
12: int main( int argc, char *argv[] )
13: {
14:   // ORB initialization
15:   CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );
16:   CORBA::BOA_var boa = orb->BOA_init( argc, argv, "mico-local-boa" );
17: 
18:   Account_impl* server = new Account_impl;
19:
20:   boa->impl_is_ready( CORBA::ImplementationDef::_nil() );
21:   orb->run ();
22:   CORBA::release( server );
23:   return 0;
24: }

After creating the implementation repository entry for the account server using the imr utility the account server stays inactive until the account client wants to bind to an object with repository id IDL:Account:1.0. The BOA daemon recognizes that there are no active account objects and consults the implementation repository for servers that implement objects with repository id IDL:Account:1.0. It will find the account server and run it. The account server in turn creates an account object in line 18, which will be announced to the BOA daemon. The server uses impl_is_ready() to tell the BOA daemon that it has completed initialization and is prepared to receive method invocations. The BOA daemon in turn finds the newly created account object and answers the bind request from the client with it. Finally run() is called on the ORB to start processing events.

run() will wait for requests and serve them as they arrive until the deactivate_impl() method is called, which deactivates the server. Calling the ORB method shutdown() will make run() return and the account server will exit. If method invocations arrive after the server has exited the BOA daemon will restart the server. See section 4.3.5 for details on restaring servers.

There are many reasons for calling deactivate_impl(). For example we could augment the account objects interface by a management interface that offers a method exit() that will shut down the account server4.7:

  // account.idl
  interface Account {
    ...
    void exit ();
  };

The implementation of the exit() method would look like this:

  // account.idl
  class Account_impl : virtual public Account_skel {
    ...
  public:
    ...
    virtual void exit ()
    {
      CORBA::BOA_var boa = _boa();
      CORBA::ORB_var orb = _orb();
      boa->deactivate_impl (CORBA::ImplementationDef::_nil());
      orb->shutdown (TRUE);
    }
  };

Note that we passed a NIL ImplementationDef to deactivate_impl() as well as to impl_is_ready(). Usually the implementation repository has to be searched to find the entry for the server and pass this one. When passing NIL the entry will be searched by the BOA. shutdown() has a boolean wait parameter which controls whether the ORB should immediately stop processing events (wait=FALSE) or wait until all pending requests have completed (wait=TRUE).

Activation Mode Persistent

Persistent servers are just like shared servers, except that the BOA daemon does not activate them. Instead they have to be started by means outside of the BOA, e.g. by a system administrator or a shell script. The code of a persistent server looks exactly like that of a a shared server. But note that once deactivate_impl() and shutdown() are called the server will not be restarted by the BOA daemon.

That means persistent servers do not need a running BOA daemon. Instead clients can connect directly to the object implementation, giving you better performance. See section 3.3.3 for an example. However, there is a reason to have even persistent servers register with the BOA daemon: you can do a bind() using the address of the BOA daemon, that is you do not need to know the address of the persistent server. Making a persistent server register with the BOA daemon is done like this:

  some_server -OARemoteAddr <micod-address> -ORBImplRepoAddr <micod-address> \
    -OAImplName <impl-name>

where <micod-address> is the address micod is bound to4.8. This is usually the same address you used as an argument to -ORBIIOPAddr when starting micod. See section 3.3.3 for details on addresses, sections 4.1.1 and 4.3.1 for details on command line arguments. <impl-name> is the name of the entry in the implementation repository the corresponds to the server.

Activation Mode Unshared

Unshared servers are similar to shared servers. The difference is that each instance of an unshared server can only serve one object instance. That is for $N$ objects you need $N$ running instances of an unshared server.

Furthermore you cannot use impl_is_ready() and deactivate_impl() but have to use obj_is_ready() and deactivate_obj() instead. Here is the main() function of an unshared account server:

 1: // file account_server2.cc
 2:
 3: #include "account.h"
 4:
 5: class Account_impl : virtual public Account_skel
 6: {
 7:   // unchanged, see section "MICO Application"
 8:   // ...
 9: };
10: 
11: 
12: int main( int argc, char *argv[] )
13: {
14:   // ORB initialization
15:   CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );
16:   CORBA::BOA_var boa = orb->BOA_init( argc, argv, "mico-local-boa" );
17: 
18:   Account_impl* server = new Account_impl;
19:
20:   boa->obj_is_ready (server, CORBA::ImplementationDef::_nil());
21:   orb->run ();
22:   CORBA::release( server );
23:   return 0;
24: }

The exit() method would look like this in an unshared server:

  // account.idl
  class Account_impl : virtual public Account_skel {
    ...
  public:
    ...
    virtual void exit ()
    {
      CORBA::BOA_var boa = _boa();
      CORBA::ORB_var orb = _orb();
      boa->deactivate_obj (this);
      orb->shutdown (TRUE);
    }
  };

Although an unshared server instance can only serve one object instance it can create more than one object instance. Imagine for instance a bank object

  // bank.idl
  interface Bank {
    Account create ();
    void destroy (in Account account);
  };

that can create new account objects and destroy account objects that are no longer needed4.9. The implementation of the create() method in an unshared server would look like this:

 1:  // bank_server.cc
 2:  class Bank_impl : virtual public Bank_skel {
 3:    ...
 4:  public:
 5:    ...
 6:    virtual Account_ptr create ()
 7:    {
 8:      Account_ptr account = new Account_impl;
 9:
10:      CORBA::BOA_var boa = _boa();
11:      boa->deactivate_obj (account);
12:
13:      return Account::_duplicate (account);
14:    }
15:  };

Note that line 11 calls deactivate_obj() on the newly created object4.10. This will tell the BOA daemon that you are not going to serve this object, instead a new server instance has to be activated for serving the newly created account object. For this to work you must of course implement saving and restoring for your objects as described in section 4.3.5.

If you need access to the newly created account object from within the server where it was first created you need to take special actions. The reason for this is that the created account object is initially an account object implementation (Account_impl), but in order to access the moved account object in the other server you need an account stub (Account_stub). Here is how to create this stub:

 1:  // bank_server.cc
 2:  class Bank_impl : virtual public Bank_skel {
 3:    ...
 4:  public:
 5:    ...
 6:    virtual Account_ptr create ()
 7:    {
 8:      CORBA::BOA_var boa = _boa();
 9:      CORBA::ORB_var orb = _orb();
10:
11:      Account_ptr account = new Account_impl;
12:      boa->deactivate_obj (account);
13:
14:      // turn 'account' into a stub
15:      CORBA::String_var ref = orb->object_to_string (account);
16:      CORBA::release (account);
17:      CORBA::Object_var obj = orb->string_to_object (ref);
18:      account = Account::_narrow (obj);
19:
20:      // now you can invoke methods on (the remote) 'account'
21:      account->deposit (100);
22:
23:      return Account::_duplicate (account);
24:    }
25:  };

The demo/boa/account3 directory contains a complete example for an unshared server that creates more than one object.

Activation Mode Per Method

Per Method servers are similar to unshared servers, except that a new server instance is launched for each method invocation. The code for a per method server looks the same as for an unshared server. But note that run() will return after the first method invocation, whereas in an unshared server run() will not return until you call shutdown().

Activation Mode Library

All activation modes discussed up until now assume client and server are different programs that run in separate processes. This approach has the advantage that client and server can be bound to each other dynamically during runtime. The drawback is the overhead for doing method invocations across process boundaries using some kind of IPC. The activation mode library eliminates this drawback while still allowing runtime binding. This is achieved by loading an object implementation (called a module from now on) into the running client. Invoking methods on an object loaded this way is as fast as a C++ method invocation.

A client that wants to use this feature does not differ from other clients, only the loadable module requires special code and you have to create a special entry in the implementation repository. To give you an example we want to change the bank account example from section 3.3.3 to make use of dynamic loading. The only change in the client is the address specified in the call to bind(): we have to use "local:" instead of "inet:localhost:8888", because we want to bind to the dynamically loaded object running in the same process:

 1: // file account_client2.cc
 2:
 3: #include "account.h"
 4:
 5: 
 6: int main( int argc, char *argv[] )
 7: {
 8:   // ORB initialization
 9:   CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );
10:   CORBA::BOA_var boa = orb->BOA_init( argc, argv, "mico-local-boa" );
11:
12:   CORBA::Object_var obj
13:     = orb->bind ("IDL:Account:1.0", "local:");
14:   if (CORBA::is_nil (obj)) {
15:      // no such object found ...
16:   }
17:   Account_var client = Account::_narrow( obj );
18: 
19:   client->deposit( 700 );
20:   client->withdraw( 250 );
21:   cout << "Balance is " << client->balance() << endl;
22:
23:   return 0;
24: }

Here is the code for the loadable module:

 0: // file module.cc
 1:
 2: #include "account.h"
 3: #include <mico/template_impl.h>
 4:
 5: class Account_impl : virtual public Account_skel
 6: {
 7:   // unchanged, see section "MICO Application"
 8:   // ...
 9: };
10:
11: static Account_ptr server = Account::_nil();
12: 
13: extern "C" CORBA::Boolean
14: mico_module_init (const char *version)
15: {
16:   if (strcmp (version, MICO_VERSION))
17:     return FALSE;
18:   server = new Account_impl;
19:   return TRUE;
20: }
21:
22: extern "C" void
23: mico_module_exit ()
24: {
25:   CORBA::release (server);
26: }

Lines 13-20 define a function mico_module_init() that is called when the module is loaded into the running client. Note that this function must be declared as extern "C" to avoid C++ name mangling. The version argument to mico_module_init() is a string specifying the MICO-version of the client the module is loaded into. Lines 16 and 17 check if this version is the same as the MICO-version the module was compiled with and make module initialization fail by returning FALSE if they differ. Otherwise a new account object is created and TRUE is returned indicating successful module initialization. Note that mico_module_init() must not perform ORB and BOA initialization since the client the module is loaded into did this already. The function mico_module_exit() is called just before the module is unloaded from the client and should release all allocated resources: in our example the account object created in mico_module_init(). mico_module_exit() is only called if mico_module_init() returned TRUE. Modules have to be compiled as a shared library, see section 4.6 for details and an example.

Although communication does not go through the BOA daemon when using loadable modules you need a running micod because you have to create an implementation repository entry for the module. See section 4.3.3 for details. The directory demo/shlib contains a complete example.

There is currently one problem with loadable modules: throwing exceptions from a loadable module into non-loadable module code results in a segmentation fault. This is not a bug in MICO but in the GNU-C++ compiler and/or dynamic loader.


Making Objects Persistent

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

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/boa/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 id4.12 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 FALSE4.13.

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/boa/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.


Migrating Objects

Up until now we described how objects are moved between different instances of the same server. Here we explain how to move objects between two completely different servers. This is for example useful if a server has to be replaced by a new version without interrupting usual business.

Recall that we augmented the account object by a management interface in section 4.3.4. The management interface offered a method exit() that terminates the server when invoked. Now let us add a method migrate() that migrates an account object to a new server. The new server is specified through an implementation repository entry.

  // account.idl
  interface Account {
    ...
    void migrate (in CORBA::ImplementationDef destination);
  };

Here is the implementation of the migrate() method:

 1:  #include "account.h"
 2:
 3:  class Account_impl : virtual public Account_skel {
 4:    ...
 5:  public:
 6:    ...
 7:    virtual void migrate (CORBA::ImplementationDef_ptr dest)
 8:    {
 9:       CORBA::BOA_var boa = _boa();
10:       boa->change_implementation (this, dest);
11:    }
12:  };

The change_implementation() in line 10 does the whole job. It will save the object's state as described in section 4.3.4 and tell the BOA daemon to use the new implementation from now on. See demo/boa/account4 for an example.

The current version of MICO can only perform the migration when the destination implementation is not currently active, which means that:

This limitation will be removed in a future version of MICO.


[Previous: Interface Repository] [Next: POA]
[Up: Implementation Overview]

Frank Pilhofer
2001-09-28