3D PLM Enterprise Architecture

Middleware Abstraction

The Callback Mechanism

Making objects collaborate
Use Case

Abstract

This article shows how to set callbacks, that is, how to subscribe or listen to events, and how to publish events to the subscribers or listeners.


What You Will Learn With This Use Case

This use case is intended to show you how the callback mechanism  [1] works.

[Top]

The CAASysCallback Case

CAASysCallback is a use case of the CAASystem.edu framework that illustrates System framework capabilities.

[Top]

What Does CAASysCallback Do

This use case shows how to implement the publish/subscribe design pattern using the example of an event publisher played by an alarm that can ring, and an event subscriber or listener played by a burglar that approaches a place when there is something to steal, but fortunately protected by an alarm. The alarm ringing makes the burglar run away.

This article deals with one of the scenarios of CAASysCallback. You can refer to the module to see the others.

[Top]

How to Launch CAASysCallback

To launch CAASysCallback, you will need to set up the build time environment, then compile CAASysCallback along with its prerequisites, set up the run time environment, and then execute the use case [2].

[Top]

Where to Find the CAASysCallback Code

The CAASysCallback use case is made of a several classes located in the CAASysCallback.m module of the CAASystem.edu framework:

Windows InstallRootDirectory\CAASystem.edu\CAASysCallback.m\
Unix InstallRootDirectory/CAASystem.edu/CAASysCallback.m/

where InstallRootDirectory is the directory where the CAA CD-ROM is installed.

[Top]

Step-by-Step

To create an event publisher, an event subscriber or listener, and a scenario to make them play together, there are four steps:

# Step Where
1 Creating an Event CAASysRingingNotification class
2 Creating an Event Publisher CAASysAlarm class
3 Creating an Event Subscriber or Listener CAASysBurglar class
4 Create a Publish/Subscribe Scenario CAASysScenario.cpp file

[Top]

Creating an Event

The event to create is the ringing of the alarm. It is created as a notification class that the alarm class will then instantiate and publish. Its header file is as follows:

#include "CATNotification.h"

class CAASysRingNotification : public CATNotification
{
  CATDeclareClass;
  public:
    CAASysRingNotification();
    virtual ~CAASysRingNotification();
  private:
    CAASysRingNotification(const CAASysRingNotification &iObjectToCopy);
};

This class simply derives from CATNotification. The CATDeclareClass macro makes CAASysRingNotification part of a component. The copy constructor is set as private and is not implemented to prevent the compiler to create one as public, and thus prevent from illegal copies of the notification instances.

#include "CAASysRingNotification.h"

CATImplementClass(CAASysRingNotification,
                  Implementation,
                  CATBaseUnknown,
                  CATNull);
...

The CATImplementClass macro declares that CAASysRingNotification is an Implementation, that is, a component main class, that OM-derives from CATBaseUnknown. The last argument must always be set to CATNull if the second one is set to Implementation.

...
CAASysRingNotification::CAASysRingNotification()
{}

CAASysRingNotification::~CAASysRingNotification()
{}

The CAASysRingNotification class constructor use the default constructor of the CATNotification class.

...
CATNotification(int iAutomaticDelete=CATNotificationDeleteOff);
...

It means that the iAutomaticDelete value is CATNotificationDeleteOff, and consequently the notification must be deleted by those which have created it. You can read the referenced article for a comparison between the callback and send/receive mechanism about the CATNotification class creation and usage [3].

[Top]

Creating an Event Publisher

The alarm class header file is as follows:

#include "CATBaseUnknown.h"

class CAASysAlarm: public CATBaseUnknown 
{
  CATDeclareClass;
  public:
    CAASysAlarm(char * iAlarmPlaceName);
    virtual ~CAASysAlarm();
    void StartRinging();
    char *GetPlace();

  private:
    CAASysAlarm ();
    CAASysAlarm(const CAASysAlarm &iObjectToCopy);
  
  private:
    char * _pAlarmPlaceName;
};

The method StartRinging is the publishing method for the CAASysRingNotification instances.

#include "CAASysAlarm.h"
#include "CAASysRingNotification.h"
#include "CATCallbackManager.h"

CATImplementClass(CAASysAlarm, Implementation, CATBaseUnknown, CATNull);

CAASysAlarm::CAASysAlarm(char * iAlarmPlaceName) 
{  
  ... // Creates the data member: the place where the alarm will ring
}

CAASysAlarm::~CAASysAlarm()
{
  ... // Deletes the data member
}
...

The constructor simply instantiates the data member, and the destructor deletes it.

...
void CAASysAlarm::StartRinging() 
{
  CATCallbackManager * pCBManager = ::GetDefaultCallbackManager(this) ;
  if ( NULL != pCBManager )
  {
    CAASysRingNotification * pNotification = new CAASysRingNotification();
    pCBManager->DispatchCallbacks(pNotification, this);
    pNotification->Release();
    pNotification = NULL ;
  }
}
...

The StartRinging method publishes the notification that states that the alarm is ringing. To do this, the global function GetDefaultCallbackManager retrieves the callback manager associated by default with the alarm class instance, and this callback uses its DispatchCallbacks method to inform its subscribers or listeners that the alarm is ringing by means of the CAASysRingNotification notification created.

You can note that the CAASysRingNotification class instance is deleted just after the DispatchCallbacks call such as explained in the CAASysRingNotification class constructor.

[Top]

Creating an Event Subscriber or Listener

The event subscriber or listener is the burglar class. Its header file is as follows:

#include "CATBaseUnknown.h"
#include "CATEventSubscriber.h"

class CAASysAlarm;

class CAASysBurglar: public CATBaseUnknown
{
  public:
    CAASysBurglar(char * iBurglarName);
    virtual ~CAASysBurglar();

    void Approach(CAASysAlarm *iAlarm);
    void RunAway (CATCallbackEvent  iEventAlarm,
                  void             *iAlarm,
                  CATNotification  *iNotifAlarm,
                  CATSubscriberData iBurglarData,
                  CATCallback       iCallBack);

  private:
    CAASysBurglar ();
    CAASysBurglar(const CAASysBurglar &iObjectToCopy);
  
  private:
    CAASysAlarm * _pAlarm;
    char        * _pBurglarName;
};

The Approach method sets the callback and the RunAway method is the callback method, that is, is called when the event referred to by the Approach method happens.

The burglar source file is as follows:

...
CAASysBurglar::~CAASysBurglar() 
{
  if ( NULL != _pAlarm) 
  {
    ::RemoveSubscriberCallbacks(this, _pAlarm);
    _pAlarm=NULL;
  };
  ... // delete the data member
}
...

The destructor uses the global function RemoveSubscriberCallbacks to remove the callbacks set by the CAASysBurglar class instance for the CAASysAlarm notification.

The Approach method that sets the callback is as follows:

...
void CAASysBurglar::Approach(CAASysAlarm *iAlarm) 
{
  if ( iAlarm !=  _pAlarm )
  {
    if ( NULL != _pAlarm) 
    {
      ::RemoveSubscriberCallbacks(this, _pAlarm);
    };
    _pAlarm=iAlarm;

    if ( NULL !=  iAlarm )
    {
      CATCallback idCB ;
      idCB = ::AddCallback(this,
                           iAlarm,
                           "CAASysRingNotification",
                           (CATSubscriberMethod)&CAASysBurglar::RunAway,
                            NULL);
    }  
  }
}...

Approach begins by removing possible callbacks set by the CAASysBurglar class instance for the CAASysAlarm notification, and then uses the global function AddCallback to set the callback. AddCallback has the following parameters:

this The event subscriber or listener
iAlarm The event publisher to subscribe to, or listen
CAASysRingNotification The notification that represents the event
(CATSubscriberMethod)&CAASysBurglar::RunAway The method to call back whenever the event happens
NULL Possible useful data for the RunAway method

The callback method RunAway is as follows:

...
void CAASysBurglar::RunAway(CATCallbackEvent  iEventAlarm,
                            void             *iAlarm,
                            CATNotification  *iNotifAlarm,
                            CATSubscriberData iBurglarData,
                            CATCallback       iCallBack)
{
  CAASysAlarm *AlarmIsRinging = (CAASysAlarm *)iAlarm;
  printf("Burglar %s run away %s and very quickly.\n", 
                _pBurglarName, AlarmIsRinging->GetPlace());
}

Its signature has the following parameters:

iEventAlarm The published event
iAlarm The event publisher
iNotifAlarm The notification that represents the event
iBurglarData Possible useful data for the RunAway method
iCallBack The callback identifier

RunAway is dedicated to the action to undertake when the alarm begins ringing. This action is up to you. It's limited here to a trace.

[Top]

Creating a Publish/Subscribe Scenario

The following scenario creates an event publisher Alarm, and an event subscriber or listener Burglar. The burglar approaches the place where Alarm is active, and sets a callback on this object. Then Alarm rings.

...
int main() 
{
  cout << endl << "The CAASysCallback program begins ...."<< endl<<endl;
  CAASysAlarm Alarm("House");
  CAASysBurglar Burglar("Peter");
  Burglar.Approach(&Alarm);
  Alarm.StartRinging();
  cout << endl << "The CAASysCallback program is finished" << endl << endl;
  return(0);
}

Below is a listing of the traces produced by this program.

The CAASysCallback program begins ....

Alarm House is created.
Burglar Peter is alive.
Burglar Peter approachs slowly House and opens his ears.
Alarm House starts ringing.
Burglar Peter run away House and very quickly.

The CAASysCallback program is finished

Burglar Peter is dead.
Alarm House is deleted.

[Top]


In Short

The callback mechanism enables publish/subscribe between objects. Any object can publish events, and any object can subscribe to events published by any other object. The publisher delegates to its callback manager the job of calling the subscribers on their requested methods, and the management of the subscriber list.

[Top]


References

[1] The Callback Mechanism
[2] Building and Launching a CAA V5 Use Case
[3] Callback versus Send/Receive Mechanism
[Top]

History

Version: 1 [Jan 2000] Document created
Version: 2 [Fev 2004] Document updated to enhance comments about the CATNotification class usage
[Top]

Copyright © 2000, Dassault Systèmes. All rights reserved.