Automatically destroying objects created by factories

Ole Storm storm@ifad.dk
Tue, 10 Mar 1998 12:01:23 +0100


Greetings experts and omniORB'ers,

I am facing the following problem:

I have an existing application I would like to CORBA-enable. The
application maintains and dynamically creates complex data structures
that I would like to give clients access to through a CORBA interface.
The data structure is based on Container-Contained classes with a number
of methods for manipulating and iterating though containers. 

As I see it there are two possible solutions to the problem of CORBA
enabling my existing application and its data structure:


1. "Wrap" the existing classes and all their methods in IDL interfaces.
Each object the client sees is a handle to (a part of) the "real" data
structure in the server. The client can access the "real" data structure
by using the various methods of the interface to navigate and iterate
through it. On the server side, each implementation object has a copy of
or a reference to the real data.

The following is an example of such:

//IDL

  exception VDMError { short err; };
  exception APIError { string msg; };

  interface Generic
    {
      string ascii();
      boolean IsNil();
      boolean IsInt();
      boolean IsReal();
      boolean IsSet();
      boolean IsMap();
      boolean IsSequence();
      void destroy();
    };

  interface Set : Generic
  { //TBD... };
  interface Map : Generic
  { //TBD... };
  interface Nil : Generic
    { };

  interface Int : Generic
    {
      long GetValue();
    };

  interface Real : Generic
    {
      double GetValue();
    };

  interface Sequence : Generic
    {
      Generic Index(in long i)
        raises (VDMError);
      Generic Hd() 
        raises (VDMError);
      Sequence Tl()
        raises (VDMError);
      void RemElem(in long i)
        raises (VDMError);
      long Length();
      boolean GetString(out string s);
      boolean IsEmpty();
      void ImpAppend(in Generic g)
        raises (APIError);
      void ImpConc(in Sequence s)
        raises (APIError);
      Set Elems();
      short First(out Generic g);
      short Next(out Generic g);
    };

Notice that many of the methods are factory methods returning new object
references!


2. Another solution is to use (recursive) IDL structs and unions to
model the servers data structure in IDL. In this way the "real" data
must first be translated into their IDL-counterpart before being passed
to clients. 
The following is an example of such:

enum Type { IntType, RealType, SetType, MapType, SeqType, NilType };

union Generic switch(Type)
{
case IntType: long i_val;
case RealType: double d_val;
case SetType: sequence<Generic> set_val;
case MapType: sequence<Generic> map_val;
case SeqType: sequence<Generic> seq_val;
case NilType: long dummy;
}


What I am actually asking for is some guidance in the dilemma of
choosing one of the two solutions - or even better, finding a third and
better one. Here are some of the pros (+) and cons (-) as I see it:

Solution 1:
+ The interface (the set of available methods) towards the CORBA exposed
data type is similar to the interface of the "real" data type.
+ It is possible to do some exception handling.
+ Simple implementation. The implementation of the methods of the
interface is trivial. Each method simply uses the corresponding method
of the original data structure.
- The fact that objects are created on the fly by factory methods
introduces the possibility of the server leaking/wasting memory.
Implementation objects can only be freed if the client calls the
destroy() method when it is finished using an object reference. If a
client does not do this, memory is wasted in the server! This is indeed
a serious drawback.
- Low efficiency. Since only "handles" are passed to clients, the cost
of iterating through a Container is relatively high. For each element
the server is called (Index()) and a new object is created and returned.
On the other hand, a _huge_ data structure can be passed to clients at
almost no cost. Only when the structure is traversed is some way, the
in-efficiency starts to show.

Solution 2:
+ Efficient. Since the entire data structure is passed to the client the
cost of iterating through the structure is not high. At least no calls
to the server is involved. On the other hand, it may prove inefficient
if _huge_ data structures are passed.
+ No problems in relation to memory management. The server will not be
liable to memory leaks, since no objects are created dynamically.
- The wide variety of methods for each interface in solution 1 is not
available. Of course one could define interfaces with methods for
manipulating the IDL data types, but the implementation of such would
not be as trivial as is the case in solution 1.


For this specific application efficiency is not that important, so I
tend to prefer solution 1. However, the problem of handling objects
constructed by factories is a _serious_ drawback! I am aware that it
should probably not be the client who is responsible for freeing objects
through the destroy() method, but I really don't like the alternative:
some kind of timing out of objects in the server.

Is there a CORBA compliant way of overriding the destruction of proxy
objects? In this way it would be possible to automatically call the
destroy() method when an object comes out of scope.

I know that with omniORB2 it is possible to define new Proxy Classes and
new Proxy Object Factories, and in this way get hands on the destructor
for the proxy. Is this a CORBA compliant approach?


Do any of you have experience with a similar problem? Any comments or
guidance is most welcome.


Kind regards,

	Ole.


P.S. I am sorry this e-mail ended up to be so large, but I sort of got
carried away ;-)

---------------------------------------------------------------
Ole Storm
The Institute of Applied Computer Science (IFAD)
Forskerparken 10, DK-5230 Odense M, Denmark
Phone: +45 6315 7134, Fax: +45 6593 2999, Email: storm@ifad.dk
WWW: http://www.ifad.dk
---------------------------------------------------------------