
            REPORT ON THE OSIMIS GMS API CHANGES TO SUPPORT:
CONDITIONAL PACKAGES, FULL ERRORS, ATOMICITY AND ASYNCHRONOUS FEATURES
----------------------------------------------------------------------

This is a short report on the OSIMIS GMS API changes to support
the future introduction of conditional packages, atomicity and asynchronous
features and to allow user-defined error parameters to be returned in
processing failure errors.


GENERAL API ENHANCEMENT
-----------------------

Managed object attributes in OSIMIS are held as an array of Attr* in every
class and can be accessed through the integer index the GDMO compiler produces:

uxObj1::createRR
    // ...
    ((Gauge*) _attrs[I_nUsers]) -> setType(OV_INTEGER);

The problem with that approach is that explicit typing is lost and the
implementor has to cast it back to the actual type. This is both tedious
and also dangerous if wrong casting is forced that will manifest itself
at runtime as a coredump in the best case or as obscure behaviour in the worst.
The GDMO compiler back-end has now been modified to produce inline methods
with the name of an attribute which return the actual type, so the above
code could be written as:

uxObj1::createRR
    // ...
    nUsers() -> setType(OV_INTEGER);

Errors will be cought at compile time and this tedious casting is not needed.
This is an incremental change and implementors are strongly encouraged
to use this way of dereferencing attributes.


CONCEPTUAL ADDITIONS / MODIFICATIONS
------------------------------------

The support for conditional packages requires full access to meta-class
information for an object instance before the latter is created. Such
information may be also used to optimise the creation of objects by
checking name bindings before creation etc. It is also in line with the
architectural principle that wants meta-class information separate
from managed object instances and possibly available also to semantic free
managing applications such as browsers etc.
As a result of the above, the meta-class object instances (of C++ class
MOClassInfo) should be initialised first and linked in a tree that mirrors
the inheritance one at run time e.g. evForwDiscr -> discr -> top.

The other three aspects, namely error parameters, atomicity and asynchronous
API features dictate the enhancement of the current polymorphic GMS API
with additional parameters.


API CHANGES
-----------

The first API change relates to the information present in class definitions
in the header file that tells the GMS which MO classes an application will
use e.g. Sma.h for the SMA application.

In the past, the class name and pointers to static creation methods
were used. The latter are now part of the MOClassInfo meta-object for the
class which acts as a "factory" in the ObjectiveC/Smalltalk sense.
As such, the information now used is the class name and pointers to methods
that will return the meta-class object and will initialise it.
A concrete example:

Old style:

{   "uxObj1",           uxObj1::cmisCreate,     uxObj1::create },
{   "uxObj2",           uxObj2::cmisCreate,     uxObj2::create },


New style:

//  classes specific to this application i.e. SMA

{ "uxObj1",             uxObj1::getClassInfo,   uxObj1::initialiseClass },
{ "uxObj2",             uxObj2::getClassInfo,   uxObj2::initialiseClass },


The meta-class object for a class is now called _classInfo and it is
of type MOClassInfo* instead of MOClassInfo . This is used in createRR
to complete the class initialisation for aspects that the GDMO compiler
cannot know and elsewhere to access other class information (attr OIDs etc.)
The old style of refering to the meta-class object using the _info variable
(an aggregate MOClassInfo type) is still available but it will not be
supported in future versions, so implementors are encouraged to migrate
to the use of _classInfo now.

Also in older OSIMIS versions, the necessary classes to declare in this
header file were only those that could be instantiated, either because of
a resource operation or through the management interface. Abstract classes
such as discriminator, logRecord etc. should not be necessarily registered.
This is no more the case, ALL the managed object classes of the inheritance
tree this application knows of should be instantiated. The order is also
important, parent classes should be registered before their children.
That way the GMS makes sure there is not an incomplete inheritance tree.




OLD POLYMORPHIC GMS API (ICM versions up to 3.3x)
-------------------------------------------------

The old GMS polymorphic API and the current one are shown below with
the changes discussed afterwards. It is noted there has been an intermediate
MIDAS version 3.4 in between with a few of the final changes - this
is not discussed in this document as it is a MIDAS issue.


static  int		<MO>::makeRdnAndSuperior (RDN&, MO*&);

virtual int             MO::createRR (void* genParm = NULLVD);
virtual int             MO::deleteRR (void* genParm = NULLVD);

virtual CMISErrors      MO::refreshSubordinates ();
virtual CMISErrors      MO::refreshSubordinate (RDN rdn);

virtual int             MO::get (int attrId, int classLevel);
virtual CMISErrors      MO::set (CMISModifyOp setMode,
                                int attrId, int classLevel, void* attrValue);
virtual CMISErrors      MO::action (int attrId, int classLevel,
                                void* actionInfo, void** actionReply);

virtual int             MO::buildReport (int attrId, int classLevel,
                                PE* report);


NEW POLYMORPHIC GMS API (version 4.0)
-------------------------------------

static  RDN	<MO>::makeRdn (MO*);

virtual int     MO::createRR (AVA*& errorInfo, void* genParm = NULLVD,
				int = -1);
virtual int     MO::deleteRR (AVA*& errorInfo, void* genParm = NULLVD,
                                Bool checkOnly = False, int = -1);

virtual int     MO::refreshSubordinates (AVA*& errorInfo,
                                         int operationId = -1);
virtual int     MO::refreshSubordinate (RDN rdn, AVA*& errorInfo,
                                         int operationId = -1);

virtual int     MO::get (int attrId, int classLevel, AVA*& errorInfo,
                        Bool checkOnly = False, int operationId = -1);
virtual int     MO::set (CMISModifyOp setMode, int attrId, int classLevel,
                        void* attrValue, AVA*& errorInfo,
                        Bool checkOnly = False, int operationId = -1);
virtual int     MO::action (int attrId, int classLevel, void* actionInfo,
                        void*& actionReply, Bool& freeFlag, AVA*& errorInfo,
                        Bool checkOnly = False, int operationId = -1);

virtual int     MO::buildReport (int eventId, int classLevel,
                        void*& report, Bool& freeFlag);


THE STATIC MAKERDN METHOD
-------------------------

The previous makeRdnAndSuperior method is now called makeRdn
and returns only the RDN as the GMS finds the superior
according to the name binding(s). The MO* argument is the superior
managed object. This may be needed in the case of a class with
multiple name bindings and possibly more than one naming attributes.
In this case, the class may need to know where it is created in order
to construct the RDN accordingly. This is a rather esoteric feature
but OSIMIS provides it nevertheless - in most circumstances this
argument will not be used.



DISCUSSION OF THE OTHER CHANGES
-------------------------------

Return value
------------

The return value for all the polymorphic methods is of type int.
Allowed values are OK (success) and NOTOK (failure) at present.
ASYNC in the future will be used to denote that the operation will
take place in asynchronous fashion.


Elimination of PEs and double pointers
--------------------------------------

The buildReport primitive was the last place where explicit ASN.1
manipulation was visible to application implementors. The GMS now
takes care of such details and the report data structure is passed instead.

On a cosmetic level, arguments passed by reference to be created within
the method are now passed as double pointers (** - C-style) but as
references to pointers (*& - C++ style). This affects the
actionReply old argument and is the case with many new arguments of this
nature.


Additional arguments
--------------------

First of all, all the new arguments are AFTER the old ones in each primitive
in the following order according to functionality:

1. any additional arguments to give more flexibility to the old methods
   without adding new functionality: the boolean freeFlag (of type Bool&)

2. any additional arguments to enable the better reporting of errors:
   the errorInfo flag (of type AVA*&)
 
3. any additional arguments to enable the GMS to exercise atomicity:
   the checkOnly boolean flag (of type Bool)
 
4. any additional arguments to enable the GMS to handle asynchronous replies:
   the operationId integer flag (of type int)
 

The free flag gives more flexibility as to how GMS deals with action reply
and event report structures. The default value is True which means that
the GMS will free them if the action/buildReport code does not set it
to False. The latter case is useful when converting old buildReport methods
to the new style as in the old case the MO was responsible for freeing
the event report structure.

In the past, the createRR/deleteRR/get/set/action methods could not
return any information with processing failure errors. In addition,
createRR could not tell the GMS in the case of invalid/missingAttributeValue
errors which attribute caused the problem. This is now fixed through the
errorInfo parameter which may contain:

a. a CMISError code
b. an identifier i.e. a name as registered in oidtable.at
c. an arbitrary value i.e. a Attr* with a value corresponding to the identifier

a. should always be present when a NOTOK is returned to tell the
GMS what error it was and the allowed values are:

createRR: invalidAttributeValue, missingAttributeValue, processingFailure
deleteRR: processingFailure
get:      processingFailure
set:      invalidAttributeValue, invalidOperation, processingFailure
action:   invalidArgumentValue, processingFailure
refreshSubordinate(s):
          processingFailure

With processingFailure one could pass b./c. to inform the remote
application of what exactly went wrong. If not supplied, the GMS
fills in general values (not saying much) as these are mandatory for CMIS.
b. should be supplied in the case of createRR invalid/missingAttributeValue
errors. Note that the methods allocate space for the error info which
is freed by the GMS. Some examples:

uxObj1::createRR
    // ..
    // no value passed as the identifier says it all
    err = new AVA(m_processingFailure, "singleInstanceClass");

    // it could have been though
    // (assume singleInstanceClass/GraphicString binding):
    err = new AVA(m_processingFailure, "singleInstanceClass",
                  new String("an instance of uxObj1 already exists!"));

monitorMetric::createRR
    // ..
    // not supplied
    err = new AVA(m_missingAttributeValue, "observedObjectInstance");

    // ..
    // supplied but with a wrong name (non-existing)
    err = new AVA(m_invalidAttributeValue, "observedObjectInstance");

monitorMetric::set
    // ..
    // trying to set a threshold with administrativeState unlocked
    err = new AVA(m_invalidOperation);


The parameter for atomicity is a boolean checkOnly flag. For the moment
it could be more or less ignored as the GMS does not support atomicity (yet)
but ideally code should be written using it now so that it becomes immediately
functional when the GMS is enhanced.

If you don't want to bother, add in the beginning of your method
(after the call of the same parent method):

    if (checkOnly)
        return NOTOK;   // don't need to set the error in this case

This will refuse atomic requests for this class. If though you want
to support atomicity, check if the request can be performed but do
not do it, simply return OK or NOTOK. Look at uxObj1/uxObj2 and
scanner/monitorMetric classes as examples.

The only difference to this style holds for the deleteRR method
which should call the parent class deleteRR last (destruction from
the leaf of the inheritance branch):

int <moclass>::deleteRR (AVA*& err, void* info, Bool checkOnly, int)
{
    if (!checkOnly) {
	// do what you were doing before here
    }
    else {
	// check if you can be destructed and if not return NOTOK
    }

    return <parentclass>::deleteRR(err, info, checkOnly);
}


Finally, the operationId parameter could be ignored for all classes
using the synchronous GMS interface i.e. all! It will be used by
classes using the asynchronous API when the latter becomes available.



OTHER (NON-POLYMORPHIC) GMS API CHANGES
---------------------------------------

One of the most important reasons for extending the GMS MO polymorphic API
(methods get, set, action, createRR, deleteRR, refreshSubordinate(s))
was to provide MIB implementors with the capability to report errors and
associated error parameters. The set of errors that may be returned
from each of these methods is described above - the most common
error being processingFailure which may point to a specific reason/parameter.

As a result of this extension, a number of other methods that access
managed objects have been also extended to provide access to this error
information which may be needed by applications to take appropriate action.
Those methods were extended with an argument of type

AVA*& error

which, if supplied, is going to be filled with the error information.
Unfortunately, because of a bug in the ATT C++ compiler, this could not
be made optional e.g.  AVA*& error = DISDARDAVA
This means that existing code that uses these methods needs to be
updated in order to compile. The exact nature of those changes is
explained below.


    MO*         getMO (DN dn, Bool refresh, AVA*& error);
    MO*         getMO (char* strDn, Bool refresh, AVA*& error);

These two methods may get a (pointer to a) managed object, possibly
after refreshing the MIT to reflect the exact state of associated resources.
The latter is the case only if a "fetch-upon-request" policy is exercised
to keep the MIT up-to-date, by redefining the refreshSubordinate(s) methods.

If this is not the case, they may always be called as e.g.
    mo = base -> getMO (dn, False, DISCARDAVA);

If though such a policy is in effect, by setting the refresh argument
to True the GMS will try to refresh the MIT by calling all the necessary
refreshSubordinate methods. In that case there are two calling possibilities:
    mo = base -> getMO(dn, True, DISCARDAVA);
or
    AVA* error = NULLAVA;
    mo = base -> getMO(dn, True, error);
    if (!mo && error) {
	// check why disaster happened and possibly do something
        // ... may check error here
	delete error;	// after we have finished with it
    }

The former is not a good idea as the calling code will not know if
a NULLMO was returned because there is no such resource or because
there is a real problem in accessing that resource.


    MO*         getSubordinate (RDN rdn, Bool refresh, AVA*& error);
    MO*         getSubordinate (char* strRdn, Bool refresh, AVA*& error);
    MO**        getSubordinates (Bool refresh, AVA*& error);
    MO**        getWholeSubtree (Bool refresh, AVA*& error, Bool base = False);

It is exactly the same case with those methods with respect to the use
of the error parameter. It is noted that last argument of getWholeSubtree
denotes if the base object is desired:

    molist = base -> getWholeSubtree(False, DISCARDAVA);

will return all the subtree objects APART from the base object. If the latter
is desired (to be the first object returned), the call should be:

    molist = base -> getWholeSubtree(False, DISCARDAVA, True);



    int         update (AVA*& error);
    Attr*       getUpdatedAttr (OID attrOid, AVA*& error);

Somethimes, there is a need to update a managed object before accessing
its attributes: update updates all the object's attributes while
getUpdatedAttr updates the requested attribute and returns it.
The latter is used by objects such as the metric as summarisation,
this is the reason the attribute OID is used as the handle.
It must be noted that the second may be emulated by using the first,
at the expense of updating the whole instance instead of one attribute:

    obj -> update(DISCARDAVA);
    Attr* attr = obj -> getAttr("<attrName>");	// old style or...
    Attr* attr = obj -> <attrName>();		// prefered new style

It should be noted that if it is known that the polymorphic get methods
for this instance do no return an error, it is ok to use DISCARDAVA
for the error. In general though, it is better to check and see if the
object was updated properly by supplying an error parameter and checking it.
    


    void        deleteWholeSubtree (DeletionType delType= dt_undefined,
				    Bool base = False);

The following method has been added to delete a whole subtree, deleting
potentially also the base object according to the last argument.
This makes sure that the deleteRR methods are called in pre-order
before the objects are deleted in post-order to maintain MIT integrity.
If an object supports the objectDeletion notification, this will
be automatically emitted. The first argument may be used to instruct
the deleteRR method of individual instances about the type of the deletion.
Implementors are encouraged to use this method for deleting even a single
managed object as it encapsulates useful behaviour (call to deleteRR,
possible emission of objectDeleteion and memory release).



NOTE: You may have noticed that DISCARDAVA has to be passed instead
      of NULLAVA to the above methods to denote that the user of the
      method is not interested in the error. The reason for this is
      that for C++ pointer reference arguments one cannot distinguish
      between a pointer with a NULL value or no pointer at all,
      so a special value is needed.

      When the user has passed a AVA* argument to be filled instead
      of DISCARDAVA, in case of an error the error info will be filled.
      It is the user's (i.e. caller's) responsibility to free the error
      information after inspected.

