Facing class loader issues when deploying multiple springboot wars with shared camunda engine on tomcat server

We have a process engine shared across multiple applications. The setup is running on aws with tomcat.

Previously for our product we had only one war (war1) which was running fine along with other application’s wars on the server. Now due to some business requirement, we have deployed another separate war(war2) similar to the previous one.

Now if the sequence of deployments is such that my war2 is the latest deployed one, the flows in both my wars are working fine.

But if my war1 is the latest deployment, my workflows in war2 start getting class-loading issues as below:

org.camunda.bpm.engine.ProcessEngineException: ENGINE-09008 Exception while instantiating class ‘full class name’: ENGINE-09017 Cannot load class ‘full class name’: full class name
at org.camunda.bpm.engine.impl.util.EngineUtilLogger.exceptionWhileInstantiatingClass(EngineUtilLogger.java:88)
at org.camunda.bpm.engine.impl.util.ClassDelegateUtil.instantiateDelegate(ClassDelegateUtil.java:54)
at org.camunda.bpm.engine.impl.bpmn.behavior.ClassDelegateActivityBehavior.getActivityBehaviorInstance(ClassDelegateActivityBehavior.java:112)
at org.camunda.bpm.engine.impl.bpmn.behavior.ClassDelegateActivityBehavior$1.call(ClassDelegateActivityBehavior.java:68)
at org.camunda.bpm.engine.impl.bpmn.behavior.ClassDelegateActivityBehavior$1.call(ClassDelegateActivityBehavior.java:65)

This usually ends up being a classloader boundary problem, not a BPMN problem.

If both WARs share one Camunda engine on Tomcat, the engine resolves Java delegate or listener classes through the process application classloader that is active for that deployment. When WAR1 is the last deployment, WAR2 delegates are often no longer visible the way you expect, so you get ENGINE-09017 Cannot load class for classes that exist inside WAR2.

A few things I would check first:

  1. Make sure each deployment is registered as its own process application, not just two WARs talking to one shared engine.
  2. If the same delegate classes need to be visible to both apps, move those shared classes into a parent/common classloader location like tomcat/lib, instead of packaging them separately inside each WAR.
  3. If the delegates are app-specific, avoid a single shared-engine plus shared-container setup for them. Either isolate the engines per app, or keep the execution path inside the same process application that owns the classes.
  4. If you are using camunda:class, try switching to delegateExpression with Spring beans that live inside the owning app. That usually makes class ownership much clearer.

The key signal in your case is the deployment order dependency. If the problem changes based on which WAR was deployed last, it is almost always classloading and process application resolution.

If you can share whether you are using camunda:class vs delegateExpression, and how the engine is bootstrapped in Tomcat, it will be easier to narrow down the cleanest fix.

HI Taylor,

Sorry for the late response. I have used both in my workflow and in my runtime environment it intermittently fails either to resolve the delegate or to load a java class.
I am not very sure about how the engine is bootstrapped in tomcat as it is bundled with ciena product tool that we are using. If you could be more specific about any settings/configuration file that you need to see, i would be able to get that.

All i know is that camunda is bundled with the docker container and has one bpm-platform.xml file for all the configurations on tomcat.

Hello!

Your class‑loading errors come from Tomcat treating the last deployed WAR’s Camunda engine as dominant, so the other WAR can’t find its delegate classes. The clean fix is to avoid running multiple embedded engines: either deploy a single shared Camunda engine (in Tomcat’s lib or as its own WAR) and let both apps register processes against it, or isolate each WAR with its own engine and ensure all BPMN delegates are packaged inside. If you keep the current setup, you’ll keep hitting “last deployed wins” conflicts, so restructuring the deployment is the most reliable solution.