This is an automated email from the ASF dual-hosted git repository.
pvillard pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new c43ee449e22 NIFI-15840 Added rollback in ExecuteGroovyScript to allow
script access to FlowFile. (#11155)
c43ee449e22 is described below
commit c43ee449e22406f9ca6a53e565d4ef8271bc99aa
Author: dan-s1 <[email protected]>
AuthorDate: Thu Apr 16 17:08:31 2026 -0400
NIFI-15840 Added rollback in ExecuteGroovyScript to allow script access to
FlowFile. (#11155)
---
.../processors/groovyx/ExecuteGroovyScript.java | 14 ++++--
.../groovyx/ExecuteGroovyScriptTest.java | 50 +++++++++++++++++++++-
2 files changed, 59 insertions(+), 5 deletions(-)
diff --git
a/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
b/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
index 489f4d62e37..300494152ab 100644
---
a/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
+++
b/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
@@ -450,10 +450,11 @@ public class ExecuteGroovyScript extends
AbstractProcessor {
//create wrapped session to control list of newly created and files
got from this session.
//so transfer original input to failure will be possible
GroovyProcessSessionWrap session = new
GroovyProcessSessionWrap(processSession, toFailureOnError);
+ FlowFile flowFile = null;
if (toFailureOnError) {
// FlowFile must be read otherwise if there is a failure before
the script is executed, a
// never ending loop occurs since the GroovyProcessSessionWrap has
nothing to send to the failure relationship.
- FlowFile flowFile = session.get();
+ flowFile = session.get();
if (flowFile == null) {
return;
}
@@ -466,7 +467,7 @@ public class ExecuteGroovyScript extends AbstractProcessor {
try {
Script script = getGroovyScript(); //compilation must be moved to
validation
- Map bindings = script.getBinding().getVariables();
+ Map<String, Object> bindings = script.getBinding().getVariables();
bindings.clear();
Map<String, String> attributes = new HashMap<>();
@@ -510,6 +511,11 @@ public class ExecuteGroovyScript extends AbstractProcessor
{
bindings.put("RecordReader", recordReader);
bindings.put("RecordWriter", recordSetWriter);
+ // Must perform rollback to allow the script access to the
FlowFile.
+ if (flowFile != null) {
+ session.rollback();
+ }
+
script.run();
bindings.clear();
@@ -519,6 +525,8 @@ public class ExecuteGroovyScript extends AbstractProcessor {
getLogger().error(t.toString(), t);
onFailSQL(sql);
if (toFailureOnError) {
+ // FlowFile must be retrieved in order send to the failure
relationship as it may not have been retrieved in the script.
+ session.get();
//transfer all received to failure with two new attributes:
ERROR_MESSAGE and ERROR_STACKTRACE.
session.revertReceivedTo(REL_FAILURE,
StackTraceUtils.deepSanitize(t));
} else {
@@ -590,7 +598,7 @@ public class ExecuteGroovyScript extends AbstractProcessor {
/** simple HashMap with exception on access of non-existent key */
private static class AccessMap extends HashMap<String, Object> {
- private String parentKey;
+ private final String parentKey;
AccessMap(String parentKey) {
this.parentKey = parentKey;
}
diff --git
a/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/test/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScriptTest.java
b/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/test/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScriptTest.java
index b7eee39ea80..d74df4362fd 100644
---
a/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/test/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScriptTest.java
+++
b/nifi-extension-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/test/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScriptTest.java
@@ -576,7 +576,7 @@ public class ExecuteGroovyScriptTest {
runner.assertValid();
runner.setEnvironmentVariableValue("test", "cannot be converted to a
date");
- runner.enqueue("test content".getBytes(StandardCharsets.UTF_8));
+ runner.enqueue("test content");
runner.run();
@@ -587,6 +587,52 @@ public class ExecuteGroovyScriptTest {
assertTrue(flowFile.getAttribute(ProcessSessionWrap.ERROR_MESSAGE).contains("IllegalAttributeException"));
}
+ @Test
+ void testTransferToFailureStrategyWhereScriptHandlesRelationship() {
+ runner.setProperty(ExecuteGroovyScript.SCRIPT_BODY, """
+ def ff = session.get()
+ if (!ff) return
+ session.transfer(ff, REL_SUCCESS)
+ """);
+ runner.setProperty(ExecuteGroovyScript.FAIL_STRATEGY,
ExecuteGroovyScript.TRANSFER_TO_FAILURE);
+ runner.assertValid();
+
+ runner.enqueue("test content");
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ExecuteGroovyScript.REL_SUCCESS,
1);
+ }
+
+ @Test
+ void testTransferToFailureStrategyWhereScriptFailsBeforeSessionGet() {
+ runner.setProperty(ExecuteGroovyScript.SCRIPT_BODY, """
+ throw new RuntimeException("fail before session.get")
+ """);
+ runner.setProperty(ExecuteGroovyScript.FAIL_STRATEGY,
ExecuteGroovyScript.TRANSFER_TO_FAILURE);
+ runner.assertValid();
+ runner.enqueue("test content");
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ExecuteGroovyScript.REL_FAILURE,
1);
+ }
+
+ @Test
+ void testTransferToFailureStrategyWhereScriptFailsAfterSessionGet() {
+ runner.setProperty(ExecuteGroovyScript.SCRIPT_BODY, """
+ session.get()
+ throw new RuntimeException("fail after session.get")
+ """);
+ runner.setProperty(ExecuteGroovyScript.FAIL_STRATEGY,
ExecuteGroovyScript.TRANSFER_TO_FAILURE);
+ runner.assertValid();
+ runner.enqueue("test content");
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ExecuteGroovyScript.REL_FAILURE,
1);
+ }
+
@Test
void testMigrateProperties() {
final Map<String, String> expectedRenamed = Map.ofEntries(
@@ -611,7 +657,7 @@ public class ExecuteGroovyScriptTest {
String expectedContent = string;
if (windows) {
- expectedContent = expectedContent.replaceAll("\n", "\r\n");
+ expectedContent = expectedContent.replace("\n", "\r\n");
}
return expectedContent;