Using the RuntimeService in the CDI event bridge

Hello,

I am using the CDI event bridge. Upon receiving an event I want to to get the ProcessInstance of the process that was responsible for the event. I am looking at the case where a new process is created. In this case a couple of events are fired (START_ACTIVITY, TAKE, etc.) depending on what happens at the start of the process.

My problem is, that I am unable to retrieve the process instance in any of the events. The variable singleResult in the example below is always null when the process is new. (If the process is already there, then I can retreive it.)

@Stateless
public class BpmCDIEventBridgeTest {
	private static Logger logger = LoggerFactory.getLogger(BpmCDIEventBridgeTest.class);
	
	public void onProcessEvent(@Observes BusinessProcessEvent event) {
		logger.info("BPM-Event received: {}", event);
		RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService();
		ProcessInstance singleResult = runtimeService.createProcessInstanceQuery()
				.processInstanceId(event.getProcessInstanceId())
				.singleResult();
		logger.info("ProcessInstance: {}", singleResult);
	}
}

If I understand this correctly, the events are sent on the execution thread of the process engine, so I should be in the same transaction on the container. Am I doing something wrong, or why can’t I find the ProcessInstance?

Thanks for your attention. Any help is very welcome!

Cheers,
Christoph

Hi Christoph,

The process instance is not yet inserted into the database at the point where you make the query. That is because the engine caches all inserts/updates/deletes until the current process engine command finishes to reduce the time that write locks are held. In consequence, new entities are not returned by range queries in the same transaction. That is a limitation in the process engine.

What do you need the process instance for at that point? Perhaps we can find another way of doing that.

Cheers,
Thorben

Hi Thorben,

thanks for your answer. I suspected as much, so it is nice to get a confirmation.

What I want to do is send a message to the desktop client applications (using JMS) that a new process has been spawned, so that they can refresh the display. This is necessary, since users have a view of the processes they have started with some state information.

Cheers,
Christoph

What do you want this message to be like?

Should it be sent when the process instance is persistent in the database, so that refreshing is guaranteed to show the new process instance?
Should it contain any kind of data (e.g. process instance id, process definition id), or is it just a plain notification?

Cheers,
Thorben

Hi Thorben,

idealy I would like to receive a notification when the data has been persisted, so that a refresh is guaranteed to get the current data. The notification does not need to be inside of the transaction or even on the same thread as the engine. The message should include the event type, the process instance id or the task id (depending on the event type).

The event types could divert from the ones in CDI. I could also work with the JPA LifeCycleEvents PostPersist, PostUpdate and PostRemove.

Does the engine use JPA internally? Maybe I could hook into the JPA lifecycle events?

Cheers,
Christoph

Hi Christoph,

The engine does not use JPA. However, there is a (non-API) concept of transaction listeners that you could use. Have a look at this discussion that revolves around a similar problem: https://groups.google.com/d/msg/camunda-bpm-users/--fRxgG6JI8/u74You2g6zkJ

Cheers,
Thorben

Hi Thorben,

thanks for the info! I was playing with this approach today.

  1. Listening for TransactionState.COMMITTING

    TransactionListener transactionListener = commandContext -> {
    logger.debug(“TransactionState.COMMITTING”);
    BpmMessage message = createMessage(eventType, event);
    if (message != null) {
    sendJmsMessage(eventType, message, getTopic(event));
    }
    };
    Context.getCommandContext().getTransactionContext().addTransactionListener(TransactionState.COMMITTING, transactionListener);

The nice thing about using a TransactionListener on COMMITTING is, that I am still inside of the transaction, so I can just call other EJB methods without having to worry about that stuff.

This works well for tasks: a newly created task has been persisted at this point and I can find it. So this solves half of my problem. Unfortunately it doesn’t work for processes. Is this by design or is the notification sent a bit to early?

  1. Listening for TransactionState.COMMITTED

Using this variation I get an exception because “The transaction is not active!”.

Caused by: de.emsw.gosa.bpm.engine.BpmException: com.arjuna.ats.jta.exceptions.InactiveTransactionException: ARJUNA016102: The transaction is not active! Uid is 0:ffffc0a8a897:64cc545d:578dffa8:198
	at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.setRollbackOnly(TransactionImple.java:306)
	at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.setRollbackOnly(BaseTransaction.java:159)
	at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.setRollbackOnly(BaseTransactionManagerDelegate.java:123)
	at org.camunda.bpm.engine.impl.interceptor.JtaTransactionInterceptor.doRollback(JtaTransactionInterceptor.java:141)
	at org.camunda.bpm.engine.impl.interceptor.JtaTransactionInterceptor.execute(JtaTransactionInterceptor.java:60)
	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:66)
	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:30)
	at org.camunda.bpm.engine.impl.AbstractQuery.singleResult(AbstractQuery.java:130)
	at de.emsw.gosa.bpm.engine.BpmUtilBean.getProcess(BpmUtilBean.java:40)

If I use the TransactionManager to create my own transaction inside of the listener like this

TransactionListener transactionListener = commandContext -> {
	logger.debug("TransactionState.COMMITTED");
	try {
		transactionManager.begin();
		try {
			BpmMessage message = createMessage(eventType, event);
			if (message != null) {
				sendJmsMessage(eventType, message, getTopic(event));
			}
		} finally {
			transactionManager.commit();
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
};
Context.getCommandContext().getTransactionContext().addTransactionListener(TransactionState.COMMITTED, transactionListener);

I get an exception that the “thread is already associated with a transaction”:

12:30:41,790 ERROR [stderr] (EJB default - 3) javax.transaction.NotSupportedException: BaseTransaction.checkTransactionState - ARJUNA016051: thread is already associated with a transaction!
12:30:41,790 ERROR [stderr] (EJB default - 3) 	at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.begin(BaseTransaction.java:72)
12:30:41,790 ERROR [stderr] (EJB default - 3) 	at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.begin(BaseTransactionManagerDelegate.java:65)
12:30:41,790 ERROR [stderr] (EJB default - 3) 	at de.emsw.gosa.bpm.message.BpmMessageBean.lambda$0(BpmMessageBean.java:90)
12:30:41,790 ERROR [stderr] (EJB default - 3) 	at org.camunda.bpm.engine.impl.cfg.jta.JtaTransactionContext$TransactionStateSynchronization.afterCompletion(JtaTransactionContext.java:104)

I’m not sure how to continue here. I realize that this is probably out of the scope of the process engine and more of a JavaEE issue, but if you have any further ideas I would be glad to hear them.

Smile,
Christoph

Hi @c.keimel,

The COMMITTING event is triggered after the process engine’s database statements have been flushed and before the transaction is committed. So that should behave for task and process instances in the same way. Depending on the transaction isolation level, I would expected both to become visible or not after the flush.

Regarding your problem when you attach it to COMMITTED: I’m afraid that I am not a JavaEE expert either, so I have no idea off the top of my head :frowning: . In a JTA environment, transaction listeners are registered as instances of javax.transaction.Synchronization with the current transaction, see https://github.com/camunda/camunda-bpm-platform/blob/7.5.0/engine/src/main/java/org/camunda/bpm/engine/impl/cfg/jta/JtaTransactionContext.java#L81-L108. Perhaps that helps with understanding the behavior.

Cheers,
Thorben

Hi @thorben,

thanks for the quick reply. I will create a small example using COMMITTING to reproduce this so we can find out if it’s a bug or some other issue on my side.

Cheers,
Christoph