Uploaded image for project: 'camunda BPM'
  1. camunda BPM
  2. CAM-13531

ClassCastException when using Spring Developer Tools and Spin

XMLWordPrintable

    • Icon: Bug Report Bug Report
    • Resolution: Unresolved
    • Icon: L3 - Default L3 - Default
    • None
    • None
    • spin
    • None

      Environment (Required on creation):

      • Spring Boot Starter
      • Spin

      Description (Required on creation; please attach any relevant screenshots, stacktraces, log files, etc. to the ticket):

      Spring offers Developer Tools [1] that can reload resources during development. This is implemented by an additional classloader (`RestartClassLoader`) which can result in problems with object deserialization.

      Steps to reproduce (Required on creation):

      1. Setup a process with asynchronous continuation on service task that fetches a Java Object and deserializes it via the spin library. See below or check the attached applicationĀ  example-classloading-issue.zip
        • Java Object (doesn't implement Serializable)
          public class MyPojo {
            protected String bar;
            public String getBar() {
              return bar;
            }
          }
          
        • Java delegate
          @Component
          public class Service implements JavaDelegate {
            public void execute(DelegateExecution delegateExecution) {
              MyPojo myPojo = (MyPojo) delegateExecution.getVariable("foo");
            }
          }
          
      2. Add the process to Spring Boot Starter app
      3. Add `spring-boot-devtools` library to the pom.xml
      4. Start the application via IDE (which will start it with enabled Spring Dev Tools)
      5. Start a process instance

      Observed Behavior (Required on creation):

      When the Job executor executes the service task, the job fails with

      java.lang.ClassCastException: com.example.workflow.MyPojo cannot be cast to com.example.workflow.MyPojo
      	at com.example.workflow.ServiceB.execute(ServiceB.java:28) ~[classes/:na]
      	at org.camunda.bpm.engine.impl.bpmn.delegate.JavaDelegateInvocation.invoke(JavaDelegateInvocation.java:40) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:58) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocationInContext(DefaultDelegateInterceptor.java:92) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:63) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskDelegateExpressionActivityBehavior$3.call(ServiceTaskDelegateExpressionActivityBehavior.java:118) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskDelegateExpressionActivityBehavior$3.call(ServiceTaskDelegateExpressionActivityBehavior.java:102) ~[camunda-engine-7.15.0.jar:7.15.0]
      ...
      	at org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler.execute(AsyncContinuationJobHandler.java:81) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler.execute(AsyncContinuationJobHandler.java:40) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.persistence.entity.JobEntity.execute(JobEntity.java:134) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.cmd.ExecuteJobsCmd.execute(ExecuteJobsCmd.java:110) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.cmd.ExecuteJobsCmd.execute(ExecuteJobsCmd.java:43) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:28) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:110) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:72) ~[camunda-engine-spring-7.15.0.jar:7.15.0]
      	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-5.3.4.jar:5.3.4]
      	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:70) ~[camunda-engine-spring-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.interceptor.CommandCounterInterceptor.execute(CommandCounterInterceptor.java:35) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobHelper.executeJob(ExecuteJobHelper.java:57) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.executeJob(ExecuteJobsRunnable.java:110) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.run(ExecuteJobsRunnable.java:71) ~[camunda-engine-7.15.0.jar:7.15.0]
      	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
      	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
      

      Expected behavior (Required on creation):

      The job is successfully executed.

      Root Cause (Required on prioritization):

      • The Java class MyPojo is loaded with the `RestartClassLoader` as the beans are loaded via the applicationContextClassloader which is restart classloader when the dev tools are enabled to reload the classes if necessary
      • The variable is deserialized and loaded with `SpinObjectValueSerializer#deserializeFromByteArray` that uses the classloader of the current thread `sun.misc.Launcher` (parent of the RestartClassLoader). For details have a look at `DomXmlDataFormatMapper#mapInternalToJava` -> `SpinReflectUtil.loadClass`.

      Solution Ideas (Optional):

      • Try to use the process engine configuration classloader instead of the current thread's classloader
      • ...

      Hints (Optional):

      • Similar issue of a classloading problem when dev tools are enabled - CAM-9043

      [1] https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using.devtools

        This is the controller panel for Smart Panels app

              Unassigned Unassigned
              yana.vasileva Yana Vasileva
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated: