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.
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.
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.
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):
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 {
}
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();
}
}