camunda BPM engine: use custom VariableType to resist the urge to flush

Introduction

I hope all of you are aware of the fact that you can provide a ProcessEnginewith your own VariableTypes. If not, I'll give you a short introduction. Please note that my descriptions are based on camunda-engine 7.1.0. There will be some changes in versoin 7.2.0 and I am not sure if my observations will still be true.

VariableType

VariableTypes help the ProcessEngine store your process variables in the table ACT_RU_VARIABLE. I would call them a mediator between the possible variables and the database schema. There are VariableType implementations for

  • Boolean
  • Serizable
  • Date
  • Double
  • Integer
  • JPA Entities
  • Long
  • Null
  • Short
  • String
  • and CustomObjects (about which I'll talk later)

If you try to add an object as process variable, which doesn't belong to one of those types, you'll see this exception:

org.camunda.bpm.engine.ProcessEngineException: couldn't find a variable type that is able to serialize \<object\>
    at org.camunda.bpm.engine.impl.variable.DefaultVariableTypes.findVariableType(DefaultVariableTypes.java:62)
    at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.getNewVariableType(VariableScopeImpl.java:315)
    at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.createVariableInstance(VariableScopeImpl.java:395)
    at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.createVariableLocal(VariableScopeImpl.java:332)
    at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:259)
    at org.camunda.bpm.engine.impl.persistence.entity.VariableScopeImpl.setVariable(VariableScopeImpl.java:242)
    at de.blogspot.wrongtracks.StoreDataDelegate.execute(StoreDataDelegate.java:9)
    at org.camunda.bpm.engine.impl.delegate.JavaDelegateInvocation.invoke(JavaDelegateInvocation.java:34)
    at org.camunda.bpm.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:39)
    at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:42)
    at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskJavaDelegateActivityBehavior.execute(ServiceTaskJavaDelegateActivityBehavior.java:49)

Provide your variable type

Every ProcessEngineConfiguration should have the methods setCustomPostVariableTypes(List<VariableType>) and setCustomPreVariableTypes(List<VariableType>) so you can add your variable types when configuring the engine.
But wait, why are there two methods, pre and post?
When searching which VariableType can handle the object you want to store as process variable the engine iterates over the list of VariableTypes and the first one, which can handle the object, wins. Maybe you want your own types to have precedence over the default types.

Flushing

Now that you know about VariableTypes I want to present to you my use case.

The case

Imagine a process that's supposed to run synchronously (i.e. without a wait state) within a JTA transaction and every task needs a result from the preceding one. Additionally, the results are JPA Entities. By default the JPAEntityVariableType would take care of the entity.
The implementation shows that every time setValue() is called the JPAEntityVariableType calls flush() on the EntityManager. Since the process runs synchronously within a transaction the flush results in unnecessary queries on my database during process execution.

The solution

Here comes the CustomObjectType class. The CustomObjectType only needs a name and a class to work. The class is used to determine if it can handle a certain object. The CustomObjectType stores all objects in the cache of the ValueField. To get rid of the flush I instantiated a CustomObjectType with the class of my result and passed it to the configuration. Now, every time I put an entity inside the process variables the CustomObjectType places them inside the cache and no flush is called.

The downside

Well, nothing comes without a price: If I should ever need a wait state my solution won't work and I'll have to find another solution or live with the flush.

Alternatives

I am not sure if my solution is the best way to solve my problem. If anyone knows a better way please let me know.

Small example

I also created a small example to show the use of the CustomObjectType here on GitHub

Did you find this article valuable?

Support Ronny Bräunlich by becoming a sponsor. Any amount is appreciated!