Enterprise Architecture

VPM Persistency

Managing Persistent Publish Subscribe Data

Performing persistent operations on subscriptions and published events.
Use Case

Abstract

This article illustrates how to handle persistent Publish / Subscribe data by using the ENOVIPubSubManager interface.


What You Will Learn With This Use Case

This use case is intended to show you how to use some of the methods available on the ENOVIPubSubManager interface. These include:

[Top]

The CAAVpyPublishSubscribe Use Case

CAAVpyPublishSubscribe is a use case of the CAAVPMPersistency.edu framework that illustrates VPMPersistency framework capabilities related to Publish / Subscribe.

[Top]

What Do CAAVpySubscribe and CAAVpyPublish Do?

CAAVpySubscribe is the first part of this use case. In this scenario, the login user mainly subscribes to two events (CreateObject and Attaching) on type Document and to one event (Attached) on a newly created instance of type Document. CAAVpyPublish is the second part of this use case. In this scenario, a newly created instance of type Document is attaching the former instance created in CAAVpySubscribe. Then, the login user looks for the published events it subscribes to and checks that four events have been raised so far: CreateObject (twice), Attaching, and Attached.

[Top]

How to Launch CAAVpySubscribe and CAAVpyPublish

To launch CAAVpySubscribe and CAAVpyPublish, you will need to set up the build time environment, then compile CAAVpySubscribe and CAAVpyPublish along with their prerequisites, set up the run time environment, and then execute the use case. [1] Launch the use case by executing the following commands:

mkrun -c "CAAVpySubscribe"

and

mkrun -c "CAAVpyPublish"

[Top]

Where to Find the CAAVpySubscribe and CAAVpyPublish Codes

The CAAVpyPublishSubscribe use case is made of three modules of the CAAVPMPersistency.edu framework:

Windows InstallRootDirectory\CAAVPMPersistency.edu\CAAVpySubscribe.m\
InstallRootDirectory\CAAVPMPersistency.edu\CAAVpyPublish.m\
InstallRootDirectory\CAAVPMPersistency.edu\CAAVpyPubSubLib.m\
Unix InstallRootDirectory/CAAVPMPersistency.edu/CAAVpySubscribe.m/
InstallRootDirectory/CAAVPMPersistency.edu/CAAVpyPublish.m/
InstallRootDirectory/CAAVPMPersistency.edu/CAAVpyPubSubLib.m/

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

[Top]

Step-by-Step

Before launching these use cases, check for their common prerequisites.

Here are the logical steps of CAAVpySubscribe:

  1. Open a VPM Session
  2. Open a Login Session
  3. Get the PubSub Manager
  4. List available events on type Document
  5. Subscribe to two events on type Document
  6. Check subscriptions on type Document
  7. Create an instance of Document and Save
  8. Subscribe to one event on instance of Document
  9. Check subscriptions on instance
  10. Get all previous subscriptions
  11. Get all subscribers on type and instance of Document
  12. Close the Sessions

Then the logical steps of CAAVpyPublish are:

  1. Open a VPM Session
  2. Open a Login Session
  3. Get the PubSub Manager
  4. Create an instance of Document and Save
  5. Get my so far published events
  6. Retrieve the Document instance created in CAAVpySubscribe
  7. Fire events on Document objects and Save
  8. Get my so far published events again
  9. Delete all Document objects and Save
  10. Remove all published events
  11. Unsubscribe from all events
  12. Close the Sessions

[Top]

Part I : CAAVpySubscribe

Open a VPM Session

	VPMSession* pSession = VPMSession::OpenSession();
    	TEST_STEP("Open VPM Session", NULL==pSession);
      
...

Use the OpenSession static method of VPMSession in order to open a new VPM session and to obtain a pointer on it.

Note that in this use case, for clarity purpose, we defined a macro TEST_STEP whose function is to test the success of the current operation. In case of failure, a KO message is displayed and the program exits. Otherwise, a OK message is displayed and the program goes on.

Here is the code of this macro (defined in header CAAVpyDisplay.h of directory PrivateInterfaces) :

	#define TEST_STEP(message,condition)               \
	if ( condition ) {                                 \
	    	cout << step <<". "#message" KO"<< endl;       \
	    	VPMSession::CloseSession();                    \
	    	return step;                                   \
	}                                                  \
	cout << step <<". "#message" OK"<< endl;           \
	step++;
      

[Top]

Open a Login Session

...
    
	CATUnicodeString userID ("MyUserID");
    	CATUnicodeString userPassword ("MyPassword");
    	CATUnicodeString userRole ("MyRoleName");
    	const int NetWorkCommunication = 0;
    	CATIVpmLoginSession_var spLoginSession;
    	rc = pSession->CreateLoginSession( userID,           // ENOVIA User login Id
                                       userPassword,         // ENOVIA User login Password
                                       userRole,             // ENOVIA User login Role
                                       NetWorkCommunication, // Bus communication activation
                                       spLoginSession );     // Login Session
      
...

In order to create a new Login Session, use the CreateLoginSession method of VPMSession as exposed above.

The method returns a CATIVpmLoginSession smart pointer on the Login Session.

In this use case scenario, the login user identified by "MyUserID" is considered as our subscriber.

[Top]

Get the PubSub Manager

...
	ENOVIEventManager_var spEventManager;
    	rc = pSession->get_ENOVEventManager( spEventManager );    
    	ENOVIPubSubManager_var spPSM = spEventManager;
      
...

All persistent Publish / Subscribe operations are managed through a single interface : ENOVIPubSubManager.

To get a smart pointer on this interface, we have first to obtain a pointer on the ENOVIEventManager interface, using the get_ENOVEventManager method of VPMSession, and then to query the ENOVIPubSubManager interface.

[Top]

List available events on type Document

...
	CATUnicodeString publisherType ("ENOVIA_Document"); 
    	CATListOfENOVIEventDefinition listdefs;
    	rc = spPSM->GetRaisableEventDefs( publisherType, listdefs );
        Dump(listdefs);
      
...

One of the first operation that can be performed on ENOVIPubSubManager is obtaining all the events that can be subscribed to on a given object type. In this use case, we will be interested in the type Document (which is a dummy type for test purpose) defined in the domain "INDEX".

To do so, use the GetRaisableEventDefs method of ENOVIPubSubManager as shown above. The result is a list of smart pointers on ENOVIEventDefinition interface, which exposes methods to get the main characteristics of events as they are defined in the XML .event files.

Again, for clarity purpose, we defined a set of "Dump" methods that process different kinds of list of objects and display the main characteristics of each object in turn. The code of these methods can be found in the CAAVpyPubSubLib module.

Dumping the ENOVIEventDefinition interface yields a lot of information about the Document events (refer to the program traces). In this use case, we focus on events CreateObject (inherited from VPMObject), Attaching, and Attached. The last two events have been defined for the purpose of this use case, along with the associated callback interface CAAIVpyDocumentEvent located in the PrivateInterfaces directory.

As far as Publish / Subscribe is concerned (i.e. dealing with persistent and asynchronous events), the callback interface only appears when firing the events using the macro EVENT_FIRE. For the management of synchronous events and subscribing with callbacks, please refer to the corresponding technical article and use cases. [2, 3]

[Top]

Subscribe to two events on type Document

...
    
	CATUnicodeString createEvent ("CreateObject");
    	rc = spPSM->Subscribe( userID,
                           publisherType,
                           createEvent );

    	CATListOfENOVEventSubFilter listfil;
    	CATUnicodeString condition ("[name is_like Document*]");
    	CORBAAny anyValue;
    	anyValue<<condition;
    	rc = spPSM->BuildListOfSubFilter( listfil,
                                      "ON_PUBLISHER",
                                      "Attach_Condition",
                                      anyValue );

    	CATUnicodeString attachingEvent ("Attaching");
    	rc = spPSM->Subscribe( userID,
                           publisherType,
                           attachingEvent,
                           "","","", listfil );

    	spPSM->FreeSubFilters( listfil );
	
...

As shown above, subscribing to an event on a given type is simply performed by calling the method Subscribe of ENOVIPubSubManager. Note that the subscription applies immediately since it is committed directly in the database.

The first three mandatory arguments are the user ID, the publisher type, and the event name. In this use case, our user first subscribes inconditionally to event CreateObject on type Document, that is, it will be warned each time an instance of Document is created.

The next three arguments of Subscribe can be used to specify the means by which the user is to be notified. Though it is not used here, by default, we provide the means to be notified by e-mail through one of the "PSmail" shells, e.g. :

    
    	rc = spPSM->Subscribe( userID,
                           publisherType,
                           createEvent,
                           "PSmail_en.sh",
			   "MyUserID@MyHostName.com" );
	

Besides the name of the shell to launch, the next mandatory argument is a valid e-mail address.

Finally, there exists a last argument to specify some filters to be applied to the current subscription. They are given in a list of ENOVEventSubFilter objects that can be built before by calling the method BuildListOfSubFilter of ENOVIPubSubManager as many times as there are filters to define. After the Subscribe call, the list has to be emptied with special method FreeSubFilters.

As shown in the code above, our user subscribes to event Attaching on type Document using only one filter of category "ON_PUBLISHER". This means that, whenever an event Attaching will be raised, a condition will be applied on the publishing object (a Document instance) to decide whether or not the subscriber has to be notified.

Here the litteral condition is "[name is_like Document*]" which is true whenever the simple attribute "name" of a Document instance is valuated with a string beginning with "Document". In the following, we will see an other category of filter.

[Top]

Check subscriptions on type Document

...
    
	CATListOfENOVPubSubDetails listdet;
    	rc = spPSM->GetMySubscriptions( userID,
                                    publisherType,
                                    listdet );
	Dump(listdet);
	spPSM->FreePubSubDetails( listdet );
	
	ENOVPubSubDetails* psd = NULL;
	rc = spPSM->GetMySubscription( userID,
                                   publisherType,
                                   createEvent,
                                   psd );

	rc = spPSM->GetMySubscription( userID,
                                   publisherType,
                                   attachingEvent,
                                   psd );
      
...

To check the current subscriptions of a user, the ENOVIPubSubManager interface exposes a number of Get*MySubscription* methods. Here, we focus on the subscriptions on a given type (Document) to check the work done so far in this use case. The result is a list of generic ENOVPubSubDetails objects that contain information about either subscriptions or published events according to the context.

Note that a "Dump" display method has been defined for this list in the CAAVpyPubSubLib module. Refer to this code and to the program traces to know the valuable information that can be retrieved from ENOVPubSubDetails objects. Note also that the list must be emptied after use with special method FreePubSubDetails.

When the event name is also specified in the query, the result is just one ENOVPubSubDetails, since to a given ( user, publisher, event ) triplet corresponds one subscription only. Do not forget to release it using the C++ "delete" keyword as illustrated in the use case. When there is no corresponding subscription, the pointer is null.

[Top]

Create an instance of Document and Save

The corresponding code will not be treated here since it is the subject of another specific use case of this framework, i.e. CAAVpyGeneralPersistency. [4] We can however sum up the different steps :

  1. Retrieve the Factory Manager from the VPM Session
  2. Retrieve the Domain Container (here the Domain is "INDEX") from the VPM Session
  3. Create a VPM object of type Document with the Factory Manager
  4. Valuate the mandatory attributes of a Document ("name" and "doctype") using interface CATIVpmAttribute
  5. Save the Login Session

Note that the Document instance is named by a string beginning with "Document", so that this instance verifies the condition defined in the above subscription filter.

Note also that saving the Login Session implies that all persistent events raised since the opening of the current VPM Session or since the last save are logged in the database and the persistent subscribers are notified accordingly. This means that our user helds now one published event, that is, event "CreateObject" fired by the just created Document instance.

[Top]

Subscribe to one event on instance of Document

...
    CATUnicodeString status ("Succeeded");
    anyValue<<status;
    rc = spPSM->BuildListOfSubFilter( listfil,
                                      "ON_EVENT",
                                      "Status",
                                      anyValue );

    ENOVIPublisher_var spPublisher = spObject;
    CATUnicodeString attachedEvent("Attached");
    rc = spPSM->Subscribe( userID,
                           spPublisher,
                           attachedEvent,
                           "","","",listfil );

    spPSM->FreeSubFilters(listfil);
      
...

The ENOVIPubSubManager interface allows also to subscribe on given instances of objects. Here our user takes the just created Document instance to subscribe on event "Attached". The second argument is no more the type of the object but instead a smart pointer on interface ENOVIPublisher which can be retrieved from every instances of type CATBaseUnknown.

Additionally, the user specifies one subscription filter of category "ON_EVENT". This category applies a condition on the event itself (instead of the publishing object), more precisely on the arguments of the associated data stream. The above filter means that the user will be notified if, and only if, the "Status" argument of the data stream associated to the "Attached" event is equal to "Succeeded".

The data stream characteristics of an event can be retrieved from the ENOVIEventDefinition interface, as shown previously in this use case. Refer to the program traces and check that event "Attached" effectively has an associated data stream named "AcknowledgeStream" with one of its arguments named "Status".

[Top]

Check subscriptions on instance

...
    rc = spPSM->GetMySubscriptions( userID,
                                    spPublisher,
                                    listdet );
    Dump(listdet);
    spPSM->FreePubSubDetails(listdet);

    rc = spPSM->GetMySubscription( userID,
                                   spPublisher,
                                   attachedEvent,
                                   psd );
	
...

As there exist methods to get the subscriptions on given types, ENOVIPubSubManager also exposes methods to get the subscriptions on given instances. The user has to specify the ENOVIPublisher view of the object instead of the object type. Everything is the same besides.

[Top]

Get all previous subscriptions

...
    rc = spPSM->GetAllMySubscriptions( userID,
                                       listdet );
    Dump(listdet);
    spPSM->FreePubSubDetails( listdet );
	
...

Here we illustrate the general method GetAllMySubscriptions which allows to get all information about the subscriptions performed by a given user. In this use case, we finally get our user's three subscriptions:

Event Document Condition
CreateObject Type NONE
Attaching Type ON_PUBLISHER
Attached Instance ON_EVENT

[Top]

Get all subscribers on type and instance of Document

...
    CATListOfCATUnicodeString listuser;
    rc = spPSM->GetSubscribers( publisherType,
                                listuser );
    Dump(listuser);
    listuser.RemoveAll();

    rc = spPSM->GetSubscribers( spPublisher,
                                listuser );
    Dump(listuser);
    listuser.RemoveAll();
	
...

For completeness, we illustrate here how to get user IDs of subscribers to events on given types or instances, through the GetSubscribers methods. The result is a list of string.

Note that there exist two other GetSubscribers methods which allow to specify the event name to restrict the query. Finally, a GetSubscribersOnEvent method allows to get the subscribers to a given event, whatever the types or instances may be.

[Top]

Close the Sessions

...
    rc = spLoginSession->Close();

    VPMSession::CloseSession();
	

To conclude this first part related to subscriptions, we close the open sessions using the Close method of CATILoginSession and the CloseSession method of VPMSession.

Let's now move on to the second part, CAAVpyPublish, in which we will raise the events our user subscribed to, in order to show methods related to published events.

[Top]

Part II : CAAVpyPublish

Open a VPM Session

Same code as in CAAVpySubscribe.

[Top]

Open a Login Session

Same code as in CAAVpySubscribe.

[Top]

Get the PubSub Manager

Same code as in CAAVpySubscribe.

[Top]

Create an instance of Document and Save

Same code as in CAAVpySubscribe.

Here we create another Document instance to be able to fire events on it.

For illustration purpose and to prepare the next steps, the Document instance is named by a string beginning with "Document", so that this instance verifies the condition filter defined in CAAVpySubscribe.

[Top]

Get my so far published events

...
    
    CATListOfENOVPubSubDetails listdet;
    spPSM->GetAllMyPublishedEvents( userID, listdet );
    Dump(listdet);
    spPSM->FreePubSubDetails(listdet);
	
...

The method GetAllMyPublishedEvents of ENOVIPubSubManager allows to retrieve the so far published events for a given subscriber. The result is a list of ENOVPubSubDetails objects, already described above.

At this stage of the use case, only two events have been published for our user, that is, the two "CreateObject" events raised after the creation of the two Document instances.

[Top]

Retrieve the Document instance created in CAAVpySubscribe

...
    CATUnicodeString entityName ("Document");
    CATIVpmPathExpression_var spPathExpr = CreatePathExpression( entityName, docName ); 
    
    CATUnicodeString queryValue ("Document-1-CAAVpySubscribe-UC");
    anyValue<<queryValue;
    CATIVpmPredicate_var spPredicate = (spPathExpr == anyValue);
    
    CATIVpmQuery_var spQuery = CreateQuery( entityName, NULL_var, spPredicate, docName );
    
    CATLISTV(CATBaseUnknown_var)* listDocument = new CATLISTV(CATBaseUnknown_var);
    rc = spFactory->RunQuery( spQuery, domainName, spContainer, listDocument );
    
    CATIVpmFactoryObject_var spDocument1 = (*listDocument)[1];
    delete listDocument;
	
...

These lines of code illustrates how to retrieve a given instance from the database using a query. Here we want to recover the Document instance previously created in the first part to be able to fire events on it (remember that our user has subscribed to an event on this specific instance).

As this is not the subject of this use case, we won't comment too much this part. The most important thing is that we query objects of type Document (in domain "INDEX"), whose "name" attribute is exactly equal to "Document-1-CAAVpySubscribe-UC". So we expect to retrieve only one object, which is the instance created in the first part.

[Top]

Fire events on Document objects and Save

...
    {   // Note the opening bracket to simulate being
        // inside the scope of the firing method

        ENOVIStream_var spPublisherStream, spClientStream;
        OBJECT_DATA_EVENT_INIT(spDocument2,Attaching,spPublisherStream);
        
        CATUnicodeString iDomain = spDocument2->GetDomainName();
        anyValue<<iDomain;
        spPublisherStream->AddArgumentValue("Domain",anyValue);
        // Other arguments ...

        OBJECT_EVENT_FIRE(spDocument2,Attaching,CAAIVpyDocumentEvent,Before,spClientStream,rc);
        
        {   // New opening bracket to ensure isolation between events
            
            ENOVIStream_var spPublisherStream, spClientStream;
            OBJECT_DATA_EVENT_INIT(spDocument1,Attached,spPublisherStream);
            
            CATUnicodeString iStatus ("Succeeded");
            anyValue<<iStatus;
            spPublisherStream->AddArgumentValue("Status",anyValue);
            // Other arguments ...
            
            OBJECT_EVENT_FIRE(spDocument1,Attached,CAAIVpyDocumentEvent,Before,spClientStream,rc);
            
            OBJECT_EVENT_FIRE(spDocument1,Attached,CAAIVpyDocumentEvent,After,spClientStream,rc);
        }
        
        OBJECT_EVENT_FIRE(spDocument2,Attaching,CAAIVpyDocumentEvent,After,spClientStream,rc);
    }

    rc = spLoginSession->Save();
	
...

Event firing does not directly concern the ENOVIPubSubManager interface but is actually in the heart of the whole ENOVIA LCA Event model on which the Publish / Subscribe model is itself built. In a realistic application, this part of the scenario is performed in every implementations of ENOVIA objects that raise events (as declared in the XML .event files).

For our purpose, we simulate the attachment of the first document, created in CAAVpySubscribe, by the second document, created in CAAVpyPublish. The simulation consists simply in raising event "Attached" on the first document and event "Attaching" on the second document.

These events must be embedded (see the brackets) to ensure their isolation and the correct processing of each macro call. The OBJECT_EVENT_FIRE and OBJECT_DATA_EVENT_INIT macros have to be used since we are outside the implementation of the Document object.

As both events have an associated data stream, we valuate their arguments accordingly with the method AddArgumentValue of ENOVIStream. The important thing to note for the use case is that the "Status" argument has value "Succeeded" so that event "Attached" verifies the condition filter defined in CAAVpySubscribe.

Finally the Login Session is saved, which triggers the processing of persistent events and the notification of their subscribers, as we will check below.

[Top]

Get my so far published events again

Same code as above.

At this stage of the use case, four events have been published for our user: the two "CreateObject" events raised after the creation of the two Document instances, and the just fired events, "Attaching" and "Attached" since the condition filters defined in the respective subscriptions have been verified :

Event Publisher Filter Condition Checked ?
Attaching Document2 ON_PUBLISHER Attribute "name" starts with "Document" YES
Attached Document1 ON_EVENT Argument "Status" equals "Succeeded" YES

[Top]

Delete all Document objects and Save

...
    LifeCycleObject_var spLifecycle;

    spLifecycle = spDocument1;
    spLifecycle->remove();
   
    spLifecycle = spDocument2;
    spLifecycle->remove();
    
    rc = spLoginSession->Save();
	
...

Before closing the Sessions, the last part of this use case is dedicated to cleanup. Here we destroy the two Document instances we created, using the method remove of interface LifeCycleObject.

[Top]

Remove all published events

...
    rc = spPSM->RemoveAllMyPublishedEvents( userID );
	
...

We also delete all events published so far for our user, using the method RemoveAllMyPublishedEvents of ENOVIOPubSubManager. A method RemovePublishedEvent is also available to delete one event at a time, providing the user, the publisher, and the event as parameters.

Note that these methods take effect immediately since a commit is done in the Publish / Subscribe tables: a call to GetAllMyPublishedEvents now returns an empty list.

[Top]

Unsubscribe from all events

...
    rc = spPSM->UnsubscribeAll( userID );
	
...

We also delete all subscriptions performed by our user, using the method UnsubscribeAll of ENOVIOPubSubManager. Other methods Unsubscribe are also available to delete one subscription at a time, providing the user, the publisher, and the event as parameters.

Note that these methods take effect immediately since a commit is done in the Publish / Subscribe tables: a call to Get*MySubscription* now returns an empty list.

[Top]

Close the Sessions

Same code as in CAAVpySubscribe.

This concludes the CAAVpyPublish part and the CAAVpyPublishSubscribe use case.

[Top]


In Short

The purpose of this use case was to illustrate most of the useful methods exposed by the ENOVIPubSubManager interface to handle persistent Publish / Subscribe data.

We have first seen how to retrieve information about declared Events on given types of objects. Then we have studied how to subscribe to these Events, either on types or on instances, and also how to define condition filters on the Subscriptions.

We have given sample codes showing how to retrieve information either about current Subscriptions or about so far published Events. Finally, we have seen how to unsubscribe from Events and how to delete published Events of no more interest.

[Top]


References

[1] Building and Launching a CAA V5 Use Case
[2]
[3]
[4] The CAAVpyGeneralPersistency Use Case
[Top]

History

Version: 1 [March 2002] Document created
[Top]

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