ClassNotFound in task listener when CMMN is deployed through REST API

I used the REST API to deploy new CMMN on the fly. After it is deployed, the new case instance can be created successfully, however, the task creation always failed with the ClassNotFoundException as shown below. It seems not able to locate the task listener java class defined in the custom process application. If I restart the Camunda server, it will work though. Could you please shed some light on this?

We have a shared process engine set up running in IBM WebSphere. Our own process application is deployed as a WAR which packages a bunch of task listener java classes, some built-in CMMN/BPMN diagrams and processes.xml.

2017-11-21 15:38:48,058 496204 [WebContainer : 0] ERROR org.camunda.bpm.engine.context - ENGINE-16004 Exception while closing command context: ENGINE-03051 There was an exception while invoking the TaskListener. Message: ‘ENGINE-09008 Exception while instantiating class ‘ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’: ENGINE-09017 Cannot load class ‘ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’: ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’
org.camunda.bpm.engine.ProcessEngineException: ENGINE-03051 There was an exception while invoking the TaskListener. Message: ‘ENGINE-09008 Exception while instantiating class ‘ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’: ENGINE-09017 Cannot load class ‘ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’: ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’
at org.camunda.bpm.engine.impl.db.EnginePersistenceLogger.invokeTaskListenerException(EnginePersistenceLogger.java:432) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.persistence.entity.TaskEntity.fireEvent(TaskEntity.java:939) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.persistence.entity.TaskEntity.setAssignee(TaskEntity.java:840) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.task.TaskDecorator.initializeTaskAssignee(TaskDecorator.java:154) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.task.TaskDecorator.initializeTaskAssignments(TaskDecorator.java:144) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.task.TaskDecorator.decorate(TaskDecorator.java:59) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.cmmn.execution.CmmnExecution.createTask(CmmnExecution.java:171) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.cmmn.entity.runtime.CaseExecutionEntity.createTask(CaseExecutionEntity.java:340) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.cmmn.entity.runtime.CaseExecutionEntity.createTask(CaseExecutionEntity.java:83) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.cmmn.behavior.HumanTaskActivityBehavior.performStart(HumanTaskActivityBehavior.java:32) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.cmmn.behavior.StageOrTaskActivityBehavior.started(StageOrTaskActivityBehavior.java:89) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]

Caused by: org.camunda.bpm.engine.ClassLoadingException: ENGINE-09017 Cannot load class ‘ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener’: ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener
at org.camunda.bpm.engine.impl.util.EngineUtilLogger.classLoadingException(EngineUtilLogger.java:135) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:107) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
at org.camunda.bpm.engine.impl.util.ClassDelegateUtil.instantiateDelegate(ClassDelegateUtil.java:42) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
… 128 common frames omitted
Caused by: java.lang.ClassNotFoundException: ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener
at java.lang.Class.forNameImpl(Native Method) ~[na:1.7.0]
at java.lang.Class.forName(Class.java:176) ~[na:1.7.0]
at org.camunda.bpm.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:84) ~[camunda-engine-7.6.1-ee.jar:7.6.1-ee]
… 129 common frames omitted

Same explanation as here: Timer and Spring beans. I guess it works after restart because your process application contains cases with the same key and they are resumed during application redeployment, so the registration is performed automatically in this case (see https://docs.camunda.org/manual/7.7/user-guide/process-applications/the-processes-xml-deployment-descriptor/#process-application-deployment for details).

Cheers,
Thorben

Thorben, it works if I just update the existing CMMN/BPMN (with same key - process definition id), it doesn’t require server restart. But if I deploy a new CMMN/BPMN (with a new key), I run into the ClassNotFound error for the task listener - which exists in our process application, but everything else looks fine, it seems in kind of partially deployed state.

For the task listener, I didn’t use Spring bean, just put the java class full name, e.g., “ca.on.gov.cac.ecor.workflow.listener.TaskAssignNotifyListener” in the diagram. And I used the REST API “/deployment/create” to deploy new diagram.

As you pointed out, the method ManagementService#registerProcessApplication is not exposed via REST API. So how can I work around it? We have demand from clients to deploy new process without restarting the server.

Thanks again!

Hi @ginger,

I checked the code once more and my first explanation is not correct. As you have observed, resolving resources from an application (does not matter if it is Java classes or Spring beans) can be controlled by the mentioned ManagementService API. If there is no such registration, the most recent registration for a process or case definition with the same key will be used (for example: let’s say we have a case definition with key foo in version 1 that belongs to a registered deployment. Now we deploy a new version 2 via REST API. The engine then uses the registration for version 1 when resolving resources). The engine cannot make this connection if the definitions do not have the same keys.

In terms of getting your use case to work, you could consider extending the deployment REST API to make the registration for you. https://docs.camunda.org/manual/7.7/reference/rest/overview/embeddability/ gives some details on extending the REST API and the deployment logic is implemented in the class DeploymentRestServiceImpl. How to obtain a ProcessApplicationReference that you need for registration is roughly outlined here: BPMN files in Process application war file.

Cheers,
Thorben

1 Like

It works. Thorben, thank you very much for your help!

I am just wondering why this can not be implemented in the core rest api (deployment/create). Isn’t it a common scenario? Is this because process engine doesn’t have the application reference and not able to do it?

Thanks for the contribution.

I made a repository with a custom rest api overwriting DeploymentRestServiceImp. Besides it was necessary register all deployments to the application in a @PostDeploy function in the ProcessApplication.