Trigger Framework
Introduction
A Trigger is an extension point that enables components to fire a certain event which in turn can be handled by observing components that listen to this event. So triggers follow the observable or listener pattern. A trigger can have certain parameterized trigger rules with which can be defined when an observer shall be informed about an event.
Triggers have first been introduced together with the dataquality feature and are currently used for this purpose.
As an example consider the import process where triggers are already in standard code. After the import has been finished, a trigger event is fired to tell all observers about it. Each trigger event contains the payload of the event in form of an entity item list - in the case of the import example the catalog items that are imported. Furthermore a trigger event contains any number of additional event data that parameterizes the event. For example it could contain the used import mapping of the import or who started a merge process etc. An observer may then f.e. only listen to the import finished event if a certain mapping has been used.
It is important to understand that a trigger extension itself like the post import trigger is just a scheme that can be used. When an observer wants to listen to a trigger event, he registers a trigger configuration and a listener at the trigger service. The listener is called if the trigger applies. The configuration defines when a trigger applies. So you could say that you only listen to a post import trigger if a certain import mapping has been used. In our basic trigger setups a listener is only called if ALL of the parameter values in the event match those defined in the trigger configuration.
Trigger Framework
Trigger
A Trigger enables components to fire a certain event which in turn can be handled by observing components that listen to this event. So triggers follow the observable or listener pattern. A trigger can have certain parameterized trigger rules with which can be defined when an observer shall be informed about an event.
Available Triggers
Trigger name |
Identifier |
Description |
Available since |
Import completed |
hlr.import.trigger.importFinished |
Called when an import has been completed, either successfully or with warnings/errors |
7.0.03 |
Merge started |
hlr.merge.trigger.mergeStarted |
Called whenever a merge has just been started |
7.0.03 |
Merge completed |
hlr.merge.trigger.mergeFinished |
Called whenever the merge process completed either successfully or with warnings/errors |
7.0.03 |
Export started |
hlr.trigger.preExport |
Called when export has been started but before any data is written to export channel |
7.0.05.09 |
TriggerRule
A TriggerRule contains a set of parameters that can be used to define if an observer wants to listen to a trigger event.
TriggerConfiguration & TriggerRuleConfiguration
These are the actual instances that refer to each a trigger and a trigger rule. The TriggerRuleConfiguration also contains the values of the parameters, f.e. if the user chooses a special catalog.
TriggerEvent
The trigger event is the actual transport object, which contains the payload data that shall be further processed as well as additional parameter data. F.e. the payload data may be a catalog item list and the parameter data the supplier of that catalog.
TriggerService
The TriggerService is the central service for handling triggers in the PIM Server platform. It provides methods to obtain trigger meta data as well as methods to register for trigger events and to fire trigger events.
Register a trigger listener
A trigger listener may be registered with the trigger service:
TriggerService::registerListener(TriggerConfiguration triggerConfiguration, TriggerEventListener listener);
A TriggerConfiguration contains the contributed trigger itself as well as all rules and parameters with values that will be used.
TriggerEventListener is an interface that only has one method to implement:
triggerFired( IProgressMonitor progressMonitor, ProblemLog problemLog, TriggerEvent triggerEvent );
Firing a trigger event
In order to fire a trigger event create a new instance of the TriggerEventImpl class.
First the trigger itself is needed. The trigger may be obtained from the Trigger Service by using its trigger identifier defined in the extension:
TriggerService triggerService = TriggerServiceComponent.getTriggerService();
Trigger trigger = triggerService.getTrigger(
"hlr.import.trigger.importFinished"
);
The constructor for a trigger event also needs a trigger and an EntityItemList as event payload.
TriggerEvent event = TriggerEventImpl( Trigger trigger, EntityItemList entityItemList );
A trigger event is finally propagated by firing the event via the trigger service.
TriggerResult triggerResult = triggerService.fireTrigger(
new
SubProgressMonitor( progressMonitor,
1000
), problemLog, event );
Note that firing a trigger and calling subsequent listeners is done synchronously!
Trigger Results
A component firing an event does not know anything about the observers that listen to the event. That also means that the event caller does not know about the observer’s context or anything that is going on in called listeners. In order to provide some sort of data sharing between event caller and observers, firing a trigger via TriggerService::fireTrigger returns a TriggerResult . A TriggerResult contains a set of TriggerEventListenerResult which is returned by each called listener.
TriggerEventListenerResult TriggerEventListener::triggerFired( IProgressMonitor progressMonitor, ProblemLog problemLog, TriggerEvent triggerEvent )
throws
InterruptedException;
Trigger result data of a certain type can be retrieved from the TriggerResult:
< C > Collection< C > TriggerResult::getListenerResults( Class< C > resultClass );
Classes that may be part of the result must implement the TriggerEventListenerResult interface (marker interface). For example if a component fires a trigger and thinks this may trigger some data quality executions the call may look like this:
triggerResult.getListenerResults( DataQualityTriggerEventListenerResult.
class
);
This way a list of all rule configurations that are used by all triggered dataquality executions could be retrieved. In fact this is used for the pre merge process in order to provide a firewall to let only good data to pass!
Detailed explanation using an example
Let's stick with the import post trigger and dataquality example scenarios.
Extension point definition
The post import trigger is defined in the com.heiler.ppm.importer5.core plugin.
Each trigger has to have a global unique identifier. Along with the usual name and description values an icon can be defined which can be used for the ui. There is no need to define the trigger two times in the core and the ui just to make the icon image resource available. However don’t forget to include the folder containing the icon in the binary build.
Trigger rules contain parameters that can be used for a certain trigger:
Apart from a unique rule identifier, the most important thing is the trigger identifier which refers to the trigger in which the rule is contained. Note that trigger rules are not directly contained inside the trigger definition of the extension. The idea is that rules may be added to a trigger by any plugin. In the case of the import finished trigger the additional rules are defined in the article.core.dataimport plugin:
The reason here is to keep package dependencies as they are as much as possible - certain parameters may need classes that aren’t available directly in the package of the trigger extension definition. In general it could also be that some parameters are dependent from a specific entity type. Importing into a catalog may provide a supplier of the catalog.
The evaluator is used when validating whether a listener should be called. The evaluator has to implement the TriggerRuleEvaluator interface. If none is defined the basic implementation takes place:
Go through all parameters of the TriggerRuleConfiguration
If the value of the parameter is also contained in the TriggerEvent or the parameter is mandatory, then try to compare the parameter values. For the rule configuration parameters a default value is retrieved if no value is provided.
If one of the comparisons fails then the trigger won’t fire and listeners are not called
The Trigger service fires a trigger only if ALL of the rules pass the validation. This logic cannot be changed.
Each trigger rule finally defines a set of parameters.
For a seamless integration and code reusability purposes the same parameter classes are used as f.e. in entity reports.
Registering the trigger listener
In our example the data quality trigger event listeners, which are configured by a user in the data quality perspective in PIM - desktop, get registered while the trigger event configuration is getting saved, as well as when the server is starting up, all configured triggers are getting loaded and registered. The following snippet shows the registration of a data quality event trigger ( which is called event configuration):
TriggerService triggerService = TriggerServiceComponent.getTriggerService();
DataQualityTriggerEventListener listener = createListener( eventConfiguration );
this
.triggerService.registerListener( eventConfiguration.getTriggerConfiguration(), listener );
The data quality (DataQualityTriggerEventListener) listener it self has the following implementation:
RuleConfigurationSelection ruleConfigurationSelection =
this
.eventConfiguration.getRuleConfigurationSelection();
EntityItemList entityItemList = triggerEvent.getEntityItemList();
Collection< RuleConfiguration > executedRules = executeMappletSelection( progressMonitor, problemLog, ruleConfigurationSelection, entityItemList );
What this code does is essentially execute all rules that are defined in the event (ruleConfigurationSelection) and execute it with the payload data from the event (entityItemList).
Firing a trigger event
First the trigger itself is needed. The trigger may be obtained from the Trigger Service by using its trigger identifier defined in the extension:
TriggerService triggerService = TriggerServiceComponent.getTriggerService();
Trigger trigger = triggerService.getTrigger(
"hlr.import.trigger.importFinished"
);
After trigger allocation the actual event has to be instantiated. The constructor for a trigger event needs the actual trigger and an EntityItemList as event payload, which may be a catalog item list for example.
TriggerEvent event = TriggerEventImpl( Trigger trigger, EntityItemList entityItemList );
for the import example here is an extract from the import job code how the import event parameter values are filled:
ImportProfile importProfile = importInfo.getImportProfile();
event.setEventData(
"importProfile"
, importProfile.getEntityProxy() );
EntitySpecificImportData importData = importProfile.getEntitySpecificImportData( entityIdentifier );
Map< String, Object > eventData = importData.getEventData();
for
( Map.Entry< String, Object > entry : eventData.entrySet() ) {
event.setEventData( entry.getKey(), entry.getValue() );
}
A trigger event is finally propagated by firing the event via the trigger service.
TriggerResult triggerResult = triggerService.fireTrigger(
new
SubProgressMonitor( progressMonitor,
1000
), problemLog, event );
Triggers usage in Data Quality
The dataquality integration offers a ui component in PIM - Desktop to manage triggers that are used to execute dataquality rules on certain events. The left part of the view "Data quality scheduling" contains all contributed triggers (except the "time controlled" entry which is used for scheduled execution). The leaf entries represent an instance of a trigger configuration. A trigger rule with its parameter will only be part of the trigger configuration if it is enabled (checked). Otherwise the rule won't be considered when determining whether an event should trigger a listener to be called.
Current possible trigger configurations are 'Import completed', 'Merge started', 'Merge completed', 'Export started', 'Entity changed' and 'Entity created'. For each event a different configuration parameters may be defined - f.e. an import completed trigger that only applies for a certain mapping etc.
Please note that if a trigger has a parameter for a field like the 'Modification field' in the 'Entity changed' trigger then there must not be a space between field name and the opening bracket for field qualifications. A correct value would be for example 'ArticleLang.DescriptionShort(7)'.
Example for 'Import completed'
Example for 'Entity changed'
Execution Flow Description
The data quality user creates a "import finished" event configuration within the data quality perspective of PIM - Desktop. Let's pretend the following trigger event configuration as an example:
import finished trigger start condions |
|
supplier |
HENRI |
supplier catalog |
HENRI wintercollection |
As the user clicks the save button, a trigger configuration is going to be created in the background. The trigger configuration consists of two tigger rule configurations, one with the supplier as an parameter and the other with the supplier catalog as an parameter. In addition the trigger configuration is getting registered as an listener for the "import finished" trigger event. The data quality trigger event listener includes the created trigger configuration and implements the callback logic which is getting executed if the import finished event is fired.
In an upcoming event the supplier "HENRI" uploads new catalog data to his catalog "HENRI wintercollection". After the import finished successfully, the import job fires the "import finished" trigger event. This calls the trigger service, which iterates through the list of event listeners an evaluates the trigger rules and trigger rule evaluators. In this process the previously registered trigger configuration also got evaluated and because all the trigger rules match, the data quality listener is getting notified. The data quality trigger event listener executes a data quality check with the rule configuration associated to the listener as well as the entity items which came with the trigger event, in this case the items which have been imported into the HENRI wintercollection catalog.