= Bonita Engine Event handlers
:description: An event handler is an extension to the engine that is configured to run when a specified event occurs.

An event handler is an extension to the engine that is configured to run when a specified event occurs.

[NOTE]
====
For Subscription editions only.
====

Event handlers can help in adding new features to the Platform. You can use them to call your own services when specific events
happen like when a case starts, or when a user submits a task.

An event is a change to any object in the database (user, activity, processDefinition,... ).
You can create an event handler to track any change to any object in the database and take the appropriate action. For example:

* Catch PROCESSINSTANCE_CREATED to detect that a process instance has started, and notify the process supervisor.
* Catch ACTIVITYINSTANCE_STATE_UPDATED to detect that a human task become available, and send email to all the users eligible to perform it.

At the end of this page there is a list of all the events.

== Constraints

The platform executes events handlers synchronously at the exact moment the related entity is updated, therefore it executes
it in the same thread and same transaction that the one executing the element.

It means that:

* Events handlers should never block, i.e. no network call, no I/O on the filesystem.
* Events handlers should never throw exception, if it does, the related element execution will fail, meaning that the transaction is rolled back and the task (for instance) goes to the failed state, which is generally NOT what one wants.

[WARNING]
====

In a cluster environment, Event handlers must be Serializable (implements Serializable and have all its fields serializable or transient) because they are shared with other nodes of the cluster.
====

What can be done is:

* Calling your own services that should be retrieved statically. e.g. pushing information to a queue on which you will poll data.

What should not be done is:

* Call Rest APIs: Network call can drastically impact Bonita platform performance.
* Call Bonita platform Java APIs: It will not work, APIs handle the transaction and when the handler is called, we are already in transaction.

[NOTE]
====
If you still want to perform Network call or Bonita platform java APIs calls, you can do it by triggering those calls asynchronously using e.g. a message queue or an ExecutorService. 
Please note that the handler is triggered during the execution of a transaction so it's better to trigger the processing after the current transaction. This can be done by registering a `javax.transaction.Synchronization` using the `javax.transaction.TransactionManager`
====

== Example: deploy an event handler

This example shows an event handler that detects changes in the state of activity instances.

=== create a maven project for event handler jar

pom.xml :

// for the 'subs' parameter, see https://docs.asciidoctor.org/asciidoc/latest/subs/apply-subs-to-blocks/
[source,xml,subs="+macros"]
----
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="\http://maven.apache.org/POM/4.0.0 \http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.bonitasoft.example</groupId>
    <artifactId>event-handler-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <properties>
        <bonita.version>pass:a[{bonitaTechnicalVersion}]</bonita.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.bonitasoft.engine</groupId>
            <artifactId>bonita-server</artifactId>
            <version>${bonita.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
----

=== Create event handler class:

Create a class that implements `SHandler<SEvent>`.

src/main/java/org/bonitasoft/example/EventHandlerExample.java:

[source,java]
----
package org.bonitasoft.example;

import java.util.UUID;

import org.bonitasoft.engine.events.model.SEvent;
import org.bonitasoft.engine.events.model.SHandler;
import org.bonitasoft.engine.events.model.SHandlerExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EventHandlerExample implements SHandler<SEvent> {

    private static Logger logger = LoggerFactory.getLogger(EventHandlerExample.class);

    private final String identifier = UUID.randomUUID().toString();

    public EventHandlerExample() {
    }

    @Override
    public void execute(SEvent event) throws SHandlerExecutionException {
        logger.info("ExampleHandler: executing event {}", event.getType());

        // add your business logic here
    }

    @Override
    public boolean isInterested(SEvent event) {
        logger.info("ExampleHandler - event {} - asks if we are interested in handling this event instance",
         event.getType());
        // add your business logic here
        // for this example purpose, assume we are always interested
        return true;
    }

    @Override
    public String getIdentifier() {
        return identifier;
    }
}
----

=== Deploy jar

* Build event-handle-example-1.0-SNAPSHOT.jar using `mvn clean install` maven command.
* Copy event-handle-example-1.0-SNAPSHOT.jar in webapps/bonita/WEB-INF/lib/ folder (for tomcat bundle)

=== Register an event handler

An event handler is registered on an event by adding an entry to the appropriate map. The list of handlers registered can be extended in the xref:bonita-bpm-platform-setup.adoc[`bonita-tenant-sp-custom.xml`] file:

[source,xml]
----
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- add event handler bean definition -->
    <bean id="myEventHandlerExample" class="org.bonitasoft.example.EventHandlerExample">
    </bean>

    <bean id="eventHandlers" class="org.springframework.beans.factory.config.MapFactoryBean">
        <property name="targetMapClass">
            <value>java.util.HashMap</value>
        </property>
        <property name="sourceMap">
            <map>
                <entry key="PROCESSINSTANCE_STATE_UPDATED" value-ref="myEventHandlerExample"/>
            </map>
        </property>
    </bean>

</beans>
----

=== Test it

Restart web server and run a basic process and check bonita log file in folder tomcat/logs:

[source,log]
----
INFOS: THREAD_ID=78 | HOSTNAME=gt | ExampleHandler: event PROCESSINSTANCE_STATE_UPDATED - asks if we are interested in handling this event instance
...
INFOS: THREAD_ID=78 | HOSTNAME=gt | ExampleHandler: executing event PROCESSINSTANCE_STATE_UPDATED
----

== Filter an event

An event handler contains a filter, `isInterested`, which detects the relevant instances of the event.
The example below shows how to use the State Id of a flow node to filter for a particular state (in this case, failed).
Flownode State Ids are defined in the subclasses of `org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState`.
There is no exhaustive list; the set of states is extensible without notice.

[source,groovy]
----
public boolean isInterested(SEvent event) {
    boolean isInterested = false;

    // Get the object associated with the event
    Object eventObject = event.getObject();

    // Check that event is related to a task
    if (eventObject instanceof SFlowNodeInstance) {
        SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) eventObject;

        // Verify that state of the task is failed. See
        // FailedActivityStateImpl
        isInterested = (flowNodeInstance.getStateId() == 3);
    }

    return isInterested;
}
----

Event handlers are recursive, that is, if an event handler itself modifies something and triggers an event, the relevant event handler is called. This means you might need to include loop detection in your event handler.

== Event list

This is a snapshot of the events used in the Engine.

|===
|  |

| Service
| Events

| ActivityInstanceServiceImpl
| ACTIVITYINSTANCE_CREATED, HUMAN_TASK_INSTANCE_ASSIGNEE_UPDATED, ACTIVITYINSTANCE_STATE_UPDATED, ACTIVITY_INSTANCE_TOKEN_COUNT_UPDATED, HIDDEN_TASK_CREATED, HIDDEN_TASK_DELETED, PENDINGACTIVITYMAPPING_CREATED, PENDINGACTIVITYMAPPING_DELETED

| ActorMappingServiceImpl
| ACTOR_CREATED, ACTOR_DELETED, ACTOR_UPDATED, ACTOR_MEMBER_CREATED, ACTOR_MEMBER_DELETED

| CategoryServiceImpl
| CATEGORY_CREATED, CATEGORY_DELETED, CATEGORY_UPDATED

| CommandServiceImpl
| COMMAND_CREATED, COMMAND_DELETED, COMMAND_UPDATED

| SCommentServiceImpl
| COMMENT_CREATED, COMMENT_DELETED

| ConnectorInstanceServiceImpl
| CONNECTOR_INSTANCE_CREATED, CONNECTOR_INSTANCE_DELETED, CONNECTOR_INSTANCE_STATE_UPDATED, CONNECTOR_INSTANCE_UPDATED

| DependencyServiceImpl
| DEPENDENCY_CREATED, DEPENDENCYMAPPING_CREATED, DEPENDENCY_DELETED, DEPENDENCYMAPPING_DELETED, DEPENDENCY_UPDATED, DEPENDENCYMAPPING_UPDATED

| DocumentMappingServiceImpl
| DOCUMENTMAPPING_CREATED, DOCUMENTMAPPING_DELETED, DOCUMENTMAPPING_UPDATED

| SEventInstanceServiceImpl
| EVENT_INSTANCE_CREATED, EVENT_TRIGGER_INSTANCE_CREATED, EVENT_TRIGGER_INSTANCE_DELETED, MESSAGE_INSTANCE_CREATED, MESSAGE_INSTANCE_DELETED, MESSAGE_INSTANCE_UPDATED

| ExternalIdentityMappingServiceImpl
| EXTERNAL_IDENTITY_MAPPING_CREATED, EXTERNAL_IDENTITY_MAPPING_DELETED

| FlowNodeInstanceServiceImpl
| FLOWNODE_INSTANCE_DELETED

| GatewayInstanceServiceImpl
| GATEWAYINSTANCE_CREATED, GATEWAYINSTANCE_HITBYS_UPDATED, GATEWAYINSTANCE_STATE_UPDATED

| IdentityServiceImpl
| GROUP_CREATED, GROUP_DELETED, GROUP_UPDATED, METADATA_CREATED, METADATA_DELETED, METADATA_UPDATED, METADATAVALUE_CREATED, METADATAVALUE_DELETED, METADATAVALUE_UPDATED, ROLE_UPDATED, ROLE_CREATED, ROLE_DELETED, USER_UPDATED, USER_CREATED, USER_DELETED, USER_CONTACT_INFO_UPDATED, USER_CONTACT_INFO_CREATED, USER_CONTACT_INFO_DELETED, USERMEMBERSHIP_UPDATED, USERMEMBERSHIP_CREATED, USERMEMBERSHIP_DELETED

| JobServiceImpl
| JOB_DESCRIPTOR_CREATED, JOB_DESCRIPTOR_DELETED, JOB_PARAMETER_CREATED, JOB_PARAMETER_DELETED, JOB_LOG_CREATED, JOB_LOG_DELETED

| JobWrapper
| JOB_COMPLETED, JOB_EXECUTING

| ProcessDefinitionServiceImpl
| PROCESSDEFINITION_CREATED, PROCESSDEFINITION_DELETED, PROCESSDEFINITION_DEPLOY_INFO_UPDATED, PROCESSDEFINITION_IS_DISABLED_UPDATED, PROCESSDEFINITION_IS_ENABLED_UPDATED, PROCESSDEFINITION_IS_RESOLVED_UPDATED

| ProcessInstanceServiceImpl
| PROCESS_INSTANCE_CATEGORY_STATE_UPDATED, PROCESSINSTANCE_CREATED, PROCESSINSTANCE_DELETED, PROCESSINSTANCE_STATE_UPDATED, PROCESSINSTANCE_UPDATED

| ProfileServiceImpl
| PROFILE_CREATED, PROFILE_DELETED, PROFILE_UPDATED, ENTRY_PROFILE_CREATED, ENTRY_PROFILE_DELETED, ENTRY_PROFILE_UPDATED, PROFILE_MEMBER_DELETED

| ReportingServiceImpl
| REPORT_CREATED, REPORT_DELETED

| SupervisorMappingServiceImpl
| SUPERVISOR_CREATED, SUPERVISOR_DELETED

| ThemeServiceImpl
| THEME_CREATED, THEME_DELETED, THEME_UPDATED
|===
