3D PLM Enterprise Architecture

Middleware Abstraction - ENOVIA Event Model

Subscribing Automatically to Events

Working with events and subscribers
Use Case

Abstract

This article discusses the CAAVpiAutomaticSubscription use case. This use case explains how to subscribe automatically to events at the opening of the Login Session, via the ENOVIEventPlugin interface, taking the example of events fired by the File Manager object described in the CAAVpiPublishEvents use case.


What You Will Learn With This Use Case

This use case is intended to show you one of the two ways that can be used to automatically subscribe to events and how to implement the dedicated callback interfaces to handle these events when they are raised. The second way is illustrated in the CAAVpiPackageSubscription use case.

[Top]

The CAAVpiAutomaticSubscription Use Case

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

[Top]

What Does CAAVpiAutomaticSubscription Do

The CAAVpiAutomaticSubscription use case processes to a subscription at the opening of the Login Session through a dedicated plug-in functionality, then raises the events that have been subscribed to, and finally calls the corresponding subscribers back. The events that are subscribed to in this sample are fired by the File Manager object of the CAAVpiPublishEvents use case.

The CAAVpiAutomaticSubscription use case is related to the CAAVpiEventObjects.m module where the following classes are defined:

[Top]

How to Launch CAAVpiAutomaticSubscription

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

Launch the use case as follows:

[Top]

Where to Find the CAAVpiAutomaticSubscription Code

The CAAVpiAutomaticSubscription use case is made of a main named CAAVpiAutomaticSubscription.cpp located in the CAAVpiAutomaticSubscription.m module and three classes named CAAVpiFileManagerListener, CAAVpiPlugin, and CAAVpiPluginListener located in the CAAVpiEventObjects.m module of the CAAVPMInterfaces.edu framework.

Windows InstallRootDirectory\CAAVPMInterfaces.edu\CAAVpiAutomaticSubscription.m\(main)
InstallRootDirectory\CAAVPMInterfaces.edu\CAAVpiEventObjects.m\(plugin+listeners classes)
Unix InstallRootDirectory/CAAVPMInterfaces.edu/CAAVpiAutomaticSubscription.m/(main)
InstallRootDirectory/CAAVPMInterfaces.edu/CAAVpiEventObjects.m/(plugin+listeners classes)

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

[Top]

Step-by-Step

We can divide this use case in two distinct main parts:

  1. Implementation of the classes CAAVpiFileManagerListener, CAAVpiPlugin, and CAAVpiPluginListener
  2. Implementation of the main CAAVpiAutomaticSubscription.cpp

There are seven steps in the first part of the CAAVpiAutomaticSubscription Use Case:

  1. Implement CAAIVpiFMEventCallbacks
  2. Implement ENOVIEventPlugin
  3. Get the current Login Session
  4. Get the Event Manager
  5. Create an instance of CAAVpiFileManagerListener class
  6. Subscribe to the events raised by instances of FileManager type
  7. Create a resource file to declare the plug-in

There are five main steps in the second part of the CAAVpiAutomaticSubscription Use Case:

  1. Create a Session
  2. Create a Login Session
  3. Create a publisher instance of class FileManager
  4. Raise some FileManager events
  5. Close the Sessions

We will now comment each of these sections in detail.

[Top]

Implement CAAIVpiFMEventCallbacks

The CAAVpiFileManagerListener class is an early subscriber that implements the specific callback interface provided by the publisher of the events of interest. For this use case, we are interested in the events emitted by the FileManager. Hence the early Subscriber will implement the CAAIVpiFMEventCallbacks interface.

Our implementation of this interface follows the CAA Object Modeler rules applied to all interface implementations.

Here is a sample of the important steps.

...
// Object modeler declarations
CATImplementClass( CAAVpiFileManagerListener, Implementation, CATBaseUnknown, CATNull );
#include "TIE_CAAIVpiFMEventCallBacks.h"
TIE_CAAIVpiFMEventCallBacks( CAAVpiFileManagerListener );

// Actual implementation
HRESULT CAAVpiFileManagerListener::onKo( const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode )
{
	//code...
}
HRESULT CAAVpiFileManagerListener::onBeforeCAACreateFile( const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode )
{
	//code...
}
HRESULT CAAVpiFileManagerListener::onAfterCAACreateFile( const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode )
{
	//code...
}

// Idem for Move and Delete File events...
...

To finalize the implementation of the callback interface, we do not forget to complete the object modeler dictionary properly by adding the line:
CAAVpiFileManagerListener CAAIVpiFMEventCallBacks libCAAVpiEventObjects
to the file CAAVPMInterfaces.edu.dic located in the directory CAAVPMInterfaces.edu/CNext/code/dictionary.

[Top]

Implement ENOVIEventPlugin

In this use case, the automatic subscription is made through a plug-in functionality. To turn this functionality on, the class CAAVpiPlugin will implement the ENOVIEventPlugin interface which declares a unique method : Init.

The whole mechanism is as follows:

Notes:

  1. in the Init method, the only allowed code is the subscription to the event CreateLoginSession step After
  2. the class that implements onAfterCreateLoginSession can be the same that the class implementing Init; for clarity purpose, we will have two distinct classes in the present use case
  3. this two-phase mechanism ensures that the subscriptions occur when the Login Session context is in a coherent state; this is not the case in the body of the Init method

Now comes the sample code that implements ENOVIEventPlugin according to this rules:

...
// Object modeler declarations
CATImplementClass( CAAVpiPlugin, DataExtension, CATBaseUnknown, T_CAAVpiPlugin );
#include "TIE_ENOVIEventPlugin.h"
TIE_ENOVIEventPlugin( CAAVpiPlugin );

// Init implementation
HRESULT CAAVpiPlugin::Init( const ENOVIEventManager_var &spEventManager )
{
	// (see source file for details...)
	// --> 1. Create an instance of CAAVpiPluginListener
	// --> 2. Check that the listener implements ENOVISessionEvent interface
	// --> 3. Retrieve a smart pointer on type CATBaseUnknown, required by the Subscribe method
	// --> 4. Subscribe to the event CreateLoginSession step After (early mode, on type)
}
...

To finalize the implementation of the ENOVIEventPlugin interface, we do not forget to complete the object modeler dictionary properly by adding the line:

T_CAAVpiPlugin ENOVIEventPlugin libCAAVpiEventObjects

to the file CAAVPMInterfaces.edu.dic located in the directory CAAVPMInterfaces.edu/CNext/code/dictionary

[Top]

Get the current Login Session

After the implementation of the ENOVIEventPlugin interface, we turn on to the code responsible of the subscriptions of interest.

This is done in the class CAAVpiPluginListener, that derives of the adapter class ENOVPackageListener and implements ENOVISessionEvent, in the callback method onAfterCreateLoginSession.

The first step in the onAfterCreateLoginSession method is to retrieve the VPM Login Session object as in the following code:

...

// Object modeler declarations
CATImplementClass( CAAVpiPluginListener, Implementation, CATBaseUnknown, CATNull );
#include "TIE_ENOVISessionEvent.h"
TIE_ENOVISessionEvent( CAAVpiPluginListener );

HRESULT CAAVpiPluginListener::onAfterCreateLoginSession( const ENOVIEvent_var& iRaisedEvent, HRESULT& ioNotifyReturnCode  )
{
    HRESULT Rc = S_OK;
    //
    // ----------------------------
    // --> 1. Get the VPM Session
    // ----------------------------
    //
    VPMSession *pSession= VPMSession::OpenSession();
    if ( NULL == pSession ) 
    {
        cout << "Unable to Get the VPM Session" << endl;
        return E_FAIL;
    }
    cout << ">>plugin listener: Get VPM Session done " << endl;
    //
    // ------------------------------
    // --> 1bis. Get the Login Session                                 
    // ------------------------------
    //
    CATIVpmLoginSession_var spLoginSession;
    Rc = pSession->GetLoginSession( spLoginSession );
    if ( FAILED(Rc) || !spLoginSession )
    {
	cout << "Unable to get the Login Session from the Session" << endl;
	return Rc;
    }
    cout << ">>plugin listener: Get Login Session done " << endl;
...

To finalize the implementation of the ENOVISessionEvent interface, we do not forget to complete the object modeler dictionary properly by adding the line:

CAAVpiPluginListener ENOVISessionEvent libCAAVpiEventObjects

to the file CAAVPMInterfaces.edu.dic located in the directory CAAVPMInterfaces.edu/CNext/code/dictionary

[Top]

Get the Event Manager

Here is the code to do it.

...
    //
    // ------------------------------
    // --> 2. Get the Event Manager                                   
    // ------------------------------
    //
    ENOVIEventManager_var spEventManager;
    Rc = spLoginSession->get_ENOVEventManager( spEventManager );
    if ( FAILED(Rc) )
    {
	cout<<"Unable to get the Event Manager from the Session"<<endl;
	return Rc;
    }
    cout << ">>plugin listener: Get Event Manager done " << endl;
...

The Event Manager is used to subscribe to events on object types or instances. By implementing appropriate callback interfaces declared by the ENOVIA domains (referred to as early subscribing) or a generic callback interface named ENOVISubscriberEvent (referred to as late subscribing), clients can do some actions when events are raised by a given type or a given instance of object.

[Top]

Create an instance of CAAVpiFileManagerListener class

In this step as soon as the subscriber is created, a query interface is carried out to retrieve the appropriate smart pointers the Subscribe method is waiting for.

...
    //
    // -----------------------------------
    // --> 3. Plugin Subscriber creation
    // -----------------------------------
    //
    CAAVpiFileManagerListener* pCAAVpiFileManagerListener = new CAAVpiFileManagerListener( "FileManager Subscriber" );

    CAAIVpiFMEventCallBacks* piCAAIVpiFMEventCallBacks = NULL;
    Rc = pCAAVpiFileManagerListener->QueryInterface( IID_CAAIVpiFMEventCallBacks, (void**)& piCAAIVpiFMEventCallBacks );
    pCAAVpiFileManagerListener->Release();
    pCAAVpiFileManagerListener = NULL;
    if ( FAILED(Rc) )
    {
	cout<<"Unable to get a CAAIVpiFMEventCallBacks handler on FileManager Subscriber"<<endl;
	return Rc;
    }

    CATBaseUnknown_var spPluginSubscriber = piCAAIVpiFMEventCallBacks;
    piCAAIVpiFMEventCallBacks->Release();
    piCAAIVpiFMEventCallBacks = NULL;
    if ( NULL_var == spPluginSubscriber )
    {
	cout<<"Unable to get a CATBaseUnknown handler on FileManager Subscriber"<<endl;
        return Rc;
    }

...

[Top]

Subscribe to the events raised by instances of FileManager type

This step is the subscription itself thanks to the Event Manager. It is referred to as a type subscription as the argument for the publisher is the string representing its type i.e. FileManager here.

...
    //
    // ----------------------------------------
    // --> 4. Subscribe on Create File
    //      (EARLY MODE Subscription on TYPE)
    // ----------------------------------------
    //
    CATUnicodeString EventName ( "CAACreateFile" );
    CATUnicodeString EventPublisherType ( "CAAVpiFileManager" );
    unsigned long cookie = 0;
    Rc = spEventManager->Subscribe( EventName, EventPublisherType, spPluginSubscriber, IID_CAAIVpiFMEventCallBacks, &cookie, ENOVIEvent::EventFireBefore );
    if ( FAILED(Rc) )
    {
	cout<<"Unable to subscribe on CreateFile"<<endl;
	return Rc;
    }
    cout << ">>plugin listener: Subscribe on CreateFile (before) done " << endl;

    Rc = spEventManager->Subscribe( EventName, EventPublisherType, spPluginSubscriber, IID_CAAIVpiFMEventCallBacks, &cookie, ENOVIEvent::EventFireAfter );
    if ( FAILED(Rc) )
    {
	cout<<"Unable to subscribe on CreateFile"<<endl;
	return Rc;
    }
    cout << ">>plugin listener: Subscribe on CreateFile (after) done " << endl;
    //
    // Etc. Idem for Move and Delete File events
...

[Top]

Create a resource file to declare the plug-in

Finally the last step of this first part is to declare our plug-in implementation through the creation of a resource file. To do this, in the directory CAAVPMInterfaces.edu/CNext/resources/msgcatalog, a file named CAAVpiPluginList.CATRsc has been created and just contains the line:

T_CAAVpiPlugin = "0";

which simply states that the late type T_CAAVpiPlugin implements the ENOVIEventPlugin interface and has to be called at the end of the Login Session creation.

However, at runtime, a new step is also required to declare the resource file name to be looked for, i.e.

export VPM_PLUGIN_OBJECTS_LIST=CAAVpiPluginList

This completes the first part of the CAAVpiAutomaticSubscription use case.

[Top]

Create a Session

Now begins the second part of this use case, which describes the main program.
The first thing to do in any Enovia program is to create the VPM Session. Here is the code to do it:

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

[Top]

Create a Login Session

Then we have to create the Login Session. During its creation the plug-in implementation will be called and just after its creation our subscriptions will be made.
The interest of this step is hence to see the traces at execution time: you should see the plug-in implementation being called, subscribing to event CreateLoginSession step After, and then the listener being called back and doing the subscriptions to the File Manager events.

...
    //
    // -------------------------------
    // --> 2. Login Session creation
    // -------------------------------
    //
    cout << "main: Create LoginSession ... " << endl;
    const int NetWorkCommunication = 0;
    CATIVpmLoginSession_var spLoginSession;
    Rc = pSession->CreateLoginSession( "MyUserID", "MyPassword", "MyRoleName", NetWorkCommunication, spLoginSession );
    if ( FAILED(Rc) )
    {
        cout<<"Unable to Create Login Session"<<endl;
        return 2;
    }
    cout << "main: Create LoginSession done " << endl << endl;
...

[Top]

Create a publisher instance of class FileManager

To fire the events we have subscribed to, we need an instance of the CAAVpiFileManager class.

Here is the corresponding code:

...
    //
    // -------------------------------
    // --> 3. File Manager creation
    // -------------------------------
    //
    cout << "main: Create File Manager ... " << endl;
    CAAVpiFileManager* pFileManager = new CAAVpiFileManager( "File Manager" );
    if ( NULL == pFileManager )
    {
        cout<<"Unable to Create File Manager"<<endl;
        return 3;
    }
    cout << "main: Create File Manager done " << endl << endl;
...

[Top]

Raise some FileManager events

Now we are ready to fire Create, Move and Delete File events and to look at the traces to see if our subscriber is actually called back.

...
    //
    // ---------------------------------------
    // --> 4. Fire Create, Move, Delete File
    // ---------------------------------------
    //
    CATUnicodeString FileName ( "CAAVpiUseCase.h" );
    CATUnicodeString FirstDirectory ( "PublicInterfaces" );

    Rc = pFileManager->CAACreateFile( FileName, FirstDirectory );
    if ( FAILED(Rc) )
    {
	cout<<"Unable to Create File"<<endl;
	return 4;
    }
    cout << "main: Create File done " << endl << endl;
    //
    // Etc. Same code for Move and Delete File...
...

[Top]

Close the Sessions

Each opened session must be closed as followed:

...
    // --------------------------------------------
    // --> 7. Close Login Session and VPM Session
    // --------------------------------------------
    //
    Rc = spLoginSession->Close();
    if ( FAILED(Rc) )
    {
	cout<<"Unable to Close Login Session"<<endl;
	return 7;
    }
    cout << "main: Close Login Session done " << endl;

    VPMSession::CloseSession();
    cout << "main: Close VPM Session done " << endl;
...

This step concludes the second part of the CAAVpiAutomaticSubscription use case.

[Top]

In Short

This use case has demonstrated one way to automatically subscribe to events via the ENOVIEventPlugin interface and how to be called back when they are raised.

[Top]


References

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

History

Version: 2 [Oct 2003] Document updated for new interface ENOVIEventPlugin
Version: 1 [Oct 2001] Document created
[Top]

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