diff --git a/pom.xml b/pom.xml
index 56babee4..43ea2fe7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -245,7 +245,37 @@
org.graalvm.js
js
- 23.0.3
+ 23.0.4
+
+
+
+ org.graalvm.python
+ python-language
+ 23.1.3
+
+
+
+ org.graalvm.python
+ python-resources
+ 23.1.3
+
+
+
+ org.graalvm.ruby
+ ruby-language
+ 23.1.3
+
+
+
+ org.graalvm.ruby
+ ruby-resources
+ 23.1.3
+
+
+
+ org.jruby
+ jruby
+ 9.4.7.0
@@ -254,6 +284,12 @@
23.1.2
+
+ org.graalvm.polyglot
+ polyglot
+ 23.1.3
+
+
org.springframework.boot
spring-boot-starter-actuator
diff --git a/src/main/java/org/folio/rest/camunda/controller/WorkflowController.java b/src/main/java/org/folio/rest/camunda/controller/WorkflowController.java
index b740e6ef..e54d5df3 100644
--- a/src/main/java/org/folio/rest/camunda/controller/WorkflowController.java
+++ b/src/main/java/org/folio/rest/camunda/controller/WorkflowController.java
@@ -1,6 +1,7 @@
package org.folio.rest.camunda.controller;
import lombok.extern.slf4j.Slf4j;
+import org.folio.rest.camunda.exception.ScriptTaskDeserializeCodeFailure;
import org.folio.rest.camunda.exception.WorkflowAlreadyActiveException;
import org.folio.rest.camunda.service.CamundaApiService;
import org.folio.rest.workflow.model.Workflow;
@@ -26,7 +27,7 @@ public WorkflowController(CamundaApiService camundaApiService) {
@PostMapping(value = {"/activate", "/activate/"}, produces = { MediaType.APPLICATION_JSON_VALUE })
public Workflow activateWorkflow(@RequestBody Workflow workflow, @TenantHeader String tenant)
- throws WorkflowAlreadyActiveException {
+ throws WorkflowAlreadyActiveException, ScriptTaskDeserializeCodeFailure {
log.debug("Activating Workflow: {}", workflow == null ? null : workflow.getId());
return camundaApiService.deployWorkflow(workflow, tenant);
}
diff --git a/src/main/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailure.java b/src/main/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailure.java
new file mode 100644
index 00000000..33fe1d24
--- /dev/null
+++ b/src/main/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailure.java
@@ -0,0 +1,17 @@
+package org.folio.rest.camunda.exception;
+
+public class ScriptTaskDeserializeCodeFailure extends Exception {
+
+ private static final long serialVersionUID = -6270663785866339965L;
+
+ private static final String MESSAGE = "Failed to De-serialize code for ScriptTask %s.";
+
+ public ScriptTaskDeserializeCodeFailure(String scriptTaskUuid) {
+ super(String.format(MESSAGE, scriptTaskUuid));
+ }
+
+ public ScriptTaskDeserializeCodeFailure(String scriptTaskUuid, Exception e) {
+ super(String.format(MESSAGE, scriptTaskUuid), e);
+ }
+
+}
diff --git a/src/main/java/org/folio/rest/camunda/service/BpmnModelFactory.java b/src/main/java/org/folio/rest/camunda/service/BpmnModelFactory.java
index 5c749b09..f87ec615 100644
--- a/src/main/java/org/folio/rest/camunda/service/BpmnModelFactory.java
+++ b/src/main/java/org/folio/rest/camunda/service/BpmnModelFactory.java
@@ -23,6 +23,7 @@
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaField;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.folio.rest.camunda.delegate.AbstractWorkflowDelegate;
+import org.folio.rest.camunda.exception.ScriptTaskDeserializeCodeFailure;
import org.folio.rest.workflow.enums.StartEventType;
import org.folio.rest.workflow.model.Condition;
import org.folio.rest.workflow.model.ConnectTo;
@@ -77,7 +78,7 @@ public class BpmnModelFactory {
@Autowired
private List workflowDelegates;
- public BpmnModelInstance fromWorkflow(Workflow workflow) {
+ public BpmnModelInstance fromWorkflow(Workflow workflow) throws ScriptTaskDeserializeCodeFailure {
// @formatter:off
ProcessBuilder processBuilder = Bpmn.createExecutableProcess().name(workflow.getName())
@@ -90,7 +91,13 @@ public BpmnModelInstance fromWorkflow(Workflow workflow) {
// @formatter:off
workflow.getNodes().stream()
.filter(node -> node instanceof EventSubprocess)
- .forEach(subprocess -> eventSubprocess(processBuilder, subprocess));
+ .forEach(subprocess -> {
+ try {
+ eventSubprocess(processBuilder, subprocess);
+ } catch (ScriptTaskDeserializeCodeFailure e) {
+ throw new RuntimeException(e);
+ }
+ });
// @formatter:on
setup(model, workflow);
@@ -98,7 +105,7 @@ public BpmnModelInstance fromWorkflow(Workflow workflow) {
return model;
}
- private BpmnModelInstance build(ProcessBuilder processBuilder, Workflow workflow) {
+ private BpmnModelInstance build(ProcessBuilder processBuilder, Workflow workflow) throws ScriptTaskDeserializeCodeFailure {
List nodes = workflow.getNodes();
if (nodes.isEmpty()) {
@@ -117,7 +124,7 @@ private BpmnModelInstance build(ProcessBuilder processBuilder, Workflow workflow
return builder.done();
}
- private void eventSubprocess(ProcessBuilder processBuilder, Node node) {
+ private void eventSubprocess(ProcessBuilder processBuilder, Node node) throws ScriptTaskDeserializeCodeFailure {
AbstractFlowNodeBuilder, ?> builder = null;
String identifier = node.getIdentifier();
String name = node.getName();
@@ -125,7 +132,7 @@ private void eventSubprocess(ProcessBuilder processBuilder, Node node) {
builder = build(builder, ((EventSubprocess) node).getNodes(), Setup.NONE);
}
- private AbstractFlowNodeBuilder, ?> build(AbstractFlowNodeBuilder, ?> builder, List nodes, Setup setup) {
+ private AbstractFlowNodeBuilder, ?> build(AbstractFlowNodeBuilder, ?> builder, List nodes, Setup setup) throws ScriptTaskDeserializeCodeFailure {
for (Node node : nodes) {
@@ -314,7 +321,6 @@ private void eventSubprocess(ProcessBuilder processBuilder, Node node) {
} else {
logger.warn("Navigation named {} is of an unknown type.", node.getName());
}
-
} else if (node instanceof Task) {
if (node instanceof Wait) {
if (node instanceof ReceiveTask) {
@@ -324,13 +330,19 @@ private void eventSubprocess(ProcessBuilder processBuilder, Node node) {
logger.warn("Wait Task named {} is of an unknown type.", node.getName());
}
} else if (node instanceof ScriptTask) {
+ String code;
+ try {
+ code = objectMapper.readValue(((ScriptTask) node).getCode(), String.class);
+ } catch (JsonProcessingException e) {
+ throw new ScriptTaskDeserializeCodeFailure(node.getId(), e);
+ }
+
builder = builder.scriptTask(node.getIdentifier()).name(node.getName())
- .scriptFormat(((ScriptTask) node).getScriptFormat()).scriptText(((ScriptTask) node).getCode());
+ .scriptFormat(((ScriptTask) node).getScriptFormat()).scriptText(code);
if (((ScriptTask) node).hasResultVariable()) {
builder = ((ScriptTaskBuilder) builder).camundaResultVariable(((ScriptTask) node).getResultVariable());
}
-
} else {
logger.warn("Script Task named {} is of an unknown type.", node.getName());
}
diff --git a/src/main/java/org/folio/rest/camunda/service/CamundaApiService.java b/src/main/java/org/folio/rest/camunda/service/CamundaApiService.java
index 1e52d25b..d10afd4a 100644
--- a/src/main/java/org/folio/rest/camunda/service/CamundaApiService.java
+++ b/src/main/java/org/folio/rest/camunda/service/CamundaApiService.java
@@ -6,6 +6,7 @@
import org.camunda.bpm.engine.repository.Deployment;
import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
+import org.folio.rest.camunda.exception.ScriptTaskDeserializeCodeFailure;
import org.folio.rest.camunda.exception.WorkflowAlreadyActiveException;
import org.folio.rest.workflow.model.Workflow;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +18,7 @@ public class CamundaApiService {
@Autowired
private BpmnModelFactory bpmnModelFactory;
- public Workflow deployWorkflow(Workflow workflow, String tenant) throws WorkflowAlreadyActiveException {
+ public Workflow deployWorkflow(Workflow workflow, String tenant) throws WorkflowAlreadyActiveException, ScriptTaskDeserializeCodeFailure {
if (workflow.isActive()) {
throw new WorkflowAlreadyActiveException(workflow.getId());
}
diff --git a/src/main/java/org/folio/rest/camunda/utility/ScriptEngineUtility.java b/src/main/java/org/folio/rest/camunda/utility/ScriptEngineUtility.java
new file mode 100644
index 00000000..8314d97e
--- /dev/null
+++ b/src/main/java/org/folio/rest/camunda/utility/ScriptEngineUtility.java
@@ -0,0 +1,37 @@
+package org.folio.rest.camunda.utility;
+
+import org.graalvm.shadowed.org.json.JSONObject;
+
+/**
+ * Provide utility functions specifically needed for scripting engines.
+ */
+public class ScriptEngineUtility {
+
+ /**
+ * Decode a JSON string into a JSONObject.
+ *
+ * This is required by several of the scripting engines, such as engine.py.
+ *
+ * @param json
+ * The JSON string to decode.
+ *
+ * @return
+ * A generated JSON object, containing the decoded JSON string.
+ */
+ public JSONObject decodeJson(String json) {
+ return new JSONObject(json);
+ }
+
+ /**
+ * Encode a JSONObject into a JSON string.
+ *
+ * @param json
+ * The JSONObject to encode.
+ *
+ * @return
+ * A String containing the encoded JSON data.
+ */
+ public String encodeJson(JSONObject json) {
+ return json.toString(2);
+ }
+}
diff --git a/src/main/resources/scripts/engine.groovy b/src/main/resources/scripts/engine.groovy
index 54a239a0..2bf87c60 100644
--- a/src/main/resources/scripts/engine.groovy
+++ b/src/main/resources/scripts/engine.groovy
@@ -1,4 +1,4 @@
-import org.folio.rest.utility.ScriptEngineUtility
+import org.folio.rest.camunda.utility.ScriptEngineUtility
def %s(String inArgs) {
def scriptEngineUtility = new ScriptEngineUtility();
diff --git a/src/main/resources/scripts/engine.java b/src/main/resources/scripts/engine.java
index ed64bb91..10485991 100644
--- a/src/main/resources/scripts/engine.java
+++ b/src/main/resources/scripts/engine.java
@@ -1,5 +1,5 @@
-import org.camunda.bpm.engine.impl.util.json.JSONObject;
-import org.folio.rest.utility.ScriptEngineUtility;
+import org.graalvm.shadowed.org.json.JSONObject;
+import org.folio.rest.camunda.utility.ScriptEngineUtility;
public String %s(String inArgs) {
ScriptEngineUtility scriptEngineUtility = new ScriptEngineUtility();
diff --git a/src/main/resources/scripts/engine.pl b/src/main/resources/scripts/engine.pl
index 10572f23..3681e14b 100644
--- a/src/main/resources/scripts/engine.pl
+++ b/src/main/resources/scripts/engine.pl
@@ -1,5 +1,5 @@
use JSON;
-use org.folio.rest.utility.ScriptEngineUtility;
+use org.folio.rest.camunda.utility.ScriptEngineUtility;
sub %s($) {
my ($inArgs) = @_;
diff --git a/src/main/resources/scripts/engine.py b/src/main/resources/scripts/engine.py
index 3aa9359b..e541fefe 100644
--- a/src/main/resources/scripts/engine.py
+++ b/src/main/resources/scripts/engine.py
@@ -1,8 +1,8 @@
import json
-import org.folio.rest.utility.ScriptEngineUtility;
+import org.folio.rest.camunda.utility.ScriptEngineUtility;
def %s(inArgs):
- scriptEngineUtility = org.folio.rest.utility.ScriptEngineUtility();
+ scriptEngineUtility = org.folio.rest.camunda.utility.ScriptEngineUtility();
args = scriptEngineUtility.decodeJson(inArgs);
returnObj = scriptEngineUtility.createJson();
%s
diff --git a/src/main/resources/scripts/engine.rb b/src/main/resources/scripts/engine.rb
index 86380595..4e4c29b1 100644
--- a/src/main/resources/scripts/engine.rb
+++ b/src/main/resources/scripts/engine.rb
@@ -1,5 +1,5 @@
require 'json'
-require 'use org.folio.rest.utility.ScriptEngineUtility'
+require 'use org.folio.rest.camunda.utility.ScriptEngineUtility'
def %s(inArgs)
scriptEngineUtility = ScriptEngineUtility();
diff --git a/src/test/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailureTest.java b/src/test/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailureTest.java
new file mode 100644
index 00000000..007b06d7
--- /dev/null
+++ b/src/test/java/org/folio/rest/camunda/exception/ScriptTaskDeserializeCodeFailureTest.java
@@ -0,0 +1,35 @@
+package org.folio.rest.camunda.exception;
+
+import static org.folio.spring.test.mock.MockMvcConstant.UUID;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ScriptTaskDeserializeCodeFailureTest {
+
+ @Test
+ void scriptEngineLoadFailedWorksTest() throws IOException {
+ ScriptTaskDeserializeCodeFailure exception = Assertions.assertThrows(ScriptTaskDeserializeCodeFailure.class, () -> {
+ throw new ScriptTaskDeserializeCodeFailure(UUID);
+ });
+
+ assertNotNull(exception);
+ assertTrue(exception.getMessage().contains(UUID));
+ }
+
+ @Test
+ void scriptEngineLoadFailedWorksWithParameterTest() throws IOException {
+ ScriptTaskDeserializeCodeFailure exception = Assertions.assertThrows(ScriptTaskDeserializeCodeFailure.class, () -> {
+ throw new ScriptTaskDeserializeCodeFailure(UUID, new RuntimeException());
+ });
+
+ assertNotNull(exception);
+ assertTrue(exception.getMessage().contains(UUID));
+ }
+}