Processor - implement a new processor

Do not skip Important notes part!

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

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.

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;
	}
}

Here are some really good advice and important procedures to be followed when you are creating a new processor. Read carefully this part!

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);
	}
  • by klementm