From 6dccdd586e6ef8d1bda625982814f2a073918924 Mon Sep 17 00:00:00 2001 From: Mark Payne Date: Mon, 30 Sep 2019 10:21:31 -0400 Subject: [PATCH] NIFI-6726: Updated fingerprint logic to ensure that Controller Services are fingerprinted when housed within a ProcessGroup This closes #3775. Signed-off-by: Bryan Bende --- .../nifi/fingerprint/FingerprintFactory.java | 15 ++++++-- .../fingerprint/FingerprintFactoryTest.java | 28 ++++++++++++++ .../group-with-controller-services.xml | 37 +++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/nifi/fingerprint/group-with-controller-services.xml diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java index 3cf3df29b8..35d081bcff 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java @@ -238,7 +238,7 @@ public class FingerprintFactory { // root group final Element rootGroupElem = (Element) DomUtils.getChildNodesByTagName(flowControllerElem, "rootGroup").item(0); - addProcessGroupFingerprint(builder, rootGroupElem, controller); + addProcessGroupFingerprint(builder, rootGroupElem, encodingVersion); final Element controllerServicesElem = DomUtils.getChild(flowControllerElem, "controllerServices"); if (controllerServicesElem != null) { @@ -354,7 +354,7 @@ public class FingerprintFactory { return builder; } - private StringBuilder addProcessGroupFingerprint(final StringBuilder builder, final Element processGroupElem, final FlowController controller) throws FingerprintException { + StringBuilder addProcessGroupFingerprint(final StringBuilder builder, final Element processGroupElem, final FlowEncodingVersion encodingVersion) throws FingerprintException { // id appendFirstValue(builder, DomUtils.getChildNodesByTagName(processGroupElem, "id")); appendFirstValue(builder, DomUtils.getChildNodesByTagName(processGroupElem, "versionedComponentId")); @@ -372,7 +372,7 @@ public class FingerprintFactory { // processors final List processorElems = DomUtils.getChildElementsByTagName(processGroupElem, "processor"); - Collections.sort(processorElems, getIdsComparator()); + processorElems.sort(getIdsComparator()); for (final Element processorElem : processorElems) { addFlowFileProcessorFingerprint(builder, processorElem); } @@ -402,7 +402,7 @@ public class FingerprintFactory { final NodeList nestedProcessGroupElems = DomUtils.getChildNodesByTagName(processGroupElem, "processGroup"); final List sortedNestedProcessGroupElems = sortElements(nestedProcessGroupElems, getIdsComparator()); for (final Element nestedProcessGroupElem : sortedNestedProcessGroupElems) { - addProcessGroupFingerprint(builder, nestedProcessGroupElem, controller); + addProcessGroupFingerprint(builder, nestedProcessGroupElem, encodingVersion); } // remote process groups @@ -426,6 +426,13 @@ public class FingerprintFactory { addFunnelFingerprint(builder, funnelElem); } + final NodeList controllerServiceElems = DomUtils.getChildNodesByTagName(processGroupElem, "controllerService"); + final List sortedControllerServiceElems = sortElements(controllerServiceElems, getIdsComparator()); + for (final Element controllerServiceElem : sortedControllerServiceElems) { + final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElem, encryptor, encodingVersion); + addControllerServiceFingerprint(builder, dto); + } + // add variables final NodeList variableElems = DomUtils.getChildNodesByTagName(processGroupElem, "variable"); final List sortedVarList = sortElements(variableElems, getVariableNameComparator()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java index befe332b5e..35bc2e3cf7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java @@ -20,6 +20,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.nifi.connectable.Position; import org.apache.nifi.controller.ScheduledState; +import org.apache.nifi.controller.serialization.FlowEncodingVersion; import org.apache.nifi.controller.serialization.FlowSerializer; import org.apache.nifi.controller.serialization.ScheduledStateLookup; import org.apache.nifi.controller.serialization.StandardFlowSerializer; @@ -40,8 +41,10 @@ import org.xml.sax.SAXParseException; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.util.Collections; @@ -323,4 +326,29 @@ public class FingerprintFactoryTest { final Element componentElement = (Element) rootElement.getElementsByTagName("inputPort").item(0); assertEquals(expected, fingerprint("addRemoteGroupPortFingerprint", Element.class, componentElement)); } + + @Test + public void testControllerServicesIncludedInGroupFingerprint() throws ParserConfigurationException, IOException, SAXException { + final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); + final Document document = docBuilder.parse(new File("src/test/resources/nifi/fingerprint/group-with-controller-services.xml")); + final Element processGroup = document.getDocumentElement(); + + final StringBuilder sb = new StringBuilder(); + fingerprinter.addProcessGroupFingerprint(sb, processGroup, new FlowEncodingVersion(1, 0)); + + final String fingerprint = sb.toString(); + final String[] criticalFingerprintValues = new String[] { + "1234", + "s1", "service1", "prop1", "value1", "org.apache.nifi.services.FingerprintControllerService", + "s2", "service2", "another property", "another value", "org.apache.nifi.services.AnotherService", + }; + + for (final String criticalValue : criticalFingerprintValues) { + assertTrue("Fingerprint did not contain '" + criticalValue + "'", fingerprint.contains(criticalValue)); + } + + // Ensure that 's1' comes before 's2' in the fingerprint + assertTrue(fingerprint.indexOf("FingerprintControllerService") < fingerprint.indexOf("AnotherService")); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/nifi/fingerprint/group-with-controller-services.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/nifi/fingerprint/group-with-controller-services.xml new file mode 100644 index 0000000000..25b0065f3a --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/nifi/fingerprint/group-with-controller-services.xml @@ -0,0 +1,37 @@ + + + + 1234 + pg1 + + s1 + service1 + org.apache.nifi.services.FingerprintControllerService + + prop1 + value1 + + + + s2 + service2 + org.apache.nifi.services.AnotherService + + another property + another value + + + \ No newline at end of file