Enterprise Architecture |
VPM Persistency |
Managing Persistent Publish Subscribe DataPerforming persistent operations on subscriptions and published events. |
Use Case |
AbstractThis article illustrates how to handle persistent Publish / Subscribe data by using the ENOVIPubSubManager interface. |
This use case is intended to show you how to use some of the methods available on the ENOVIPubSubManager interface. These include:
[Top]
CAAVpyPublishSubscribe is a use case of the CAAVPMPersistency.edu framework that illustrates VPMPersistency framework capabilities related to Publish / Subscribe.
[Top]
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]
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]
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]
Before launching these use cases, check for their common prerequisites.
Here are the logical steps of CAAVpySubscribe:
Then the logical steps of CAAVpyPublish are:
[Top]
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]
... 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]
... 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]
... 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]
... 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]
... 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]
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 :
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]
... 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]
... 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]
... 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]
... 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]
... 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]
Same code as in CAAVpySubscribe.
[Top]
Same code as in CAAVpySubscribe.
[Top]
Same code as in CAAVpySubscribe.
[Top]
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]
... 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]
... 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]
... { // 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]
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]
... 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]
... 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]
... 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]
Same code as in CAAVpySubscribe.
This concludes the CAAVpyPublish part and the CAAVpyPublishSubscribe use case.
[Top]
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]
[1] | Building and Launching a CAA V5 Use Case |
[2] | |
[3] | |
[4] | The CAAVpyGeneralPersistency Use Case |
[Top] |
Version: 1 [March 2002] | Document created |
[Top] |
Copyright © 2002, Dassault Systèmes. All rights reserved.