Appending an item to a list from a parallel subprocess to a global variable

I have a model with a multi-instance subprocess, whose cardinality is set by a list that an user will populate through a form in a previous user task. This list corresponds to a list of projects. The subprocess is launched for each project, and what I want to do is, in the final user task, set an “approved” or “not approved”, and then push this particular project (which, in any given subprocess instance, will be in a “project” variable) to a “approvedProjects” list, in a global scope.

Model

Ideally, since this is a very basic functionality, I don’t want to write an entire Java class, so I’ve been trying to get this done with scripting. The main problem I’m having is regarding serialization. With a groovy script task, I can fetch both the current project, and the global approvedProjects variables, but how can I, with groovy, add that project to the approvedProjects variable?

When I try this script:

project = execution.getVariable('project');

println project

execution.getVariable('approvedProjects');

println approvedProjects

approvedProjects.add(project);

execution.setVariable('approvedProjects', approvedProjects);

I get this error:

An error happened while submitting the task form : Cannot submit task form bf835848-cd5a-11ed-95ef-7c8ae1e03c0e: Unable to evaluate script while executing activity 'Activity_1p1x1mu' in the process definition with id 'Process_0gy2g6n:1:99ab3b8f-cd5a-11ed-95ef-7c8ae1e03c0e': org.camunda.bpm.engine.ProcessEngineException: Cannot serialize object in variable 'approvedProjects': org.camunda.spin.impl.json.jackson.JacksonJsonNode

So, I’m assuming this should be fairly straightforward, only problem is how can I get around this serialization problem? What is the right way of fetching a list variable from the execution context, adding a value to it, and then set the variable back?

The projects list is created in a user task form, as a json object. The approvedProjects is created in a task’s output as an empty list. The project is pulled from the collection in the multi-instance subprocess, so I assume it’s also a JSON object (since the form creates a JSON list).

Hello my dear!
Welcome to our community :smiley:

This part of serialization / deserialization in Camunda really sometimes seems more complicated than it really is.

I know very little about groovy… but let’s get to the logic itself!
This “approvedProjects” variable, from what I understand, is an array of approved projects… if that’s right…

Try creating a variable and declaring it as an empty array within your script…
With javascript/java inside your script in camunda modeler I would do something like this:

var approvedProjects = new java.util.ArrayList();

then implement your logic to add projects into this array.

If you want to make a Java class for this in your project (which I don’t see the need for) you can’t forget to create an empty constructor, because “Jackson” uses “reflection” to fill in the attributes.

Try to do as I told you, creating an empty array declaring the full path java.util.ArrayList() and let me know please…
I believe it will solve your problem… or part of it at least :smiley:

See ya :rocket:

Regards.
William Robert Alves

Sorry I didn’t provide more context. Here’s the full BPM file.
selecao-pre-incubados.bpmn (24.7 KB)

I’ll have a list of projects defined before the multi-instance subprocess, and each project will go through the subprocess. The approvedProjects should already exist before the multi-instance subprocess so it can be accessed by each subprocess instance. As far as I’ve been able to tell, fetching that variable, or the current project variable, is not the problem. I can even add the current project to the approvedProjects array, and print them out.

I tried this script:

var project = execution.getVariable('project');

var approvedProjects = execution.getVariable('approvedProjects');

console.log(project);
console.log(approvedProjects);

var newApprovedProjects = new java.util.ArrayList();

newApprovedProjects.add(project);
approvedProjects.forEach(p => {
    newApprovedProjects.add(p);
});

console.log(newApprovedProjects);

execution.setVariable('approvedProjects', newApprovedProjects);

And I get the correct logs, but it fails at the end with this error:

2023-03-29 13:16:58.342 ERROR 14936 --- [io-18089-exec-2] org.camunda.bpm.engine.context           : ENGINE-16004 Exception while closing command context: Unable to evaluate script while executing activity 'Activity_1p1x1mu' in the process definition with id 'Process_0gy2g6n:1:d9dba4c5-ce4c-11ed-a57a-7c8ae1e03c0e':org.camunda.bpm.engine.ProcessEngineException: Cannot serialize object in variable 'approvedProjects': org.camunda.spin.impl.json.jackson.JacksonJsonNode

If I take the last line from the script, it executes fine. So the problem is with:

execution.setVariable('approvedProjects', newApprovedProjects);

newApprovedProjects is of type java.util.ArrayList, but it’s being converted to JacksonJsonNode and then being serialized? This is what I’m having trouble trying to understand.

Okay, so I tinkered with it for a little while and found a working solution, and I’ll leave it here for future reference.

In this page from the Spin documentation, they use this script as an example:

...
<scriptTask id="task" name="Script Task" scriptFormat="javascript">
  <script>
    <![CDATA[
    var address = S(customer).prop("address");
    address.prop("city", "New York");
    execution.setVariable("address", address.toString());
    ]]>
  </script>
</scriptTask>
...

So, assuming all those variables in my script were JacksonNode’s, I just used their functions and serialized the list before setting the global variable.