Custom implementation
Provide a custom history backend
In order to understand how to provide a custom history backend, it is useful to first look at a more detailed view of the history architecture:
Whenever the state of a runtime entity is changed, the core execution component of the process engine fires History Events. In order to make this flexible, the actual creation of the History Events as well as populating the history events with data from the runtime structures is delegated to the History Event Producer. The producer is handed in the runtime data structures (such as an ExecutionEntity or a TaskEntity), creates a new History Event and populates it with data extracted from the runtime structures.
The event is next delivered to the History Event Handler which constitutes the History Backend. The drawing above contains a logical component named event transport. This is supposed to represent the channel between the process engine core component producing the events and the History Event Handler. In the default implementation, events are delivered to the History Event Handler synchronously and inside the same JVM. It is however conceptually possible to send the event stream to a different JVM (maybe running on a different machine) and making delivery asynchronous. A good fit might be a transactional message Queue (JMS).
Once the event has reached the History Event Handler, it can be processed and stored in some kind of datastore. The default implementation writes events to the History Database so that they can be queried using the History Service.
Exchanging the History Event Handler with a custom implementation allows users to plug in a custom History Backend. To do so, two main steps are required:
- Provide a custom implementation of the HistoryEventHandler interface.
- Wire the custom implementation in the process engine configuration.
Composite History Handling
Note that if you provide a custom implementation of the HistoryEventHandler and wire it to the process engine, you override the default DbHistoryEventHandler. The consequence is that the process engine will stop writing to the history database and you will not be able to use the history service for querying the audit log. If you do not want to replace the default behavior but only provide an additional event handler, you can use the class org.camunda.bpm.engine.impl.history.handler.CompositeHistoryEventHandler
that dispatches events to a collection of handlers.
Spring Boot
Note that providing your custom HistoryEventHandler
in a Spring Boot Starter environment works slightly differently. By default, the Camunda Spring Boot starter uses a CompositeHistoryEventHandler
which wraps a list of HistoryEventHandler implementations that you can provide via the customHistoryEventHandlers
engine configuration property. If you want to override the default DbHistoryEventHandler
, you have to explicitly set the enableDefaultDbHistoryEventHandler
engine configuration property to false
.
Implement a custom history level
To provide a custom history level the interface org.camunda.bpm.engine.impl.history.HistoryLevel
has to be implemented. The custom history level implementation
then has to be added to the process engine configuration, either by configuration or a process engine plugin.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.camunda.bpm.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration" >
<property name="customHistoryLevels">
<list>
<bean class="org.camunda.bpm.example.CustomHistoryLevel" />
</list>
</property>
</bean>
</beans>
The custom history level has to provide a unique id and name for the new history level.
public int getId() {
return 42;
}
public String getName() {
return "custom-history";
}
If the history level is enabled, the method
boolean isHistoryEventProduced(HistoryEventType eventType, Object entity)
is called for every history event to determine if the event should be saved to the history. The event types used in the
engine can be found in org.camunda.bpm.engine.impl.history.event.HistoryEventTypes
(see Javadocs).
The second argument is the entity for which the event is triggered, e.g., a process instance, activity
instance or variable instance. If the entity
is null the engine tests if the history level in general
handles such history events. If the method returns false
, the engine will not generate
any history events of this type again. This means that if your history level only wants to generate the history
event for some instances of an event it must still return true
if entity
is null
.
Please have a look at this complete example to get a better overview.
Removal time inheritance
Historic instances inherit the removal time from the respective historic top-level instance. If the custom history level is configured in a way, so that the historic top-level instance is not written, the removal time is not available.
The following historic instances are considered as top-level instances:
- Batch instance
- Root process instance
- Root decision instance
User operation logs and custom history level
The following implementation is required in order to enable User Operation Logs:
public boolean isHistoryEventProduced(HistoryEventType eventType, Object entity) {
if (eventType.equals(HistoryEventTypes.USER_OPERATION_LOG)){
return true;
}
...
}