Processor - implement a new processor
In this tutorial, we will describe how to create a new processor. Before we begin to build a new processor, there is a little bit of background of what exactly the processor is.
Here is documentation about processors: Events - processing of events
Preparation
There is no big preparation before the first steps. As long as you know when does this processor needs to be placed and what it will listen to, we are ready to begin!
In our example, we will show a processor that sends a notification every time a new node is created in the organizational structure. In this case, it will listen to IdmTreeNodeDto change - especially TreeNodeEvent.TreeNodeEventType.CREATE - when something is created.
As you might already know it will be placed after save. Here it is +1000 in order.
Creation of processor
As you can see in the example below there are few this all processor has to have.
* Extends AbstractEntityEventProcessor<Entity> - here entity is IdmTreeNodeDto
* Function getOrder() - this function is not obligatory but is strongly recommended to use it - it sets the order of our processor - when it will start
* In the constructor you define what type of event it will listen to - create/update/delete or its combinations
* setName() method - each processor has its own name and here is the method where you can set it
* process method - this is the core of the processor. In this method, all actions are held.
package eu.bcvsolutions.idm.extras.event.processor.tree; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Description; import org.springframework.stereotype.Component; import eu.bcvsolutions.idm.core.api.dto.IdmIdentityDto; import eu.bcvsolutions.idm.core.api.dto.IdmTreeNodeDto; import eu.bcvsolutions.idm.core.api.event.AbstractEntityEventProcessor; import eu.bcvsolutions.idm.core.api.event.DefaultEventResult; import eu.bcvsolutions.idm.core.api.event.EntityEvent; import eu.bcvsolutions.idm.core.api.event.EventResult; import eu.bcvsolutions.idm.core.api.service.ConfigurationService; import eu.bcvsolutions.idm.core.api.service.IdmConfigurationService; import eu.bcvsolutions.idm.core.api.service.IdmIdentityService; import eu.bcvsolutions.idm.core.model.event.TreeNodeEvent; import eu.bcvsolutions.idm.core.notification.api.domain.NotificationLevel; import eu.bcvsolutions.idm.core.notification.api.dto.IdmMessageDto; import eu.bcvsolutions.idm.core.notification.api.service.NotificationManager; import eu.bcvsolutions.idm.extras.ExtrasModuleDescriptor; /** * After creation of new tree node sends notification to authority * * @author Marek Klement */ @Component @Description("After creation of new tree node sends notification to authority") public class NewTreeNodeProcessor extends AbstractEntityEventProcessor<IdmTreeNodeDto> { // name of the processor public static final String PROCESSOR_NAME = "new-node-processor"; public static final String TREE_NODE_CREATE_ROLE = IdmConfigurationService.IDM_PRIVATE_PROPERTY_PREFIX + ExtrasModuleDescriptor.MODULE_ID + ".treeNodeRecipient"; @Autowired private NotificationManager notificationManager; @Autowired private IdmIdentityService identityService; @Autowired private ConfigurationService configurationService; public NewTreeNodeProcessor() { super(TreeNodeEvent.TreeNodeEventType.CREATE); } @Override public String getName() { return PROCESSOR_NAME; } @Override public EventResult<IdmTreeNodeDto> process(EntityEvent<IdmTreeNodeDto> event) { IdmTreeNodeDto treeNode = event.getContent(); // String roleName = configurationService.getValue(TREE_NODE_CREATE_ROLE); if (roleName == null) { return new DefaultEventResult<>(event, this); } List<IdmIdentityDto> recipients = identityService.findAllByRoleName(roleName); // if (treeNode != null) { notificationManager.send( ExtrasModuleDescriptor.TOPIC_NEW_TREE_NODE, new IdmMessageDto.Builder() .setLevel(NotificationLevel.SUCCESS) .addParameter("treeNodeName", treeNode.getName()) .addParameter("treeNodeCode", treeNode.getCode()) .addParameter("created", treeNode.getCreated()) .addParameter("uid", treeNode.getId()) .build(), recipients); } // always return DefaultEventResult return new DefaultEventResult<>(event, this); } @Override public int getOrder() { // after create return 1000; } }
Important notes
Here are some really good advice and important procedures to be followed when you are creating a new processor. Read carefully this part!
Save vs. Publish
This is usually a major issue when writing a new processor. Using of Save method in the processor may result in the main issue with synchronization or another thing.
* When save is called it will create a new event * This new event has its own properties which are different than those from the original event * Original event may have skip provisioning in properties, but the new one does not have * Same thing with recalculation of auto roles
This diagram shows what actually happens. Look also at the code below to see the proper solution.
/** * Save identity trough new event with reference on parent event. * * @param identity * @param parentEvent */ private void saveIdentity(IdmIdentityDto identity, EntityEvent<?> parentEvent) { // Create new event type IdentityEventType eventType = identityService.isNew(identity) ? IdentityEventType.CREATE : IdentityEventType.UPDATE; // create new event with parents properties IdentityEvent event = new IdentityEvent(eventType, identity, parentEvent.getProperties()); // sets parent event.setParentId(parentEvent.getId()); identityService.publish(event, parentEvent); }