This is an automated email from the ASF dual-hosted git repository.
exceptionfactory 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 cfef1137eb3 NIFI-15715 Fixed Mock CapturingLogger Throwable argument
handling and added tests to TransformXml (#11008)
cfef1137eb3 is described below
commit cfef1137eb3e7bf4f22f7edc0238833ad6ae4de7
Author: dan-s1 <[email protected]>
AuthorDate: Fri Mar 13 16:56:07 2026 -0400
NIFI-15715 Fixed Mock CapturingLogger Throwable argument handling and added
tests to TransformXml (#11008)
Signed-off-by: David Handermann <[email protected]>
---
.../nifi/processors/standard/TransformXml.java | 2 +-
.../nifi/processors/standard/TestTransformXml.java | 223 +++++++++++++++++++++
.../java/org/apache/nifi/util/CapturingLogger.java | 18 +-
3 files changed, 237 insertions(+), 6 deletions(-)
diff --git
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/TransformXml.java
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/TransformXml.java
index 13e6e317d5b..e6ff53aaf6b 100644
---
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/TransformXml.java
+++
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/TransformXml.java
@@ -319,7 +319,7 @@ public class TransformXml extends AbstractProcessor {
session.getProvenanceReporter().modifyContent(transformed,
stopWatch.getElapsed(TimeUnit.MILLISECONDS));
getLogger().info("Transformation Completed {}", original);
} catch (final ProcessException e) {
- getLogger().error("Transformation Failed", original, e);
+ getLogger().error("Transformation Failed {}", original, e);
session.transfer(original, REL_FAILURE);
}
}
diff --git
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestTransformXml.java
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestTransformXml.java
index 4807d63bb8d..262d784fb2c 100644
---
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestTransformXml.java
+++
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestTransformXml.java
@@ -16,8 +16,11 @@
*/
package org.apache.nifi.processors.standard;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.nifi.lookup.SimpleKeyValueLookupService;
+import org.apache.nifi.processor.Relationship;
import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.util.LogMessage;
import org.apache.nifi.util.MockComponentLog;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.PropertyMigrationResult;
@@ -25,23 +28,40 @@ import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestTransformXml {
+ private static final String XML_FOR_TESTING_PARAMETERS = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <data>
+ <item>Some data</item>
+ </data>
+ """;
+
private TestRunner runner;
+ @TempDir
+ private Path temptDir;
+
@BeforeEach
void setUp() {
runner = TestRunners.newTestRunner(TransformXml.class);
@@ -353,6 +373,202 @@ public class TestTransformXml {
assertTrue(logger.getWarnMessages().isEmpty());
}
+ @Test
+ void testParameterDeclaredAndSet() throws IOException {
+ final String xslt = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:param name="customParam" select="'From XSLT'" />
+ <xsl:template match="/">
+ <root>
+ <message>
+ Value Selected: <xsl:value-of
select="$customParam"/>
+ </message>
+ </root>
+ </xsl:template>
+ </xsl:stylesheet>
+ """;
+
+ final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
+ runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
+ runner.setProperty("customParam", "From NIFI");
+ runner.enqueue(XML_FOR_TESTING_PARAMETERS);
+
+ runner.run();
+ runner.assertAllFlowFilesTransferred(TransformXml.REL_SUCCESS);
+ final MockFlowFile transformed =
runner.getFlowFilesForRelationship(TransformXml.REL_SUCCESS).getFirst();
+ final String expectedTransform = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <root>
+ <message>
+ Value Selected: From
NIFI</message>
+ </root>
+ """;
+ transformed.assertContentEquals(expectedTransform);
+ }
+
+ @Test
+ void testParameterSetButNotDeclared() throws IOException {
+ final String xslt = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="/">
+ <root>
+ <message>
+ Value Selected:
+ </message>
+ </root>
+ </xsl:template>
+ </xsl:stylesheet>
+ """;
+
+ final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
+ runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
+ runner.setProperty("customParam", "From NIFI");
+ runner.enqueue(XML_FOR_TESTING_PARAMETERS);
+
+ runner.run();
+ runner.assertAllFlowFilesTransferred(TransformXml.REL_SUCCESS);
+ final MockFlowFile transformed =
runner.getFlowFilesForRelationship(TransformXml.REL_SUCCESS).getFirst();
+ final String expectedTransform =
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <root>
+ <message>
+ Value Selected:
+ </message>
+ </root>
+ """;
+ transformed.assertContentEquals(expectedTransform);
+ }
+
+ @Test
+ void testParameterNotDeclaredButUsedInXslt() throws IOException {
+ final String xslt = """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="/">
+ <root>
+ <message>
+ Value Selected: <xsl:value-of
select="$customParam"/>
+ </message>
+ </root>
+ </xsl:template>
+ </xsl:stylesheet>
+ """;
+
+ final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
+ runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
+ runner.setProperty("customParam", "From NIFI");
+ runner.enqueue(XML_FOR_TESTING_PARAMETERS);
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(TransformXml.REL_FAILURE);
+ final MockComponentLog logger = runner.getLogger();
+ final List<LogMessage> errorMessages = logger.getErrorMessages();
+ assertTrue(errorMessages.getFirst().getMsg().contains("Variable
$customParam has not been declared"));
+ }
+
+ @ParameterizedTest
+ @MethodSource("parameterAsSpecificTypeArgs")
+ void testParameterAsSpecificType(String paramType, String parameterValue,
String defaultValue, Relationship expectedRelationship, String
expectedTransform) throws IOException {
+ final String parameterName = "customParam";
+ final String xslt = getXSLTWithParameterDefinedWithType(paramType,
defaultValue);
+ final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
+ runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
+ runner.setProperty(parameterName, parameterValue);
+ runner.enqueue(XML_FOR_TESTING_PARAMETERS);
+
+ runner.run();
+ runner.assertAllFlowFilesTransferred(expectedRelationship);
+
+ if (expectedTransform != null) {
+ final MockFlowFile transformed =
runner.getFlowFilesForRelationship(expectedRelationship).getFirst();
+ transformed.assertContentEquals(expectedTransform);
+ }
+
+ if (expectedRelationship == TransformXml.REL_FAILURE) {
+ assertTrue(runner.getLogger().getErrorMessages().stream()
+ .map(LogMessage::getThrowable)
+ .map(ExceptionUtils::getStackTrace)
+ .anyMatch(stackTrace ->
stackTrace.contains("ValidationException")));
+ }
+ }
+
+ private static Stream<Arguments> parameterAsSpecificTypeArgs() {
+ return Stream.of(
+ Arguments.argumentSet("Valid number", "xs:integer", "100",
"0", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:integer value is
100</message>
+ </report>
+ """),
+ Arguments.argumentSet("Invalid number", "xs:integer", "NIFI",
"0", TransformXml.REL_FAILURE, null),
+ Arguments.argumentSet("Valid boolean lowercase", "xs:boolean",
"true", "false", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:boolean value is
true</message>
+ </report>
+ """),
+ Arguments.argumentSet("Invalid boolean uppercase",
"xs:boolean", "TRUE", "false", TransformXml.REL_FAILURE, null),
+ Arguments.argumentSet("Valid ISO 8601 date", "xs:date",
"2026-01-01", "xs:date('1970-01-01')", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:date value is
2026-01-01</message>
+ </report>
+ """, null), //29 April 2003
+ Arguments.argumentSet("Invalid ISO 8601 date", "xs:date", "1
January 2026", "xs:date('1970-01-01')", TransformXml.REL_FAILURE, null),
+ Arguments.argumentSet("Valid ISO 8601 date time",
"xs:dateTime", "2026-01-01T00:00:00", "xs:dateTime('1970-01-01T00:00:00')",
TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:dateTime value is
2026-01-01T00:00:00</message>
+ </report>
+ """),
+ Arguments.argumentSet("Invalid ISO 8601 date time",
"xs:dateTime", "1970-01-01 00:00:00", "xs:dateTime('1970-01-01T00:00:00')",
TransformXml.REL_FAILURE, null),
+ Arguments.argumentSet("Valid time without timezone/offset",
"xs:time", "12:34:56.789", "current-time()", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:time value is
12:34:56.789</message>
+ </report>
+ """),
+ Arguments.argumentSet("Valid UTC time", "xs:time",
"12:34:56Z", "current-time()", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:time value is
12:34:56Z</message>
+ </report>
+ """),
+ Arguments.argumentSet("Valid Offset from UTC time", "xs:time",
"12:34:56-05:00", "current-time()", TransformXml.REL_SUCCESS,
+ """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <report xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <message>Param of type xs:time value is
12:34:56-05:00</message>
+ </report>
+ """)
+
+ );
+ }
+
+ private String getXSLTWithParameterDefinedWithType(String type, String
defaultValue) {
+ return """
+ <xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+
xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xsl:param name="customParam" as="%1$s" select="%2$s"/>
+ <xsl:template match="/">
+ <report>
+ <message>Param of type %1$s value is <xsl:value-of
select="$customParam"/></message>
+ </report>
+ </xsl:template>
+ </xsl:stylesheet>
+ """.formatted(type, defaultValue);
+ }
+
@Test
void testMigrateProperties() {
final Map<String, String> expectedRenamed = Map.ofEntries(
@@ -369,4 +585,11 @@ public class TestTransformXml {
final PropertyMigrationResult propertyMigrationResult =
runner.migrateProperties();
assertEquals(expectedRenamed,
propertyMigrationResult.getPropertiesRenamed());
}
+
+ private Path writeXslt(String xslt, String xsltName) throws IOException {
+ final Path xsltPath = temptDir.resolve(xsltName);
+ Files.writeString(xsltPath, xslt);
+
+ return xsltPath;
+ }
}
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/CapturingLogger.java
b/nifi-mock/src/main/java/org/apache/nifi/util/CapturingLogger.java
index c0014612a23..f9730580cba 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/CapturingLogger.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/CapturingLogger.java
@@ -88,7 +88,8 @@ public class CapturingLogger implements Logger {
@Override
public void trace(String format, Object... arguments) {
- traceMessages.add(new LogMessage(null, format, null, arguments));
+ final Throwable throwable = lastArgIsException(arguments) ?
(Throwable) arguments[arguments.length - 1] : null;
+ traceMessages.add(new LogMessage(null, format, throwable, arguments));
logger.trace(format, arguments);
}
@@ -161,7 +162,8 @@ public class CapturingLogger implements Logger {
@Override
public void debug(String format, Object... arguments) {
- debugMessages.add(new LogMessage(null, format, null, arguments));
+ final Throwable throwable = lastArgIsException(arguments) ?
(Throwable) arguments[arguments.length - 1] : null;
+ debugMessages.add(new LogMessage(null, format, throwable, arguments));
logger.debug(format, arguments);
}
@@ -232,8 +234,9 @@ public class CapturingLogger implements Logger {
@Override
public void info(String format, Object... arguments) {
+ final Throwable throwable = lastArgIsException(arguments) ?
(Throwable) arguments[arguments.length - 1] : null;
String message = MessageFormatter.arrayFormat(format,
arguments).getMessage();
- infoMessages.add(new LogMessage(null, message, null, arguments));
+ infoMessages.add(new LogMessage(null, message, throwable, arguments));
logger.info(format, arguments);
}
@@ -303,8 +306,9 @@ public class CapturingLogger implements Logger {
@Override
public void warn(String format, Object... arguments) {
+ final Throwable throwable = lastArgIsException(arguments) ?
(Throwable) arguments[arguments.length - 1] : null;
String message = MessageFormatter.arrayFormat(format,
arguments).getMessage();
- warnMessages.add(new LogMessage(null, message, null, arguments));
+ warnMessages.add(new LogMessage(null, message, throwable, arguments));
logger.warn(format, arguments);
}
@@ -384,8 +388,9 @@ public class CapturingLogger implements Logger {
@Override
public void error(String format, Object... arguments) {
+ final Throwable throwable = lastArgIsException(arguments) ?
(Throwable) arguments[arguments.length - 1] : null;
final String message = MessageFormatter.arrayFormat(format,
arguments).getMessage();
- errorMessages.add(new LogMessage(null, message, null, arguments));
+ errorMessages.add(new LogMessage(null, message, throwable, arguments));
logger.error(format, arguments);
}
@@ -434,4 +439,7 @@ public class CapturingLogger implements Logger {
logger.error(marker, msg, t);
}
+ private boolean lastArgIsException(final Object[] os) {
+ return (os != null && os.length > 0 && (os[os.length - 1] instanceof
Throwable));
+ }
}