Creating JSON object in JAVA client to post into REST API

Hi,
I am writing a java client that makes REST calls to Camunda.

I understand the following code should allow me to create JSON string that is correctly formatted so that it can be posted into the API ( as per the variable format we see here: https://docs.camunda.org/manual/7.4/reference/rest/process-definition/post-start-process-instance/#request ).

My example code is this

package sandbox.camunda;

import org.camunda.bpm.engine.variable.Variables;
import org.camunda.bpm.engine.variable.value.ObjectValue;

class Customer {
  String content;
  Boolean approved;

  Customer() {}

  public String getContent() {
    return content;
  }
  public void setContent(String content) {
    this.content = content;
  }
  public Boolean getApproved() {
    return approved;
  }
  public void setApproved(Boolean approved) {
    this.approved = approved;
  }
  public String toString() {
    return String.format( "This content is %s and approved is %s", content , approved );
  }
}

public class Thing {
  public static void main( String[] args ) {

    Customer cust = new Customer();
    cust.setContent("tweet");
    cust.setApproved( true );

    ObjectValue typedCustValue = Variables
      .objectValue(cust)
      .serializationDataFormat("application/json")
      .create();

    System.out.println( typedCustValue.getObjectType() );
    System.out.println( typedCustValue.toString() );
    System.out.println( "Why is this NULL? " + typedCustValue.getValueSerialized());
  }
}

The output is this

class sandbox.camunda.Customer
ObjectValue [value=This content is tweet and approved is true, isDeserialized=true, serializationDataFormat=application/json, objectTypeName=null, serializedValue=null]
Why is this NULL? null

I was expecting the typedCustValue.getValueSerialized() to return a string of appropriately formatted json that I could directly post into the process-definition/key//start rest API.

Any suggestions would be appreciated.

Thanks
Pete

Hi Pete,

Serialization does not happen when the variable is created but only when you invoke a process engine API method with that variable. The reason for that is that serialization may depend on the process in which context the variable is set which is unknown when the variable is created.

I suggest you use another way to construct JSON objects for invoking the REST API, for example by using Jackson directly.

Cheers,
Thorben

Thanks Thorben, Is there a way I can invoke the serialisation in the same way the process would?

I would prefer to use the existing objects so that code changes are caught at compile time during upgrades.

Thanks
Pete

Hi Pete,

You can use Camunda Spin for that, see https://docs.camunda.org/manual/7.4/reference/spin/ and https://docs.camunda.org/manual/7.4/reference/spin/json/04-mapping-json/. In the end, it also uses Jackson to serialize Java objects as JSON.

Cheers,
Thorben

Another option is to create a custom JAX-RS service and use it as an interface between your external application and Camunda’s engine. This way you can directly pass in your JSON-capable objects and let the JAX-RS framework handle the marshaling. Once the JAX-RS service receives the inflated object it can then make direct calls into the BPM engine via an injected RunTimeService reference.

However, you’re then maintaining your object types (the ones being marshaled through JAX-RS) between projects. This isn’t an issue depending on build (maven) configuration. But, I’ve taken the route of dynamically constructing JSON objects, using Jackson, and directly passing in a String containing the JSON definition. Though this provides increased flexibility… I don’t disagree with your concerns regarding unmanaged changes in type definition. But, I’m also not required the burdensome task of calling all the object-type setters prior to sending it along an HTTP-client (etc.) .

Hi Gary, I have seen a number of threads that have suggested JAX-RS, but I am not quite sure how to go about this, any chance you know of a decent code sample or blog that shows how to go about this?

I was hopeful that given there were java objects (VariableMap etc) supplied by Camunda that they would have the ability to convert into objects that matched the rest interface expectations, but I tried SPIN as Thorben suggested but SPIN converts the object into a JSON object that would be equivalent to what I would expect from Gson or Jackson ( which it uses ) so as you said, I still need to create my own Java objects that include the additional type fields for ObjectValues.

I think the way forward is to build a Java client for Camunda that maintains the methods and objects as part of the library, The only unknown is whether to use JAX-RS somehow or to just make calls using a HTTP client, for the moment I am just using Jersey. I can’t help but think that this must be a common requirement though and that there must be an easier way.

Thanks
Pete

Since you’re using a shared Camunda engine running within Wildfly, you likely have Eclipse managing a project supporting the construction, packaging, and deployment into the WildFly application server.

If you created this Eclipse project using Camunda’s “org.camunda.bpm.archetype” then you already have all necessary Eclipse-project infrastructure ready to support JAX-RS development and deployment alongside your process models (model.bpmn files).

REQUIRED archetype: camunda-archtetype-ejb-war

You also must be using the (making this easy) the Wildfly pre-packaged Camunda distribution. This distribution, from my experience, is ready-and-capable of advanced features (namely CDI).

Basic steps (I don’t… yet have a detailed blog entry on this process):

  1. Create a discrete ReST application for your targeted Wildfly server

package com.acme.bpmapi;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/rest")
public class RestApplication extends Application {

}
  1. Create your ReST Service

  • you’ll likely also see this in an upcoming blog-post)
  • I referenced Camunda’s CDI example
package com.acme.util;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.logging.Logger;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.json.JsonObject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.camunda.bpm.engine.ManagementService;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.repository.ProcessDefinition;


@RequestScoped
@Path("acmebpm")
@Produces({ "application/xml", "application/json","application/text" })
@Consumes({ "application/xml", "application/json","application/text" })
public class AcmeBpmApi {
	
	private final Logger LOGGER = Logger.getLogger(AcmeBpmApi.class.getName());
	
	@Inject
	private ManagementService managementService;  // THIS ISN'T USED - just an example
	
	@Inject
	private RepositoryService repositoryService;
	
	@Inject
	private RuntimeService runtimeService;
	
	
	
	@POST
    @Path("workorder")
	@Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)  
	public JsonObject workOrder(JsonObject jsonObject) {
	
		LOGGER.info("*** invoked");
		
		List<ProcessDefinition> processDefinitions = repositoryService.createProcessDefinitionQuery().latestVersion().list();
		
		// just an example
		processDefinitions
			.forEach(processDefinition->
				System.out.println("Process Name: " + processDefinition.getName() +" - Key: "+processDefinition.getKey()));		
		
		LOGGER.info("*** Testing RuntimeService");
		
		// build and call a BPMN start message event
		runtimeService.createMessageCorrelation("start_test_message") // MATCH THE START MESSAGE NAME in your
																	  //     process
		.setVariable("acmeBpmApiMessage", "acmeBpmApi_message_payload")
		.correlate();
		
		
		return jsonObject;
	}
	
	public AcmeBpmApi() {
	    super();
	}
	
	
	
}