How to use Camunda Spin library in a remote application?

I wonder if I can use the Camunda Spin library for serialization / deserialization of process variables outside the process engine itself. And if so, how to do that.

In our case, the process engine is running as a standalone microservice in a Spring Boot container. The application (ie. other microservices) interact with the process engine using the Camunda REST API. Our architecture follows the ‘Standalone (Remote) Process Engine Server’ approach.

So how do we set and retrieve process variables in a remote application?

Of course we can de-/serialize values with our own (Jackson) ObjectMapper, like as follows (REST client based on Camunda’s OpenApi specification, generated with OpenApi generator):

// generated REST client classes
import org.example.bpm.rest.model.ProcessInstanceWithVariablesDto;
import org.example.bpm.rest.model.StartProcessInstanceDto;
import org.example.bpm.rest.model.VariableValueDto;
import org.example.bpm.rest.service.ProcessDefinitionApi;

import org.example.myservice.rest.model.MyDTO;
import com.fasterxml.jackson.databind.ObjectMapper;
...

@Service
public class MyClass {

  @Autowired ProcessDefinitionApi processDefinitionApi;
  
  private ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
  ...

  public void myService() {
    MyDTO myDto = new MyDTO().field1("abcd").field2(LocalDate.now());

    // TODO can I use Camunda Spin to do this?
    VariableValueDto myDtoVariable =
        new VariableValueDto()
            .putValueInfoItem("objectTypeName", MyDTO.class.getName())
            .putValueInfoItem("serializationDataFormat", "application/json")
            .type("object")
            .value(mapper.writeValueAsString(myDto));

    StartProcessInstanceDto params = new StartProcessInstanceDto()
        .putVariablesItem("someDtoVar", myDtoVariable);

    // REST API call
    ProcessInstanceWithVariablesDto processInstance =
        processDefinitionApi.startProcessInstanceByKey("MyProcess", params);

    // TODO can I use Camunda Spin to do this?
    Map<String, VariableValueDto> variables = processInstance.getVariables();
    VariableValueDto myDtoVariable2 = variables.get("someDtoVar");
    String json = (String) myDtoVariable2.getValue();
    MyDTO myDto2 = mapper.readValue(json, MyDTO.class);
  }
  ...
}

Could this be done with the help of the Spin library?

  • If yes, can you provide me an example?
  • If not, what is the recommended way to set and retrieve process variables in a remote application?

Any help appreciated, thanks in advance!

In the meanwhile I learned a couple of thinks about de-/serialization of process variables, and I’d like to share my insights because I think they might be helpful for others too.

  • Camunda Spin works entirely within the BPM engine. It has no function on the Camunda REST API. In other words: the Jackson ObjectMapper used to serialize / deserialize requests and responses on the REST API is independent from the ObjectMapper that is used by the Spin library.
  • So the first part of the answer to my question is: No :slight_smile: Spin cannot be used outside the BPM engine.
  • Spin is primarily a standardized interface for de-/serialization frameworks like Jackson and others. It is implemented as a engine plugin. It is used for de-/serialization of process variables within the engine at the point where the engine stores and retrieves process variables.
  • The ObjectMapper used by Spin can be configured or even replaced the way you like. How I did this is shown in the example below.
  • As mentioned, Camunda’s REST API doesn’t make use of Spin. Instead, it has its own ObjectMapper used for de-/serialization and - unfortunately - this ObjectMapper instance is hard coded; I found no way to inject my own configured one. The only thing that can be configured is the date format, which I did as shown below (Camunda BPM running as a Spring Boot application):
@Configuration
...
public class BpmServiceConfiguration {
  ...
  @Bean
  public ObjectMapper objectMapper() {
    // JsonNullableModule is crucial for the RestTemplate generated by the OpenApi generator
    return new ObjectMapper().findAndRegisterModules().
        .registerModule(new JsonNullableModule());
  }


  @Bean
  public CommandLineRunner configureProcessEngine(ObjectMapper objectMapper) {
    return args -> {
      // configuration of the ObjectMapper used by Spin
      JacksonJsonDataFormat dataFormat = (JacksonJsonDataFormat) DataFormats.json();
      dataFormat.setObjectMapper(objectMapper);
      ...
    };
  }

  @Bean
  public ServletContextInitializer initializer() {
    return servletContext -> {
      // configuration of the ObjectMapper used by the REST API (only Date format can be configured)
      servletContext.setInitParameter(
          CustomJacksonDateFormatListener.CONTEXT_PARAM_NAME, "yyyy-MM-dd'T'HH:mm:ss");
      servletContext.addListener(CustomJacksonDateFormatListener.class);
    };
  }

Outside the BPM service, i.e. in other services acting as REST clients for the BPM service, I implemented some library functions in order to de-/serialize process variables. Not a big deal, the basic pattern is shown in my initial post.

One last important note: There are two REST APIs to retrieve process variables: ProcessInstanceApi#getProcessInstanceVariables(String, boolean) and TaskVariableApi#getTaskVariables(String, boolean). As a REST client, make sure to call these APIs with ‘deserializeValues=false’ in order to get a result that can be deserialized again. If you’re using the brand new (and highly appreciated) Camunda OpenApi specification, see here for further details to quick fix a bug in the current API (as of 7.13.0)

So, this reflects my current understanding, and I hope that was helpful for one or another. Please tell me if I missed something or if I got things wrong.