Support for large process variables ( > 4000 chars )

4,716 views
Skip to first unread message

Gareth

unread,
Dec 7, 2015, 5:13:56 AM12/7/15
to camunda BPM users
Hi All,

I'm using Camunda 7.2 almost exclusively through the REST API.  I'm having trouble updating a process variable to a value greater than 4000 chars.  When operating inside a Delegate, I can simply specify a byte array, which allows more than 4000 chars.  Through the REST API, I get the following error when trying to send a byte[]:

REST route: /process-instance/{id}/variables

Variables Passed: vars.put( key, new VariableInstanceValueType( Variables.byteArrayValue( ( activityVariablesObject.toString() ).getBytes() ), byte[].class.getSimpleName() ) );

Error: org.camunda.bpm.engine.rest.exception.RestException: Unsupported value type 'byte[]'

I note the manual states:

value - The variable's value. For variables of type Object, the serialized value has to be submitted as a String value.

So tried the following:

Variables Passed: vars.put( key, new VariableInstanceValueType( Variables.byteArrayValue( ( activityVariablesObject.toString() ).getBytes() ), Object.class.getSimpleName() ) );
 
Error: Caused by: org.camunda.bpm.engine.rest.exception.InvalidRequestException: Must provide 'null' or String value for value of SerializableValue type 'Object'.



Is there a supported method of updating large process variables through the REST API ?


Thanks in advance,

Gareth


thorben....@camunda.com

unread,
Dec 7, 2015, 5:55:38 AM12/7/15
to camunda BPM users
Hi Gareth,

If you use the Java API directly instead of the REST API, you could submit a String as a typed object value. Then the value is written as a byte array in a BLOB field. That is not so easy to do via REST since it expects a serialized object value. For serialization via Java's object serialization mechanism, that would require you to post the String value's serialized byte stream encoded in base64.

A simpler alternative could be to store the value as an object of type JSON. You could try a request with the following body:

{
"modifications": {
    "variableName": {
       "value" : "\"here comes a very long string\"",
       "type": "Object",
       "valueInfo": {"objectTypeName": "java.util.String", "serializationDataFormat": "application/json"}
    }
  }
}

I haven't tried this myself, so it might not work. Also, this requires that you use Spin and its engine integration.

By the way: You can find the names of all variable types here: https://docs.camunda.org/manual/latest/user-guide/process-engine/variables/#supported-variable-values
For the REST API, the type names are these values but in capitalized form (i.e. Bytes instead of bytes).

Cheers,
Thorben

Gareth

unread,
Dec 7, 2015, 7:52:33 AM12/7/15
to camunda BPM users
Thanks for the ideas Thorben,

I tried base64 encoding the "bytes", but still got strange errors:

Caused by: org.codehaus.jackson.JsonParseException: Failed to decode VALUE_STRING as base64 (MIME-NO-LINEFEEDS): Illegal character '{' (code 0x7b) in base64 content at [Source: java.io.StringReader@5b396c3; line: 1, column: 3]


So I tried the JSON String option, with some progress....


The large variable appears to be accepted by the POST, but is returned as null when retrieved through REST route, and the REST interface gives the following exception


"errorMessage":"Cannot deserialize object in variable 'stackTrace': SPIN/JACKSON-JSON-01001 Unable to parse input into json node"

I'll do some digging into SPIN as I've not explicitly used it before, but if anyone can shed any light on this error in the mean time it would be appreciated.

Kind Regards

Gareth

thorben....@camunda.com

unread,
Dec 7, 2015, 8:06:40 AM12/7/15
to camunda BPM users
Hi Gareth,

The type name has to be java.lang.String, not java.util.String, sorry. Also, you might need to escape certain characters in the String value, e.g. quotation marks.

Cheers,
Thorben

thorben....@camunda.com

unread,
Dec 7, 2015, 8:10:32 AM12/7/15
to camunda BPM users
As an alternative, if you want to set the byte[] representation of the String as a variable, you can do that by using the method POST /process-instance/{id}/variables/{name}/data with a multi-part request. See [1] for details.

[1] https://docs.camunda.org/manual/latest/reference/rest/process-instance/variables/post-single-variable-binary/

Gareth

unread,
Dec 7, 2015, 8:43:46 AM12/7/15
to camunda BPM users
Thanks Thorben, I should have spotted that too, easy to overlook when its a String.

I still get the same error though. ( Unable to parse input into json node )

Can you confirm the "value" should just be a quoted String, rather than a formatted JSON String ? ( Ive tried both and get the same error )

Ill try the binary post too ....

Gareth

unread,
Dec 7, 2015, 9:05:12 AM12/7/15
to camunda BPM users
Ok - Getting there.  You have to enforce quotes as part of the String content, now to escape the other quotes and confirm 4K + support ......

Matthias Dietl

unread,
Dec 8, 2015, 6:04:50 AM12/8/15
to camunda BPM users
Hey guys,

I've got a similar problem, but I'm not using REST.

I try to set a larger object as process variable, in this case an object of class org.w3c.dom.Element, which represents a XML object.

This is my approach so far:

execution.setVariable("book", Variables.objectValue(book).serializationDataFormat(Variables.SerializationDataFormats.JAVA).create());

This seems to work, but not if the object is too large (I think >2MB XML size), then I get a stackoverflow error from Camunda when trying to execute the above statement.

What am I doing wrong? Is there a better approach to save such variables?

Thanks,
Matthias

Gareth

unread,
Dec 8, 2015, 6:14:36 AM12/8/15
to camunda BPM users
Hi Matthias,

I have not tried 2mb of Data, but it shouldn't matter, what database are you using ? as the data type will be a BLOB, so its max size may be your limiting factor here.

Have you tried serialising the XML as a byte array ? e.g. 

Variables.byteArrayValue( ( book.toString() ).getBytes() )


( This wont fix the underlying blob size, but is a different approach incase its something else )


Can you provide the full stack trace ?


Kind Regards


Gareth

thorben....@camunda.com

unread,
Dec 8, 2015, 6:42:59 AM12/8/15
to camunda BPM users
I agree with Gareth, the stacktrace would be interesting.

Side note: Be aware that book.toString().getBytes() uses the default platform encoding to encode a String which can lead to nasty surprises when reading it later. You might rather want to use book.toString().getBytes("UTF-8");

Matthias Dietl

unread,
Dec 8, 2015, 7:47:14 AM12/8/15
to camunda BPM users
Thanks for the input so far.

Gareth, your suggestion with the byteArrayValue seems to do the trick, looks promising - thanks for that.

But now I try to save a ArrayList as process variable and run into the same problem. I'm using MSSQL as database. I think I am doing something wrong but don't now what exactly.

This is the line:

execution.setVariable("imagenodes", Variables.objectValue(imagenodes).serializationDataFormat(Variables.SerializationDataFormats.JAVA).create());

The stacktrace is as follows, I cut the biggest part because it consists of the last 4 lines repeated indefinitely:

08-Dec-2015 13:34:15.441 SEVERE [pool-3-thread-1] org.camunda.bpm.engine.impl.interceptor.CommandContext.close Error while closing command context
 org.camunda.bpm.engine.ProcessEngineException: Error while evaluating expression: ${NlmImportService.importImages(execution)}. Cause: java.lang.StackOverflowError
at org.camunda.bpm.engine.impl.el.JuelExpression.getValue(JuelExpression.java:59)
at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskExpressionActivityBehavior.execute(ServiceTaskExpressionActivityBehavior.java:44)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute.execute(PvmAtomicOperationActivityExecute.java:42)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute.execute(PvmAtomicOperationActivityExecute.java:27)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:494)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:484)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:464)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(PvmAtomicOperationTransitionNotifyListenerStart.java:56)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(PvmAtomicOperationTransitionNotifyListenerStart.java:26)
at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:65)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:494)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:473)
at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:58)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:494)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:473)
at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:58)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:494)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:484)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:464)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionCreateScope.scopeCreated(PvmAtomicOperationTransitionCreateScope.java:34)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationCreateScope.execute(PvmAtomicOperationCreateScope.java:50)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationCreateScope.execute(PvmAtomicOperationCreateScope.java:24)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:494)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:484)
at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:464)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.AbstractPvmAtomicOperationTransitionNotifyListenerTake.eventNotificationsCompleted(AbstractPvmAtomicOperationTransitionNotifyListenerTake.java:37)
at org.camunda.bpm.engine.impl.pvm.runtime.operation.AbstractPvmAtomicOperationTransitionNotifyListenerTake.eventNotificationsCompleted(AbstractPvmAtomicOperationTransitionNotifyListenerTake.java:28)
at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:65)
at org.camunda.bpm.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:134)
at org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler.execute(AsyncContinuationJobHandler.java:90)
at org.camunda.bpm.engine.impl.persistence.entity.JobEntity.execute(JobEntity.java:123)
at org.camunda.bpm.engine.impl.cmd.ExecuteJobsCmd.execute(ExecuteJobsCmd.java:100)
at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:24)
at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:97)
at org.camunda.bpm.engine.spring.SpringTransactionInterceptor$1.doInTransaction(SpringTransactionInterceptor.java:42)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:40)
at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:32)
at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.executeJob(ExecuteJobsRunnable.java:79)
at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.run(ExecuteJobsRunnable.java:67)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.camunda.bpm.engine.impl.javax.el.ELException: java.lang.StackOverflowError
at org.camunda.bpm.engine.impl.javax.el.BeanELResolver.invoke(BeanELResolver.java:481)
at org.camunda.bpm.engine.impl.javax.el.CompositeELResolver.invoke(CompositeELResolver.java:397)
at org.camunda.bpm.engine.impl.juel.AstMethod.invoke(AstMethod.java:91)
at org.camunda.bpm.engine.impl.juel.AstMethod.eval(AstMethod.java:75)
at org.camunda.bpm.engine.impl.juel.AstEval.eval(AstEval.java:50)
at org.camunda.bpm.engine.impl.juel.AstNode.getValue(AstNode.java:26)
at org.camunda.bpm.engine.impl.juel.TreeValueExpression.getValue(TreeValueExpression.java:114)
at org.camunda.bpm.engine.impl.delegate.ExpressionGetInvocation.invoke(ExpressionGetInvocation.java:33)
at org.camunda.bpm.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:39)
at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:59)
at org.camunda.bpm.engine.impl.el.JuelExpression.getValue(JuelExpression.java:50)
... 47 more
Caused by: java.lang.StackOverflowError
at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.getRealPrevSibling(DeferredDocumentImpl.java:886)
at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.getRealPrevSibling(DeferredDocumentImpl.java:877)
at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.getNodeValueString(DeferredDocumentImpl.java:1157)
at com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl.getNodeValueString(DeferredDocumentImpl.java:1134)
at com.sun.org.apache.xerces.internal.dom.DeferredTextImpl.synchronizeData(DeferredTextImpl.java:96)
at com.sun.org.apache.xerces.internal.dom.NodeImpl.writeObject(NodeImpl.java:2017)
at sun.reflect.GeneratedMethodAccessor495.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)


thorben....@camunda.com

unread,
Dec 8, 2015, 7:51:32 AM12/8/15
to camunda BPM users
Hi Matthias,

Do the elements in the ArrayList have a circular reference of objects? Also, are you able to provide a test case for that behavior?

Cheers,
Thorben

Gareth

unread,
Dec 8, 2015, 9:49:13 AM12/8/15
to camunda BPM users
Are you sure that the error message is caused by the setVariable method ?

The exception looks like its trying to evaluate an expression and failing:

Error while evaluating expression: ${NlmImportService.importImages(execution)}

As Thorben points out - Stack overflows are typically caused by circular references.  Are you maybe making an accidental recursive call by passing 'execution' into an expression variable ?

Matthias Dietl

unread,
Dec 8, 2015, 11:13:00 AM12/8/15
to camunda BPM users
Hey guys,

thank you very much for your input.

I think I narrowed it down a bit: the exception only occurs when the arraylist contains at least one org.w3c.dom.Node object. When the arraylist is empty, everything is fine.

The code essentially looks like this:

Element book = DocumentBuilderFactory
       
.newInstance()
       
.newDocumentBuilder()
       
.parse(new InputSource(new ByteArrayInputStream(bytes))).getDocumentElement();


ArrayList<Node> imagenodes = new ArrayList<>();
final NodeList images = book.getElementsByTagName("graphic");


for (int i = 0; i < images.getLength(); i++) {
 imagenodes
.add(images.item(i));
}


execution
.setVariable("imagenodes", Variables.objectValue(imagenodes).serializationDataFormat(Variables.SerializationDataFormats.JAVA).create());

Is this something you guys could reproduce? bytes is just a byte array from a xml with <graphic>-Tags.

thorben....@camunda.com

unread,
Dec 8, 2015, 11:26:03 AM12/8/15
to camunda BPM users
Hi Matthias,

Apparently the object graph is too deep to serialize (see https://stackoverflow.com/questions/25147565/serializing-java-object-without-stackoverflowerror).

Note that Camunda has a feature to work with XML and persist it in a process variable that might save you the need to parse it yourself and store the heavy dom elements. See [1] and [2].

Cheers,
Thorben

[1] https://docs.camunda.org/manual/latest/user-guide/data-formats/
[2] https://docs.camunda.org/manual/latest/user-guide/data-formats/xml/#native-xml-variable-value

Matthias Dietl

unread,
Dec 11, 2015, 5:32:14 AM12/11/15
to camunda BPM users
Hi Thorben,

thanks for the advice, I refactored my code to use the built in Spin XML functions, everything looks good now.

Greetings
Matthias

mp4...@att.com

unread,
Dec 13, 2015, 5:36:03 PM12/13/15
to camunda BPM users
I think the issue here is the persistence database. I don't recall the precise context in which I had a similar "4000" character limit, but it was because the underlying history database that support (I assume) displaying the contents of variables in Cockpit has a limit of 4000 characters on one column.

When I trimmed by output to be below 4000 characters, the errors went away.

In my case, it was output from running a command on a remote server. I handled it like this:

execution.setVariable("stdOut", stdoutString.substring(0, ( stdoutString.length()>3999?3999:stdoutString.length() ))) ;

The alternative would be to modify the column size of the Camunda persistence database wherever it was (H2, MySQL, etc.).

If you're using H2 on your local workstation, then you can get to the Query tool at localhost:8080/H2/H2

Andy

unread,
Dec 18, 2015, 2:26:16 PM12/18/15
to camunda BPM users, mp4...@att.com

You are right. History table fields is set 4000 VARCHAR. 
As we have more bigger data to be stored, we are experimenting to increase field length to 6000 or 8000.

PEOPLES, MICHAEL P

unread,
Dec 21, 2015, 8:49:21 AM12/21/15
to Andy, camunda BPM users

I see no reason why this couldn’t be increased or the data type changed, but our Camunda friends would need to weigh in on this.  I, in particular, would need much larger column sizes because I will be capturing unstructured output that I want to preserve.

 

However, I hesitate to modify anything in that database for fear of unintended consequences.

 

Michael

Reply all
Reply to author
Forward
0 new messages