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

Subsections

- BOA Initialization
- BOA Daemon
- Implementation Repository
  - Listing All Entries
  - Details For One Entry
  - Creating New Entries
  - Deleting Entries
  - Forcing Activation of an Implementation
  - Examples
- Activation Modes
  - Activation Mode Shared
  - Activation Mode Persistent
  - Activation Mode Unshared
  - Activation Mode Per Method
  - Activation Mode Library
- Making Objects Persistent
- Migrating Objects

#
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

- name
- activation mode
- shell command or loadable module path
- list of repository ids

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

- `persistent`
- `shared`
- `unshared`
- `permethod`
- `library`
- `poa`

`<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 objects you need 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:

- 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/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 `FALSE` 4.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:

- you cannot migrate an object to a persistent server
- you cannot migrate an object to a shared server that is already running

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

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