3D PLM Enterprise Architecture

Middleware Abstraction - ENOVIA Event Model

Creating and Publishing Events

Working with events, publishers and subscribers
Use Case

Abstract

This article discusses the CAAVpiPublishEvents use case. This use case explains how to create Events, and use them in Programs.

What You Will Learn With This Use Case

This use case is intended to show you how to declare and raise events for you own objects. The use case will show you also how a client object will be able to subscribe to your events and you will see the Publisher and Subscriber behaviors when events are raised.

[Top]

The CAAVpiPublishEvents Use Case

CAAVpiPublishEvents is a use case of the CAAVPMInterfaces.edu framework that illustrates VPMInterfaces framework capabilities.

[Top]

What Does CAAVpiPublishEvents Do

The goal of CAAVpiPublishEvents use case is to show the whole process from the declaration of new events that a publisher class can raise to the emission of those events and the call of subscribers which will react to those events.

The CAAVpiPublishEvents use Case is closely linked to CAAVpiEventsObjects module, where the new events, the test publisher class (CAAVpiFileManager) and the test subscriber class (CAAVpiFMEventsSubscriber) are defined.

In our scenario, the publisher is a FileManager which has three methods:

Each method will raise an event. Those events will be declared in the INDEX.event file , and their definition allows a subscriber to veto the move file and delete file operations.

On the other side CAAVpiPublishEvents:

The traces should show the event raised and the callbacks with no problem in the first case, the event raised and the veto put by the subscriber in the second case.

[Top]

How to Launch CAAVpiPublishEvents

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

Launch the use case as follows:

[Top]

Where to Find the CAAVpiPublishEvents Code

The CAAVpiPublishEvents use case is made of an executable class named CAAVpiPublishEvents located in the CAAVpiPublishEvents.m module, 2 classes CAAVpiFileManager and CAAVpiFMEventsSubscriber located in the CAAVpiEventObjects of the CAAVPMInterfaces.edu framework:

Windows InstallRootDirectory\CAAVPMInterfaces.edu\CAAVpiPublishEvents.m\ (executable class)
InstallRootDirectory\CAAVPMInterfaces.edu\CAAVpiEventObjects.m\ (event declarations,callback interface,publisher and subscriber classes)
Unix InstallRootDirectory/CAAVPMInterfaces.edu/CAAVpiPublishEvents.m/ (executable class)
InstallRootDirectory/CAAVPMInterfaces.edu/CAAVpiEventObjects.m/ (event declarations,callback interface,publisher and subscriber classes)

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

[Top]

Step-by-Step

There are five main steps in CAAVpiPublishEvents Use Case:

  1. Prolog: Prerequisite declarations and classes in CAAVpiEventObjects module
  2. Start of CAAVpiPublishEvents: Create an ENOVIA Session and get its event manager
  3. Subcriptions of a created Client object.
  4. Creation of a Publisher object and call of its methods which raise events
  5. End of CAAVpiPublishEvents: Session Close

We will now comment each of these sections in detail.

[Top]

Prolog

Before launching the subscribing and publishing events, four steps, in the CAAVpiEventObjects module:

...
<Class Name="CAAVpiFileManager" Type="Real">
  <Event Name="CreateFile"
         Mode="Public"
         Before="Yes"
         After="Yes"
         Veto="No"
         EventInterface="CAAIVpiFMEventCallBacks" *>
  </Event>
...
* see next item
...
CATDeclareInterface;
 //
 public:
 ...
 virtual HRESULT onKo (const ENOVIEvent_var& iRaisedEvent, HRESULT& oNotifyReturnCode) = 0 ;
 virtual HRESULT onBeforeCreateFile (const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode) = 0 ;
 virtual HRESULT onAfterCreateFile  (const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode) = 0 ;
...

Note>: The methods of a callback interface always have the same signature and their name is generic. If you define a new event MyEvent (before/after), the methods to be inserted in the associated interface will be:

In the XML event declaration file, in the Event tag, the EventInterface attribute is valuated with the name of the callback interface.

The methods which emit events will insert 2 macros "EVENT_FIRE" , one at the start of the method, the second one at the end of the method
//=======================================================================
//function: MoveFile
//purpose:
//=======================================================================
HRESULT CAAVpiFileManager::MoveFile( const CATUnicodeString& iFileName,
     const CATUnicodeString& iFromDirectory,
     const CATUnicodeString& iToDirectory)
{
  // 1: Fill the Data Stream related to the event "MoveFile" with the arguments defined
  // in the declaration file: INDEX.event
  ENOVIStream_var spPublisherStream=NULL_var;
  DATA_EVENT_INIT(MoveFile,spPublisherStream);

  CORBAAny iTmpValue ; // for conversion of data.
  iTmpValue<<iFileName; // translates a CATUnicodeString into a CORBAAny;
  spPublisherStream->AddArgumentValue("FileName",iTmpValue); // adds arguments in the stream
  ...
  // Fire event MoveFile step before.
  EVENT_FIRE(MoveFile,CAAIVpiFMEventCallBacks,Before,spClientStream,oNotify);
  ...
  //5. fire the step After of the event MoveFile
  EVENT_FIRE(MoveFile,CAAIVpiFMEventCallBacks,After,spClientStream,oNotify);
  if(FAILED(oNotify)){.... }
  return S_OK;
}

HRESULT CAAVpiFMEventsSubscriber::onBeforeMoveFile (const ENOVIEvent_var& iRaisedEvent,
                                                  HRESULT& ioNotifyReturnCode)
{
  cout<<"\t->["<<_Name.ConvertToChar()<<"] is called back on event:";
  cout<<"MoveFile (step before)"<<endl;

  ioNotifyReturnCode=S_OK;
  // get the data stream attached to the event entity.
  ENOVIStream_var spLinkedStream=NULL_var;
  iRaisedEvent->GetDataEvent(spLinkedStream);
  if(NULL_var==spLinkedStream){
    cout<<"\t\t No data stream attached to the event"<<endl;
    return S_FALSE;
  }
  // just verify the stream Name
  CATUnicodeString oStreamName;
  spLinkedStream->GetName(oStreamName);
  cout<<"\t\t Data stream : "<<oStreamName<<endl;
  
  //get the arguments: directories and file name
  CORBAAny oArgVal;
  CATUnicodeString oArgType,Result;

  // get the Target directory for Move operation and check it's not a protected one.

  HRESULT rc = spLinkedStream->GetArgumentValue("ToDirectory",oArgVal,oArgType);
  if(FAILED(rc)){
    cout<<"\t\t==> impossible to get argument ToDirectory in stream : FAIL"<<endl;
    return rc;
  }
  oArgVal>>Result;
  if(Result==_TheProtectedDir){
    cout<<"\t\t*****************************************************************************"<<endl;
    cout<<"\t\t *** The Target Directory is a protected one : Put a VETO on the event !!! ***"<<endl;
    cout<<"\t\t *****************************************************************************"<<endl;
    ioNotifyReturnCode=E_ACCESSDENIED;
  }
  else
    cout<<"\t\t TargetDirectory : "<<Result.ConvertToChar()<<" OK for me"<<endl;
  return S_OK;
}

In this code sample, this code is called when the first step of MoveFile event is raised ("Before"). The code checks if the target directory name (stored in the datastream of the event) equals to _TheProtectedDirectory, name of the directory protected by this subscriber instance. if Yes, the subscriber sends a veto return code, which is transmitted to the publisher, so that the execution of the method is stopped .

[Top]

Create an ENOVIA Session and get the Event Manager

...
VPMSession* pSession = VPMSession::OpenSession();
if ( NULL == pSession ) {
  cout<<endl<<endl << "Unable to Open a VPM Session" << endl;
  return 1;
}
cout<<endl<<endl << " 1. Open VPM Session OK " << endl<<endl;

...

// --------------------------------------------------
// --> 2. Get the Event Manager in order to subscribe
// ---------------------------------------------------
//
ENOVIEventManager_var spEventManager;
rc = spLoginSession->get_ENOVEventManager( spEventManager );
if ( FAILED(rc) ){
  cout<<endl<<endl<<"Unable to get the Event Manager from the Session"<<endl;
  return 2;
}
cout<<endl<<endl << " 2. Get Event Manager OK " << endl << endl;
...

The Event Manager is used to subscribe to events on objects types or instances. By implementing explicit callback interfaces, declared by the ENOVIA domains or a generic callback interface ENOVISubscriberEvent, client just can subscribe to events on a given type (Strings arguments), or on a given instance (pointer argument).

[Top]

Creation and Subscriptions of a client object

...
CATUnicodeString File1("NormalFile.txt"),File2("VeryImportantFile.txt");
CATUnicodeString Dir1("aNormalDirectory"),Dir2("anotherNormalDirectory"),Dir3("UntouchableDirectory");

// create an instance of subscriber
CAAVpiFMEventsSubscriber* pSubscriber = new CAAVpiFMEventsSubscriber("TheFMSubscriber#1");
pSubscriber->SetProtectedFile(File2); // to be able to veto a delete on this file.
pSubscriber->SetProtectedDirectory(Dir3); // to be able to veto a Move into that directory
...
CAAIVpiFMEventCallBacks * piFMEventSubscriber=NULL;
rc = pSubscriber->QueryInterface(IID_CAAIVpiFMEventCallBacks, (void**) &piFMEventSubscriber);
...
CATUnicodeString CreateEvent("CreateFile"),MoveEvent("MoveFile"),DelEvent("DeleteFile");
CATUnicodeString PublisherClass("CAAVpiFileManager");
rc = piFMEventSubscriber->QueryInterface(IID_CATBaseUnknown, (void**)& piCBU);
...
CATBaseUnknown_var spCBU(piCBU);
...
rc = spEventManager->Subscribe(CreateEvent,PublisherClass,spCBU,IID_CAAIVpiFMEventCallBacks,&oCookie);

The CAAVpiFMEventsSubscriber class is a client sample: its aim is to subscribe to events declared by the CAAVpiFileManager class, and veto them if the data given by the event match the protected file and directory stored in it ( refer to the two methods SetProtectedFile and SetProtectedDirectory).

To be able to subscribe , the CAAVpiFMEventsSubscriber has implemented the CallBack interface given by the Publisher side: CAAIVpiFMEventCallBacks. Then, another Query Interface is needed to be able to get a CATBaseUnknown_var smart pointer, which is the argument needed for the Subscribe method of the event manager.

[Top]

Creation of a PublisherObject and call of the methods which raise events

...
CAAVpiFileManager * pFileMgr = new CAAVpiFileManager("TheTestFileManager");
CATUnicodeString File1("NormalFile.txt"),File2("VeryImportantFile.txt");
CATUnicodeString Dir1("aNormalDirectory"),Dir2("anotherNormalDirectory"),Dir3("UntouchableDirectory");
rc = pFileMgr->CreateFile(File1,Dir1);  rc = pFileMgr->CreateFile(File2,Dir2);

//Simulate MoveFile for File from Dir1 to Dir2
...

rc = pFileMgr->MoveFile(File1,Dir2,Dir2);
...
rc = pFileMgr->MoveFile(File2,Dir2,Dir3);

//simulate DeleteFiles for File1 and File2:
// For File1 the execution will be ok
// For File2, there should be a Veto put by the subscriber, while File2 is protected by it.
// Traces should show such a behaviour.

rc = pFileMgr->DeleteFile(File1);
rc = pFileMgr->DeleteFile(File2);
...

The CAAVpiFileManager is intended to simulate create file, move file and delete file operations. Each of those methods raise an event, in two steps (before/after). The test of delete file is done here with a normal file and a file which was protected by the subscriber object, and the test of move file is done with normal directories and a directory protected by the same subscriber object.

The interest of that use case is to see traces at execution time: you should see the publisher and the subscriber behaviours, in the normal case, and in the case of a veto put by the subscriber on the execution of the publisher method.

[Top]

End of Use case: Session close

...
 rc = VPMSession::CloseSession();

Each opened session must be closed.

[Top]


In Short

This use case has demonstrated the way to define and raise events for a new publisher class, and how the clients can subscribe to those events and be called back when they are emitted.

[Top]


References

[1] Building and Launching a CAA V5 Use Case
[2] The ENOVIA Event Model
[3] ENOVIA Event Use Case Overview
[Top]

History

Version: 1 [Oct 2001] Document created
[Top]

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