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 aec21031161 NIFI-15729 Fixed Script reloading in
ScriptedRecordSetWriter (#11022)
aec21031161 is described below
commit aec2103116150b71ed320952b2e0c44c186c0c89
Author: David Handermann <[email protected]>
AuthorDate: Thu Mar 19 03:35:51 2026 -0500
NIFI-15729 Fixed Script reloading in ScriptedRecordSetWriter (#11022)
- Set ScriptRunner to null on modification to Script File and Script Body
properties
---
.../script/AbstractScriptedControllerService.java | 6 +-
.../record/script/ScriptedRecordSetWriterTest.java | 162 +++++++++++----------
.../groovy/test_record_writer_inline.groovy | 10 +-
3 files changed, 87 insertions(+), 91 deletions(-)
diff --git
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/AbstractScriptedControllerService.java
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/AbstractScriptedControllerService.java
index da79cc676fc..3bfb6c3629d 100644
---
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/AbstractScriptedControllerService.java
+++
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/AbstractScriptedControllerService.java
@@ -109,7 +109,6 @@ public abstract class AbstractScriptedControllerService
extends AbstractControll
*/
@Override
public void onPropertyModified(final PropertyDescriptor descriptor, final
String oldValue, final String newValue) {
-
validationResults.set(new HashSet<>());
if (ScriptingComponentUtils.SCRIPT_FILE.equals(descriptor)
@@ -117,10 +116,7 @@ public abstract class AbstractScriptedControllerService
extends AbstractControll
|| ScriptingComponentUtils.MODULES.equals(descriptor)
|| scriptingComponentHelper.scriptEngine.equals(descriptor)) {
scriptNeedsReload.set(true);
- // Need to reset scriptEngine if the value has changed
- if (scriptingComponentHelper.scriptEngine.equals(descriptor) ||
ScriptingComponentUtils.MODULES.equals(descriptor)) {
- scriptRunner = null;
- }
+ scriptRunner = null;
}
}
diff --git
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/record/script/ScriptedRecordSetWriterTest.java
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/record/script/ScriptedRecordSetWriterTest.java
index 6e643a940d3..a7b6f620a0b 100644
---
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/record/script/ScriptedRecordSetWriterTest.java
+++
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/java/org/apache/nifi/record/script/ScriptedRecordSetWriterTest.java
@@ -16,115 +16,123 @@
*/
package org.apache.nifi.record.script;
-import org.apache.nifi.processor.AbstractProcessor;
-import org.apache.nifi.processor.ProcessContext;
-import org.apache.nifi.processor.ProcessSession;
-import org.apache.nifi.processor.exception.ProcessException;
-import org.apache.nifi.processors.script.AccessibleScriptingComponentHelper;
import org.apache.nifi.script.ScriptingComponentHelper;
import org.apache.nifi.script.ScriptingComponentUtils;
import org.apache.nifi.serialization.RecordSetWriter;
import org.apache.nifi.serialization.SimpleRecordSchema;
+import org.apache.nifi.serialization.WriteResult;
import org.apache.nifi.serialization.record.MapRecord;
import org.apache.nifi.serialization.record.RecordField;
import org.apache.nifi.serialization.record.RecordFieldType;
import org.apache.nifi.serialization.record.RecordSchema;
import org.apache.nifi.serialization.record.RecordSet;
-import org.apache.nifi.util.MockComponentLog;
+import org.apache.nifi.util.NoOpProcessor;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-/**
- * Unit tests for the ScriptedReader class
- */
-public class ScriptedRecordSetWriterTest {
- @TempDir
- private static Path targetPath;
+class ScriptedRecordSetWriterTest {
+ private static final Path GROOVY_SCRIPT_LOCATION =
Paths.get("src/test/resources/groovy/test_record_writer_inline.groovy");
+ private static final String GROOVY_SCRIPT_ENGINE = "Groovy";
- @BeforeAll
- public static void setUpOnce() throws Exception {
-
Files.copy(Paths.get("src/test/resources/groovy/test_record_writer_inline.groovy"),
targetPath, StandardCopyOption.REPLACE_EXISTING);
- }
+ private static final String SERVICE_ID =
ScriptedRecordSetWriterTest.class.getSimpleName();
+
+ private static final String ID_FIELD = "id";
+ private static final String LABEL_FIELD = "label";
+ private static final RecordSchema RECORD_SCHEMA = new SimpleRecordSchema(
+ List.of(
+ new RecordField(ID_FIELD,
RecordFieldType.INT.getDataType()),
+ new RecordField(LABEL_FIELD,
RecordFieldType.STRING.getDataType())
+ )
+ );
+ private static final MapRecord[] RECORDS = new MapRecord[]{
+ new MapRecord(RECORD_SCHEMA, Map.of(
+ ID_FIELD, 1,
+ LABEL_FIELD, "First Record"
+ )),
+ new MapRecord(RECORD_SCHEMA, Map.of(
+ ID_FIELD, 2,
+ LABEL_FIELD, "Second Record"
+ ))
+ };
+ private static final RecordSet RECORD_SET = RecordSet.of(RECORD_SCHEMA,
RECORDS);
+ private static final String RECORD_TAG = "record";
+
+ private static final String APPLICATION_XML = "application/xml";
+ private static final String TEXT_XML = "text/xml";
+
+ private final TestRunner runner =
TestRunners.newTestRunner(NoOpProcessor.class);
@Test
void testRecordWriterGroovyScript() throws Exception {
- final TestRunner runner = TestRunners.newTestRunner(new
AbstractProcessor() {
- @Override
- public void onTrigger(final ProcessContext context, final
ProcessSession session) throws ProcessException {
- }
- });
-
- MockScriptedWriter recordSetWriterFactory = new MockScriptedWriter();
- runner.addControllerService("writer", recordSetWriterFactory);
- runner.setProperty(recordSetWriterFactory, "Script Engine", "Groovy");
- runner.setProperty(recordSetWriterFactory,
ScriptingComponentUtils.SCRIPT_FILE, targetPath.toString());
- runner.setProperty(recordSetWriterFactory,
ScriptingComponentUtils.SCRIPT_BODY, (String) null);
- runner.setProperty(recordSetWriterFactory,
ScriptingComponentUtils.MODULES, (String) null);
- runner.enableControllerService(recordSetWriterFactory);
-
- RecordSchema schema =
recordSetWriterFactory.getSchema(Collections.emptyMap(), null);
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- RecordSetWriter recordSetWriter =
recordSetWriterFactory.createWriter(new MockComponentLog("id",
recordSetWriterFactory), schema, outputStream, Collections.emptyMap());
- assertNotNull(recordSetWriter);
-
- SimpleRecordSchema recordSchema = new
SimpleRecordSchema(Arrays.asList(new RecordField("id",
RecordFieldType.INT.getDataType()),
- new RecordField("name", RecordFieldType.STRING.getDataType()),
- new RecordField("code", RecordFieldType.INT.getDataType())));
- MapRecord[] records = createMapRecords(recordSchema);
-
- recordSetWriter.write(RecordSet.of(recordSchema, records));
-
- DocumentBuilder documentBuilder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
- Document document = documentBuilder.parse(new
ByteArrayInputStream(outputStream.toByteArray()));
- XPathFactory xpathfactory = XPathFactory.newInstance();
- XPath xpath = xpathfactory.newXPath();
- assertEquals("1", xpath.evaluate("//record[1]/id/text()", document));
- assertEquals("200", xpath.evaluate("//record[2]/code/text()",
document));
- assertEquals("Ramon", xpath.evaluate("//record[3]/name/text()",
document));
+ final String scriptBody = Files.readString(GROOVY_SCRIPT_LOCATION);
+ final ScriptedRecordSetWriter scriptedRecordSetWriter =
addScriptedRecordSetWriter(scriptBody);
+
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try (
+ RecordSetWriter recordSetWriter =
scriptedRecordSetWriter.createWriter(runner.getLogger(), RECORD_SCHEMA,
outputStream, Collections.emptyMap())
+ ) {
+ assertEquals(APPLICATION_XML, recordSetWriter.getMimeType());
+
+ final WriteResult writeResult = recordSetWriter.write(RECORD_SET);
+ assertEquals(RECORDS.length, writeResult.getRecordCount());
+ }
+
+ assertRecordsFound(outputStream);
}
- private static MapRecord[] createMapRecords(SimpleRecordSchema
recordSchema) {
- Map<String, Object> map = new LinkedHashMap<>(3);
- map.put("id", 1);
- map.put("name", "John");
- map.put("code", 100);
- Map<String, Object> map1 = new LinkedHashMap<>(3);
- map1.put("id", 2);
- map1.put("name", "Mary");
- map1.put("code", 200);
- Map<String, Object> map2 = new LinkedHashMap<>(3);
- map2.put("id", 3);
- map2.put("name", "Ramon");
- map2.put("code", 300);
-
- return new MapRecord[]{new MapRecord(recordSchema, map), new
MapRecord(recordSchema, map1), new MapRecord(recordSchema, map2)};
+ @Test
+ void testScriptReloaded() throws Exception {
+ final String scriptBody = Files.readString(GROOVY_SCRIPT_LOCATION);
+ final ScriptedRecordSetWriter scriptedRecordSetWriter =
addScriptedRecordSetWriter(scriptBody);
+
+ final RecordSetWriter recordSetWriter =
scriptedRecordSetWriter.createWriter(runner.getLogger(), RECORD_SCHEMA, new
ByteArrayOutputStream(), Collections.emptyMap());
+ assertEquals(APPLICATION_XML, recordSetWriter.getMimeType());
+
+ runner.disableControllerService(scriptedRecordSetWriter);
+ final String scriptBodyUpdated = scriptBody.replace(APPLICATION_XML,
TEXT_XML);
+ runner.setProperty(scriptedRecordSetWriter,
ScriptingComponentUtils.SCRIPT_BODY, scriptBodyUpdated);
+ runner.enableControllerService(scriptedRecordSetWriter);
+
+ final RecordSetWriter recordSetWriterReload =
scriptedRecordSetWriter.createWriter(runner.getLogger(), RECORD_SCHEMA, new
ByteArrayOutputStream(), Collections.emptyMap());
+ assertEquals(TEXT_XML, recordSetWriterReload.getMimeType());
}
- public static class MockScriptedWriter extends ScriptedRecordSetWriter
implements AccessibleScriptingComponentHelper {
- @Override
- public ScriptingComponentHelper getScriptingComponentHelper() {
- return this.scriptingComponentHelper;
- }
+
+ private void assertRecordsFound(final ByteArrayOutputStream outputStream)
throws Exception {
+ final Document document = getDocument(outputStream);
+ final NodeList recordNodes = document.getElementsByTagName(RECORD_TAG);
+
+ assertEquals(RECORDS.length, recordNodes.getLength());
+ }
+
+ private Document getDocument(final ByteArrayOutputStream outputStream)
throws Exception {
+ final byte[] bytes = outputStream.toByteArray();
+ final DocumentBuilder documentBuilder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ return documentBuilder.parse(new ByteArrayInputStream(bytes));
+ }
+
+ private ScriptedRecordSetWriter addScriptedRecordSetWriter(final String
scriptBody) throws Exception {
+ final ScriptedRecordSetWriter scriptedRecordSetWriter = new
ScriptedRecordSetWriter();
+ runner.addControllerService(SERVICE_ID, scriptedRecordSetWriter);
+ runner.setProperty(scriptedRecordSetWriter,
ScriptingComponentHelper.getScriptEnginePropertyBuilder().build().getName(),
GROOVY_SCRIPT_ENGINE);
+ runner.setProperty(scriptedRecordSetWriter,
ScriptingComponentUtils.SCRIPT_BODY, scriptBody);
+ runner.enableControllerService(scriptedRecordSetWriter);
+
+ return scriptedRecordSetWriter;
}
}
diff --git
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_record_writer_inline.groovy
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_record_writer_inline.groovy
index 99cb1df80a9..34d006d151e 100644
---
a/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_record_writer_inline.groovy
+++
b/nifi-extension-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/test/resources/groovy/test_record_writer_inline.groovy
@@ -19,7 +19,6 @@ import groovy.xml.MarkupBuilder
import org.apache.nifi.controller.AbstractControllerService
import org.apache.nifi.controller.ConfigurationContext
-import org.apache.nifi.flowfile.FlowFile
import org.apache.nifi.logging.ComponentLog
import org.apache.nifi.schema.access.SchemaNotFoundException
import org.apache.nifi.serialization.RecordSetWriter
@@ -30,7 +29,6 @@ import org.apache.nifi.serialization.record.RecordSchema
import org.apache.nifi.serialization.record.RecordSet
import org.apache.nifi.stream.io.NonCloseableOutputStream
-
class GroovyRecordSetWriter implements RecordSetWriter {
private int recordCount = 0;
private final OutputStream out;
@@ -99,14 +97,8 @@ class GroovyRecordSetWriter implements RecordSetWriter {
class GroovyRecordSetWriterFactory extends AbstractControllerService
implements RecordSetWriterFactory {
- ComponentLog logger;
-
- void setLogger(ComponentLog logger) {
- this.logger = logger
- }
-
void onEnabled(final ConfigurationContext context) {
- logger.info("in onEnabled")
+
}
@Override