diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
index 1a24deab13dd..047c5810fb3c 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
@@ -34,6 +34,8 @@
src/test/resources/RestrictBackpressureSettings/RestrictBackpressureSettings_noViolation.json
src/test/resources/RestrictBackpressureSettings/RestrictBackpressureSettings.json
+ src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration_noViolation.json
+ src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/DisallowComponentType.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/DisallowComponentType.java
index cacb5a81dd08..e562a59ab5e3 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/DisallowComponentType.java
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/DisallowComponentType.java
@@ -26,9 +26,7 @@
import org.apache.nifi.flowanalysis.FlowAnalysisRuleContext;
import org.apache.nifi.processor.util.StandardValidators;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -44,17 +42,13 @@ public class DisallowComponentType extends AbstractFlowAnalysisRule {
.addValidator(StandardValidators.NON_BLANK_VALIDATOR)
.build();
- private final static List propertyDescriptors;
-
- static {
- List _propertyDescriptors = new ArrayList<>();
- _propertyDescriptors.add(COMPONENT_TYPE);
- propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors);
- }
+ private final static List PROPERTY_DESCRIPTORS = List.of(
+ COMPONENT_TYPE
+ );
@Override
protected List getSupportedPropertyDescriptors() {
- return propertyDescriptors;
+ return PROPERTY_DESCRIPTORS;
}
@Override
@@ -63,8 +57,7 @@ public Collection analyzeComponent(VersionedComponent c
String componentType = context.getProperty(COMPONENT_TYPE).getValue();
- if (component instanceof VersionedExtensionComponent) {
- VersionedExtensionComponent versionedExtensionComponent = (VersionedExtensionComponent) component;
+ if (component instanceof VersionedExtensionComponent versionedExtensionComponent) {
String encounteredComponentType = versionedExtensionComponent.getType();
String encounteredSimpleComponentType = encounteredComponentType.substring(encounteredComponentType.lastIndexOf(".") + 1);
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettings.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettings.java
index d336cbfdb6ee..9f8094b69e94 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettings.java
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettings.java
@@ -21,24 +21,19 @@
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
-import org.apache.nifi.flow.VersionedComponent;
-import org.apache.nifi.flow.VersionedConnection;
import org.apache.nifi.flow.VersionedProcessGroup;
-import org.apache.nifi.flow.VersionedProcessor;
import org.apache.nifi.flowanalysis.AbstractFlowAnalysisRule;
import org.apache.nifi.flowanalysis.FlowAnalysisRuleContext;
import org.apache.nifi.flowanalysis.GroupAnalysisResult;
+import org.apache.nifi.flowanalysis.rules.util.ConnectionViolation;
+import org.apache.nifi.flowanalysis.rules.util.FlowAnalysisRuleUtils;
+import org.apache.nifi.flowanalysis.rules.util.ViolationType;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.processor.util.StandardValidators;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
@Tags({"connection", "backpressure"})
@CapabilityDescription("This rule will generate a violation if backpressure settings of a connection exceed configured thresholds. "
@@ -125,117 +120,81 @@ protected List getSupportedPropertyDescriptors() {
@Override
public Collection analyzeProcessGroup(VersionedProcessGroup pg, FlowAnalysisRuleContext context) {
- final Collection results = new HashSet<>();
+ final Collection violations = new ArrayList<>();
final long minCount = context.getProperty(COUNT_MIN).asLong();
final long maxCount = context.getProperty(COUNT_MAX).asLong();
final double minSize = context.getProperty(SIZE_MIN).asDataSize(DataUnit.B);
final double maxSize = context.getProperty(SIZE_MAX).asDataSize(DataUnit.B);
- // Map of all id/components to generate more human readable violations
- final Map idComponent = Stream.of(
- pg.getFunnels().stream(),
- pg.getProcessors().stream(),
- pg.getInputPorts().stream(),
- pg.getOutputPorts().stream())
- .flatMap(c -> c)
- .collect(Collectors.toMap(c -> c.getIdentifier(), Function.identity()));
-
- pg.getConnections().stream().forEach(
+ pg.getConnections().forEach(
connection -> {
if (connection.getBackPressureObjectThreshold() < minCount) {
- results.add(buildViolation(connection,
- idComponent.get(connection.getSource().getId()),
- idComponent.get(connection.getDestination().getId()),
+ violations.add(new ConnectionViolation(connection,
BackpressureViolationType.BP_COUNT_THRESHOLD_BELOW_LIMIT,
- getViolationMessage(BackpressureViolationType.BP_COUNT_THRESHOLD_BELOW_LIMIT, connection.getBackPressureObjectThreshold().toString(), Long.toString(minCount))));
+ this.getClass().getSimpleName(),
+ connection.getBackPressureObjectThreshold().toString(),
+ context.getProperty(COUNT_MIN).getValue()));
}
if (connection.getBackPressureObjectThreshold() > maxCount) {
- results.add(buildViolation(connection,
- idComponent.get(connection.getSource().getId()),
- idComponent.get(connection.getDestination().getId()),
+ violations.add(new ConnectionViolation(connection,
BackpressureViolationType.BP_COUNT_THRESHOLD_ABOVE_LIMIT,
- getViolationMessage(BackpressureViolationType.BP_COUNT_THRESHOLD_ABOVE_LIMIT, connection.getBackPressureObjectThreshold().toString(), Long.toString(maxCount))));
+ this.getClass().getSimpleName(),
+ connection.getBackPressureObjectThreshold().toString(),
+ context.getProperty(COUNT_MAX).getValue()));
}
final double sizeThreshold = DataUnit.parseDataSize(connection.getBackPressureDataSizeThreshold(), DataUnit.B);
if (Double.compare(sizeThreshold, minSize) < 0) {
- results.add(buildViolation(connection,
- idComponent.get(connection.getSource().getId()),
- idComponent.get(connection.getDestination().getId()),
+ violations.add(new ConnectionViolation(connection,
BackpressureViolationType.BP_SIZE_THRESHOLD_BELOW_LIMIT,
- getViolationMessage(BackpressureViolationType.BP_SIZE_THRESHOLD_BELOW_LIMIT, connection.getBackPressureDataSizeThreshold(), context.getProperty(SIZE_MIN).getValue())));
+ this.getClass().getSimpleName(),
+ connection.getBackPressureDataSizeThreshold(),
+ context.getProperty(SIZE_MIN).getValue()));
}
if (Double.compare(sizeThreshold, maxSize) > 0) {
- results.add(buildViolation(connection,
- idComponent.get(connection.getSource().getId()),
- idComponent.get(connection.getDestination().getId()),
+ violations.add(new ConnectionViolation(connection,
BackpressureViolationType.BP_SIZE_THRESHOLD_ABOVE_LIMIT,
- getViolationMessage(BackpressureViolationType.BP_SIZE_THRESHOLD_ABOVE_LIMIT, connection.getBackPressureDataSizeThreshold(), context.getProperty(SIZE_MAX).getValue())));
+ this.getClass().getSimpleName(),
+ connection.getBackPressureDataSizeThreshold(),
+ context.getProperty(SIZE_MAX).getValue()));
}
}
);
- return results;
- }
-
- private GroupAnalysisResult buildViolation(final VersionedConnection connection, final VersionedComponent source,
- final VersionedComponent destination, final BackpressureViolationType backpressureViolationType, final String violationMessage) {
- // The reason why we want the violation to be on the processor when we have one
- // (either as source or destination) is because in case the rule is "enforced"
- // we want the corresponding component to be invalid. If this is not a processor
- // (funnel, process group, etc) we cannot make it invalid so we put the
- // violation on the connection itself.
- if (!(source instanceof VersionedProcessor) && !(destination instanceof VersionedProcessor)) {
- // connection between two components that are not processors and cannot be invalid, setting violation on connection
- return GroupAnalysisResult.forComponent(connection,
- connection.getIdentifier() + "_" + backpressureViolationType.getId(),
- getLocationMessage(connection, source, destination) + violationMessage).build();
- } else if (source instanceof VersionedProcessor) {
- // defining violation on source processor
- return GroupAnalysisResult.forComponent(source,
- connection.getIdentifier() + "_" + backpressureViolationType.getId(),
- getLocationMessage(connection, source, destination) + violationMessage).build();
- } else {
- // defining violation on destination processor
- return GroupAnalysisResult.forComponent(destination,
- connection.getIdentifier() + "_" + backpressureViolationType.getId(),
- getLocationMessage(connection, source, destination) + violationMessage).build();
- }
- }
-
- private String getLocationMessage(final VersionedConnection connection, final VersionedComponent source, final VersionedComponent destination) {
- if (source == null || destination == null) {
- return "The connection [" + connection.getIdentifier() + "] is violating the rule for backpressure settings. ";
- }
- return "The connection [" + connection.getIdentifier() + "] connecting " + source.getName() + " [" + source.getIdentifier() + "] to "
- + destination.getName() + " [" + destination.getIdentifier() + "] is violating the rule for backpressure settings. ";
- }
-
- private String getViolationMessage(final BackpressureViolationType backpressureViolationType, final String configured, final String limit) {
- return switch (backpressureViolationType) {
- case BP_COUNT_THRESHOLD_ABOVE_LIMIT -> "The connection is configured with a Backpressure Count Threshold of " + configured + " and it should be lesser or equal than " + limit + ".";
- case BP_COUNT_THRESHOLD_BELOW_LIMIT -> "The connection is configured with a Backpressure Count Threshold of " + configured + " and it should be greater or equal than " + limit + ".";
- case BP_SIZE_THRESHOLD_ABOVE_LIMIT -> "The connection is configured with a Backpressure Data Size Threshold of " + configured + " and it should be lesser or equal than " + limit + ".";
- case BP_SIZE_THRESHOLD_BELOW_LIMIT -> "The connection is configured with a Backpressure Data Size Threshold of " + configured + " and it should be greater or equal than " + limit + ".";
- };
+ return FlowAnalysisRuleUtils.convertToGroupAnalysisResults(pg, violations);
}
- private enum BackpressureViolationType {
+ private enum BackpressureViolationType implements ViolationType {
- BP_COUNT_THRESHOLD_BELOW_LIMIT("BackpressureCountThresholdTooLow"),
- BP_COUNT_THRESHOLD_ABOVE_LIMIT("BackpressureCountThresholdTooHigh"),
- BP_SIZE_THRESHOLD_BELOW_LIMIT("BackpressureSizeThresholdTooLow"),
- BP_SIZE_THRESHOLD_ABOVE_LIMIT("BackpressureSizeThresholdTooHigh");
+ BP_COUNT_THRESHOLD_BELOW_LIMIT("BackpressureCountThresholdTooLow", "Back Pressure Count Threshold", "cannot be less than"),
+ BP_COUNT_THRESHOLD_ABOVE_LIMIT("BackpressureCountThresholdTooHigh", "Back Pressure Count Threshold", "cannot be greater than"),
+ BP_SIZE_THRESHOLD_BELOW_LIMIT("BackpressureSizeThresholdTooLow", "Back Pressure Data Size Threshold", "cannot be less than"),
+ BP_SIZE_THRESHOLD_ABOVE_LIMIT("BackpressureSizeThresholdTooHigh", "Back Pressure Data Size Threshold", "cannot be greater than");
private final String id;
+ private final String configurationItem;
+ private final String violationMessage;
- BackpressureViolationType(String id) {
+ BackpressureViolationType(String id, String configurationItem, String violationMessage) {
this.id = id;
+ this.configurationItem = configurationItem;
+ this.violationMessage = violationMessage;
}
+ @Override
public String getId() {
return this.id;
}
+ @Override
+ public String getConfigurationItem() {
+ return this.configurationItem;
+ }
+
+ @Override
+ public String getViolationMessage() {
+ return this.violationMessage;
+ }
+
}
}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpiration.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpiration.java
new file mode 100644
index 000000000000..41046a25b546
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpiration.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.flowanalysis.AbstractFlowAnalysisRule;
+import org.apache.nifi.flowanalysis.FlowAnalysisRuleContext;
+import org.apache.nifi.flowanalysis.GroupAnalysisResult;
+import org.apache.nifi.flowanalysis.rules.util.ConnectionViolation;
+import org.apache.nifi.flowanalysis.rules.util.FlowAnalysisRuleUtils;
+import org.apache.nifi.flowanalysis.rules.util.ViolationType;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.util.FormatUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Tags({"connection", "expiration", "age"})
+@CapabilityDescription("This rule will generate a violation if FlowFile expiration settings of a connection exceed configured thresholds. "
+ + "Improper configuration of FlowFile expiration settings can cause files to be deleted unexpectedly and can cause the content "
+ + "repository to fill up.")
+public class RestrictFlowFileExpiration extends AbstractFlowAnalysisRule {
+ public static final PropertyDescriptor ALLOW_ZERO = new PropertyDescriptor.Builder()
+ .name("Allow Zero Expiration")
+ .description("If set to true, a 0 second FlowFile Expiration on connections is allowed despite other configured restrictions."
+ + " If set to false, a 0 second FlowFile Expiration will be compared against the other configured restrictions."
+ + " This can be used to prevent a user from setting a value of 0 seconds which could fill up"
+ + " the content repository if files accumulate in front of stopped processors.")
+ .required(true)
+ .allowableValues("true", "false")
+ .defaultValue("true")
+ .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
+ .build();
+
+ public static final PropertyDescriptor MIN_FLOWFILE_EXPIRATION = new PropertyDescriptor.Builder()
+ .name("Minimum FlowFile Expiration")
+ .description("This is the minimum value that should be set for the FlowFile Expiration setting on connections."
+ + " This can be used to prevent a user from setting a very small expiration which can cause files to be"
+ + " deleted unexpectedly.")
+ .required(true)
+ .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+ .defaultValue("1 min")
+ .build();
+
+ public static final PropertyDescriptor MAX_FLOWFILE_EXPIRATION = new PropertyDescriptor.Builder()
+ .name("Maximum FlowFile Expiration")
+ .description("This is the maximum value that should be set for the FlowFile Expiration setting on connections."
+ + " This can be used to prevent a user from setting a large expiration which could fill up the content"
+ + " repository if files accumulate in front of stopped processors.")
+ .required(true)
+ .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
+ .defaultValue("30 days")
+ .build();
+
+ private static final List PROPERTY_DESCRIPTORS = List.of(
+ ALLOW_ZERO,
+ MIN_FLOWFILE_EXPIRATION,
+ MAX_FLOWFILE_EXPIRATION
+ );
+
+ @Override
+ protected List getSupportedPropertyDescriptors() {
+ return PROPERTY_DESCRIPTORS;
+ }
+
+ @Override
+ protected Collection customValidate(ValidationContext validationContext) {
+ final List results = new ArrayList<>();
+
+ final long minSize = validationContext.getProperty(MIN_FLOWFILE_EXPIRATION).asTimePeriod(TimeUnit.MILLISECONDS);
+ final long maxSize = validationContext.getProperty(MAX_FLOWFILE_EXPIRATION).asTimePeriod(TimeUnit.MILLISECONDS);
+
+ if (minSize > maxSize) {
+ results.add(
+ new ValidationResult.Builder()
+ .subject(MIN_FLOWFILE_EXPIRATION.getName())
+ .valid(false)
+ .explanation("Value of '" + MIN_FLOWFILE_EXPIRATION.getName() + "' cannot be greater than '" + MAX_FLOWFILE_EXPIRATION.getName() + "'")
+ .build());
+ }
+
+ return results;
+ }
+
+ @Override
+ public Collection analyzeProcessGroup(VersionedProcessGroup pg, FlowAnalysisRuleContext context) {
+ final Collection violations = new ArrayList<>();
+
+ final boolean allowZero = context.getProperty(ALLOW_ZERO).asBoolean();
+ final long minSize = context.getProperty(MIN_FLOWFILE_EXPIRATION).asTimePeriod(TimeUnit.MILLISECONDS);
+ final long maxSize = context.getProperty(MAX_FLOWFILE_EXPIRATION).asTimePeriod(TimeUnit.MILLISECONDS);
+
+ pg.getConnections().forEach(connection -> {
+ final long connectionExpiration = FormatUtils.getTimeDuration(connection.getFlowFileExpiration(), TimeUnit.MILLISECONDS);
+
+ if (connectionExpiration != 0 || !allowZero) {
+ if (connectionExpiration < minSize) {
+ violations.add(new ConnectionViolation(connection,
+ ExpirationViolationType.EXPIRATION_BELOW_MINIMUM,
+ this.getClass().getSimpleName(),
+ connection.getFlowFileExpiration(),
+ context.getProperty(MIN_FLOWFILE_EXPIRATION).getValue()));
+ }
+ if (connectionExpiration > maxSize) {
+ violations.add(new ConnectionViolation(connection,
+ ExpirationViolationType.EXPIRATION_ABOVE_MAXIMUM,
+ this.getClass().getSimpleName(),
+ connection.getFlowFileExpiration(),
+ context.getProperty(MAX_FLOWFILE_EXPIRATION).getValue()));
+ }
+ }
+ });
+
+ return FlowAnalysisRuleUtils.convertToGroupAnalysisResults(pg, violations);
+ }
+
+ private enum ExpirationViolationType implements ViolationType {
+
+ EXPIRATION_BELOW_MINIMUM("FlowFileExpirationTooLow", "FlowFile Expiration", "cannot be less than"),
+ EXPIRATION_ABOVE_MAXIMUM("FlowFileExpirationTooHigh", "FlowFile Expiration", "cannot be greater than");
+
+ private final String id;
+ private final String configurationItem;
+ private final String violationMessage;
+
+ ExpirationViolationType(String id, String configurationItem, String violationMessage) {
+ this.id = id;
+ this.configurationItem = configurationItem;
+ this.violationMessage = violationMessage;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public String getConfigurationItem() {
+ return this.configurationItem;
+ }
+
+ @Override
+ public String getViolationMessage() {
+ return this.violationMessage;
+ }
+ }
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ConnectionViolation.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ConnectionViolation.java
new file mode 100644
index 000000000000..b756802fe8a4
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ConnectionViolation.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules.util;
+
+import org.apache.nifi.flow.VersionedComponent;
+import org.apache.nifi.flow.VersionedConnection;
+import org.apache.nifi.flow.VersionedFunnel;
+import org.apache.nifi.flow.VersionedProcessor;
+import org.apache.nifi.flowanalysis.GroupAnalysisResult;
+import org.apache.nifi.util.StringUtils;
+
+import java.util.Map;
+
+public class ConnectionViolation {
+ private final VersionedConnection connection;
+ private final ViolationType violationType;
+ private final String rule;
+ private final String configuredValue;
+ private final String ruleLimit;
+
+ public ConnectionViolation(VersionedConnection connection,
+ ViolationType violationType,
+ String rule,
+ String configuredValue,
+ String ruleLimit) {
+ this.connection = connection;
+ this.violationType = violationType;
+ this.rule = rule;
+ this.configuredValue = configuredValue;
+ this.ruleLimit = ruleLimit;
+ }
+
+ /**
+ * Convert this ConnectionViolation to a Flow Analysis Rule GroupAnalysisResult.
+ *
+ * @param components the components in a ProcessGroup that could be attached to this VersionedConnection.
+ * @return a GroupAnalysisResult
+ */
+ public GroupAnalysisResult convertToGroupAnalysisResult(Map components) {
+ VersionedComponent source = components.get(connection.getSource().getId());
+ VersionedComponent destination = components.get(connection.getDestination().getId());
+ return GroupAnalysisResult.forComponent(
+ assignConnectionViolationToComponent(connection, source, destination),
+ connection.getIdentifier() + "_" + violationType.getId(),
+ getConnectionLocationMessage(connection, source, destination, rule)
+ + " " + getViolationReason())
+ .build();
+ }
+
+ /*
+ * Connection violations should be assigned to a connected Processor (either the source or destination)
+ * because in case the rule is "enforced" we want the corresponding component to be invalid.
+ * If neither connected component is a Processor (i.e. funnel, process group, port, etc.), then we cannot make
+ * it invalid so put the violation on the connection itself.
+ */
+ private VersionedComponent assignConnectionViolationToComponent(
+ final VersionedConnection connection, final VersionedComponent source, final VersionedComponent destination) {
+
+ if (!(source instanceof VersionedProcessor) && !(destination instanceof VersionedProcessor)) {
+ // for connections between two components that are not processors and cannot be invalid, set violation on connection
+ return connection;
+ } else if (source instanceof VersionedProcessor) {
+ // set violation on source processor
+ return source;
+ } else {
+ // set violation on destination processor
+ return destination;
+ }
+ }
+
+ /*
+ * Format a message stating what configured value violates a rule.
+ */
+ private String getViolationReason() {
+ return String.format("The connection is configured with a %s of %s which %s %s.",
+ violationType.getConfigurationItem(), configuredValue, violationType.getViolationMessage(), ruleLimit);
+ }
+
+ /*
+ * Format a rule violation message for a VersionedConnection with information about the source and destination VersionedComponent.
+ */
+ private String getConnectionLocationMessage(
+ final VersionedConnection connection, final VersionedComponent source, final VersionedComponent destination, final String rule) {
+
+ StringBuilder message = new StringBuilder();
+ message.append("The connection ");
+ if (StringUtils.isNotEmpty(connection.getName())) {
+ // Output connection name if it has been named
+ message.append(connection.getName()).append(' ');
+ } else {
+ // Output connection relationships by name
+ String relationships = String.join(",", connection.getSelectedRelationships());
+ if (StringUtils.isNotEmpty(relationships)) {
+ message.append(relationships).append(' ');
+ }
+ }
+ message.append("[").append(connection.getIdentifier()).append("]");
+ if (source != null) {
+ String sourceName =
+ (source instanceof VersionedFunnel) ? "funnel" : source.getName();
+ message.append(" from source ").append(sourceName).append(" [").append(source.getIdentifier()).append("]");
+ }
+ if (destination != null) {
+ String destinationName =
+ (destination instanceof VersionedFunnel) ? "funnel" : destination.getName();
+ message.append(" to destination ").append(destinationName).append(" [").append(destination.getIdentifier()).append("]");
+ }
+ message.append(" violates the ").append(rule).append(" rule.");
+ return message.toString();
+ }
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/FlowAnalysisRuleUtils.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/FlowAnalysisRuleUtils.java
new file mode 100644
index 000000000000..1d9c38eed78b
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/FlowAnalysisRuleUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules.util;
+
+import org.apache.nifi.flow.VersionedComponent;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.flowanalysis.GroupAnalysisResult;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class FlowAnalysisRuleUtils {
+ /**
+ * Build a map of all id/components in a ProcessGroup so that we can generate more human-readable violations.
+ *
+ * @param group a ProcessGroup
+ * @return a map of VersionedComponents indexed by their id
+ */
+ public static Map gatherComponents(VersionedProcessGroup group) {
+ return Stream.of(
+ group.getFunnels().stream(),
+ group.getProcessors().stream(),
+ group.getInputPorts().stream(),
+ group.getOutputPorts().stream())
+ .flatMap(c -> c)
+ .collect(Collectors.toMap(VersionedComponent::getIdentifier, Function.identity()));
+ }
+
+ /**
+ * Convert ConnectionViolations to GroupAnalysisResults.
+ *
+ * @param pg process group containing the connections
+ * @param violations flow analysis violations
+ * @return a collection of Flow Analysis Rule GroupAnalysisResult
+ */
+ public static Collection convertToGroupAnalysisResults(VersionedProcessGroup pg, Collection violations) {
+ if (!violations.isEmpty()) {
+ final Map components = FlowAnalysisRuleUtils.gatherComponents(pg);
+ final Collection results = new HashSet<>();
+ violations.forEach(violation -> results.add(violation.convertToGroupAnalysisResult(components)));
+ return results;
+ } else {
+ return Collections.emptySet();
+ }
+ }
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ViolationType.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ViolationType.java
new file mode 100644
index 000000000000..4cb312482138
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/util/ViolationType.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules.util;
+
+public interface ViolationType {
+ /**
+ * @return a unique identifier for this type of violation
+ */
+ String getId();
+
+ /**
+ * @return the configuration setting that caused the violation
+ */
+ String getConfigurationItem();
+
+ /**
+ * @return text that explains why the configured value is in violation
+ */
+ String getViolationMessage();
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
index df6a6e35f682..f5f1df7eb4b5 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
@@ -15,3 +15,4 @@
org.apache.nifi.flowanalysis.rules.DisallowComponentType
org.apache.nifi.flowanalysis.rules.RestrictBackpressureSettings
+org.apache.nifi.flowanalysis.rules.RestrictFlowFileExpiration
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettingsTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettingsTest.java
index d088d8512141..976250823a96 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettingsTest.java
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictBackpressureSettingsTest.java
@@ -16,10 +16,14 @@
*/
package org.apache.nifi.flowanalysis.rules;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.Collection;
import java.util.List;
+import org.apache.nifi.components.ValidationResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -44,14 +48,26 @@ public void setup() {
public void testWrongCountConfiguration() {
setProperty(RestrictBackpressureSettings.COUNT_MIN, "100");
setProperty(RestrictBackpressureSettings.COUNT_MAX, "10");
- assertFalse(rule.customValidate(validationContext).isEmpty());
+ Collection results = rule.customValidate(validationContext);
+ assertEquals(1, results.size());
+ results.forEach(result -> {
+ assertFalse(result.isValid());
+ assertEquals(RestrictBackpressureSettings.COUNT_MIN.getName(), result.getSubject());
+ assertTrue(result.getExplanation().contains("cannot be strictly greater than"));
+ });
}
@Test
public void testWrongSizeConfiguration() {
setProperty(RestrictBackpressureSettings.SIZE_MIN, "1GB");
setProperty(RestrictBackpressureSettings.SIZE_MAX, "1MB");
- assertFalse(rule.customValidate(validationContext).isEmpty());
+ Collection results = rule.customValidate(validationContext);
+ assertEquals(1, results.size());
+ results.forEach(result -> {
+ assertFalse(result.isValid());
+ assertEquals(RestrictBackpressureSettings.SIZE_MIN.getName(), result.getSubject());
+ assertTrue(result.getExplanation().contains("cannot be strictly greater than"));
+ });
}
@Test
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpirationTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpirationTest.java
new file mode 100644
index 000000000000..0b62a8594b08
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictFlowFileExpirationTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules;
+
+import org.apache.nifi.components.ValidationResult;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collection;
+import java.util.List;
+
+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 RestrictFlowFileExpirationTest extends AbstractFlowAnalaysisRuleTest {
+ @Override
+ protected RestrictFlowFileExpiration initializeRule() {
+ return new RestrictFlowFileExpiration();
+ }
+
+ @BeforeEach
+ @Override
+ public void setup() {
+ super.setup();
+ setProperty(RestrictFlowFileExpiration.ALLOW_ZERO, "true");
+ setProperty(RestrictFlowFileExpiration.MIN_FLOWFILE_EXPIRATION, RestrictFlowFileExpiration.MIN_FLOWFILE_EXPIRATION.getDefaultValue());
+ setProperty(RestrictFlowFileExpiration.MAX_FLOWFILE_EXPIRATION, RestrictFlowFileExpiration.MAX_FLOWFILE_EXPIRATION.getDefaultValue());
+ }
+
+ @Test
+ public void testBadConfiguration() {
+ setProperty(RestrictFlowFileExpiration.MIN_FLOWFILE_EXPIRATION, "2 days");
+ setProperty(RestrictFlowFileExpiration.MAX_FLOWFILE_EXPIRATION, "1 day");
+ Collection results = rule.customValidate(validationContext);
+ assertEquals(1, results.size());
+ results.forEach(result -> {
+ assertFalse(result.isValid());
+ assertEquals(RestrictFlowFileExpiration.MIN_FLOWFILE_EXPIRATION.getName(), result.getSubject());
+ assertTrue(result.getExplanation().contains("cannot be greater than"));
+ });
+ }
+
+ @Test
+ public void testNoViolations() throws Exception {
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration_noViolation.json",
+ List.of()
+ );
+ }
+
+ @Test
+ public void testViolationsAllowZeroTrue() throws Exception {
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json",
+ List.of(
+ "e26f3857-0192-1000-776a-3d62e15f75dc", // connection from UpdateAttribute to funnel, 1 sec
+ "e27073f8-0192-1000-cf43-9c41e69eadd2" // connection from output port to funnel, 45 days
+ )
+ );
+ }
+
+ @Test
+ public void testViolationsAllowZeroFalse() throws Exception {
+ setProperty(RestrictFlowFileExpiration.ALLOW_ZERO, "false");
+
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json",
+ List.of(
+ "e26f20c1-0192-1000-ff8b-bcf395c02076", // GenerateFlowFile, GenerateFlowFile connected to UpdateAttribute, 0 sec
+ "e26f3857-0192-1000-776a-3d62e15f75dc", // UpdateAttribute, UpdateAttribute connected to Funnel, 1 sec
+ "e26f3857-0192-1000-776a-3d62e15f75dc", // UpdateAttribute, Funnel connected to UpdateAttribute, 0 sec
+ "e26fd0d5-0192-1000-ee3d-f90141590475", // connection from Funnel to Funnel, 0 sec
+ "e27073f8-0192-1000-cf43-9c41e69eadd2" // connection from Process Group output port to Funnel, 45 days
+ )
+ );
+ }
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json
new file mode 100644
index 000000000000..7c9da29868db
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration.json
@@ -0,0 +1 @@
+{"flowContents":{"identifier":"d846f5a8-343e-3bda-8c49-4413dde11686","instanceIdentifier":"e26effa4-0192-1000-7826-18bfabd139ac","name":"RestrictBackpressureSettings","comments":"","position":{"x":1787.6793181202202,"y":391.0950428985503},"processGroups":[{"identifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d","instanceIdentifier":"e270189e-0192-1000-8aba-e274768ade7e","name":"EmbeddedProcessGroup","comments":"","position":{"x":-408.0,"y":216.0},"processGroups":[],"remoteProcessGroups":[],"processors":[{"identifier":"aa55546e-8ea7-3276-90ab-0c4b420297e5","instanceIdentifier":"e27038cf-0192-1000-1662-801cfccb85fb","name":"GenerateFlowFile","comments":"","position":{"x":-392.0,"y":-144.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.0.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"0B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"1","Unique FlowFiles":"false","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"},{"identifier":"905b896f-b267-32e4-9c72-2399757d7aad","instanceIdentifier":"e270c1f6-0192-1000-a019-53616442b6c2","name":"UpdateAttribute","comments":"","position":{"x":312.0,"y":128.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.0.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":["success"],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"inputPorts":[{"identifier":"5aa1a366-1c99-39e7-90b2-526d327eacf8","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b","name":"input","position":{"x":-227.0000091008809,"y":117.99999088368702},"type":"INPUT_PORT","concurrentlySchedulableTaskCount":1,"scheduledState":"ENABLED","allowRemoteAccess":false,"portFunction":"STANDARD","componentType":"INPUT_PORT","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"outputPorts":[{"identifier":"292428fc-fe6c-3420-9cf3-fc09587cd56e","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158","name":"output","position":{"x":295.9999908991191,"y":-91.00000911631298},"type":"OUTPUT_PORT","concurrentlySchedulableTaskCount":1,"scheduledState":"ENABLED","allowRemoteAccess":false,"portFunction":"STANDARD","componentType":"OUTPUT_PORT","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"connections":[{"identifier":"7539bdc8-3dd2-3636-ae44-201abecdbfb9","instanceIdentifier":"e2705bd1-0192-1000-6dad-2b1959453b79","name":"","source":{"id":"aa55546e-8ea7-3276-90ab-0c4b420297e5","type":"PROCESSOR","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"GenerateFlowFile","comments":"","instanceIdentifier":"e27038cf-0192-1000-1662-801cfccb85fb"},"destination":{"id":"292428fc-fe6c-3420-9cf3-fc09587cd56e","type":"OUTPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"output","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"31 days","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"},{"identifier":"366f7db1-c00b-30b9-a55a-f75f3d40a10f","instanceIdentifier":"e270d1b3-0192-1000-1407-369fa7dfe9ee","name":"","source":{"id":"5aa1a366-1c99-39e7-90b2-526d327eacf8","type":"INPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"input","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b"},"destination":{"id":"905b896f-b267-32e4-9c72-2399757d7aad","type":"PROCESSOR","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"UpdateAttribute","comments":"","instanceIdentifier":"e270c1f6-0192-1000-a019-53616442b6c2"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"59 secs","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"labels":[],"funnels":[],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","componentType":"PROCESS_GROUP","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","flowFileConcurrency":"UNBOUNDED","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"remoteProcessGroups":[],"processors":[{"identifier":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc","name":"UpdateAttribute","comments":"","position":{"x":232.0,"y":-216.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.0.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"84ca6d91-c383-34d0-8545-a27387acdabb","instanceIdentifier":"e26f20c1-0192-1000-ff8b-bcf395c02076","name":"GenerateFlowFile","comments":"","position":{"x":-472.0,"y":-232.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.0.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"0B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"1","Unique FlowFiles":"false","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"inputPorts":[],"outputPorts":[],"connections":[{"identifier":"d676965e-fd5d-3790-b008-dca3e47cc2cf","instanceIdentifier":"e27073f8-0192-1000-cf43-9c41e69eadd2","name":"","source":{"id":"292428fc-fe6c-3420-9cf3-fc09587cd56e","type":"OUTPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"output","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158"},"destination":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"45 days","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"67162d32-8bac-3306-a143-5b10c88b345e","instanceIdentifier":"e26f4ded-0192-1000-b21b-23c498138f39","name":"","source":{"id":"84ca6d91-c383-34d0-8545-a27387acdabb","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"GenerateFlowFile","comments":"","instanceIdentifier":"e26f20c1-0192-1000-ff8b-bcf395c02076"},"destination":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"dfbb4dc1-1f80-3102-b6b0-6b2557fc056a","instanceIdentifier":"e2717989-0192-1000-3d06-d6ae392ca1bd","name":"","source":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"destination":{"id":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"1 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"da573419-8fcb-3918-969c-71fc8f759c1e","instanceIdentifier":"e26fd0d5-0192-1000-ee3d-f90141590475","name":"","source":{"id":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532"},"destination":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"a1b2134c-2132-3a66-bdd3-632192e7e1e0","instanceIdentifier":"e2716319-0192-1000-8e8e-1418566faa42","name":"","source":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"destination":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"234c33fc-7962-3de7-bae1-0b3f6b211c70","instanceIdentifier":"e270eaa4-0192-1000-0622-8f9af5319328","name":"","source":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"destination":{"id":"5aa1a366-1c99-39e7-90b2-526d327eacf8","type":"INPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"input","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"5 days","prioritizers":[],"bends":[{"x":352.0,"y":280.0}],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"labels":[],"funnels":[{"identifier":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532","position":{"x":-278.89053176231346,"y":50.36534866201407},"componentType":"FUNNEL","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"17a37393-376f-3399-865d-665853b10d47","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb","position":{"x":390.4401741769484,"y":57.74767262457948},"componentType":"FUNNEL","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","componentType":"PROCESS_GROUP","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","flowFileConcurrency":"UNBOUNDED"},"externalControllerServices":{},"parameterContexts":{},"flowEncodingVersion":"1.0","parameterProviders":{},"latest":false}
\ No newline at end of file
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration_noViolation.json b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration_noViolation.json
new file mode 100644
index 000000000000..1dc178ec8a2c
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictFlowFileExpiration/RestrictFlowFileExpiration_noViolation.json
@@ -0,0 +1 @@
+{"flowContents":{"identifier":"d846f5a8-343e-3bda-8c49-4413dde11686","instanceIdentifier":"e26effa4-0192-1000-7826-18bfabd139ac","name":"RestrictBackpressureSettings","comments":"","position":{"x":1787.6793181202202,"y":391.0950428985503},"processGroups":[{"identifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d","instanceIdentifier":"e270189e-0192-1000-8aba-e274768ade7e","name":"EmbeddedProcessGroup","comments":"","position":{"x":-408.0,"y":216.0},"processGroups":[],"remoteProcessGroups":[],"processors":[{"identifier":"aa55546e-8ea7-3276-90ab-0c4b420297e5","instanceIdentifier":"e27038cf-0192-1000-1662-801cfccb85fb","name":"GenerateFlowFile","comments":"","position":{"x":-392.0,"y":-144.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.0.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"0B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"1","Unique FlowFiles":"false","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"},{"identifier":"905b896f-b267-32e4-9c72-2399757d7aad","instanceIdentifier":"e270c1f6-0192-1000-a019-53616442b6c2","name":"UpdateAttribute","comments":"","position":{"x":312.0,"y":128.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.0.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":["success"],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"inputPorts":[{"identifier":"5aa1a366-1c99-39e7-90b2-526d327eacf8","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b","name":"input","position":{"x":-227.0000091008809,"y":117.99999088368702},"type":"INPUT_PORT","concurrentlySchedulableTaskCount":1,"scheduledState":"ENABLED","allowRemoteAccess":false,"portFunction":"STANDARD","componentType":"INPUT_PORT","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"outputPorts":[{"identifier":"292428fc-fe6c-3420-9cf3-fc09587cd56e","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158","name":"output","position":{"x":295.9999908991191,"y":-91.00000911631298},"type":"OUTPUT_PORT","concurrentlySchedulableTaskCount":1,"scheduledState":"ENABLED","allowRemoteAccess":false,"portFunction":"STANDARD","componentType":"OUTPUT_PORT","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"connections":[{"identifier":"7539bdc8-3dd2-3636-ae44-201abecdbfb9","instanceIdentifier":"e2705bd1-0192-1000-6dad-2b1959453b79","name":"","source":{"id":"aa55546e-8ea7-3276-90ab-0c4b420297e5","type":"PROCESSOR","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"GenerateFlowFile","comments":"","instanceIdentifier":"e27038cf-0192-1000-1662-801cfccb85fb"},"destination":{"id":"292428fc-fe6c-3420-9cf3-fc09587cd56e","type":"OUTPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"output","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"},{"identifier":"366f7db1-c00b-30b9-a55a-f75f3d40a10f","instanceIdentifier":"e270d1b3-0192-1000-1407-369fa7dfe9ee","name":"","source":{"id":"5aa1a366-1c99-39e7-90b2-526d327eacf8","type":"INPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"input","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b"},"destination":{"id":"905b896f-b267-32e4-9c72-2399757d7aad","type":"PROCESSOR","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"UpdateAttribute","comments":"","instanceIdentifier":"e270c1f6-0192-1000-a019-53616442b6c2"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"fb55e92a-7d8e-31b7-b45e-702050f9749d"}],"labels":[],"funnels":[],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","componentType":"PROCESS_GROUP","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","flowFileConcurrency":"UNBOUNDED","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"remoteProcessGroups":[],"processors":[{"identifier":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc","name":"UpdateAttribute","comments":"","position":{"x":232.0,"y":-216.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.0.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"84ca6d91-c383-34d0-8545-a27387acdabb","instanceIdentifier":"e26f20c1-0192-1000-ff8b-bcf395c02076","name":"GenerateFlowFile","comments":"","position":{"x":-472.0,"y":-232.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.0.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"0B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"1","Unique FlowFiles":"false","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"inputPorts":[],"outputPorts":[],"connections":[{"identifier":"d676965e-fd5d-3790-b008-dca3e47cc2cf","instanceIdentifier":"e27073f8-0192-1000-cf43-9c41e69eadd2","name":"","source":{"id":"292428fc-fe6c-3420-9cf3-fc09587cd56e","type":"OUTPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"output","instanceIdentifier":"e2704f0f-0192-1000-d1d9-0629c2e8e158"},"destination":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"67162d32-8bac-3306-a143-5b10c88b345e","instanceIdentifier":"e26f4ded-0192-1000-b21b-23c498138f39","name":"","source":{"id":"84ca6d91-c383-34d0-8545-a27387acdabb","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"GenerateFlowFile","comments":"","instanceIdentifier":"e26f20c1-0192-1000-ff8b-bcf395c02076"},"destination":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"dfbb4dc1-1f80-3102-b6b0-6b2557fc056a","instanceIdentifier":"e2717989-0192-1000-3d06-d6ae392ca1bd","name":"","source":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"destination":{"id":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"da573419-8fcb-3918-969c-71fc8f759c1e","instanceIdentifier":"e26fd0d5-0192-1000-ee3d-f90141590475","name":"","source":{"id":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532"},"destination":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"a1b2134c-2132-3a66-bdd3-632192e7e1e0","instanceIdentifier":"e2716319-0192-1000-8e8e-1418566faa42","name":"","source":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"destination":{"id":"c4d73a47-2c5a-3f57-9a29-49788913d7d2","type":"PROCESSOR","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"UpdateAttribute","comments":"","instanceIdentifier":"e26f3857-0192-1000-776a-3d62e15f75dc"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"234c33fc-7962-3de7-bae1-0b3f6b211c70","instanceIdentifier":"e270eaa4-0192-1000-0622-8f9af5319328","name":"","source":{"id":"17a37393-376f-3399-865d-665853b10d47","type":"FUNNEL","groupId":"d846f5a8-343e-3bda-8c49-4413dde11686","name":"Funnel","comments":"","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb"},"destination":{"id":"5aa1a366-1c99-39e7-90b2-526d327eacf8","type":"INPUT_PORT","groupId":"fb55e92a-7d8e-31b7-b45e-702050f9749d","name":"input","instanceIdentifier":"e270a7d2-0192-1000-4b49-b7a20716fa6b"},"labelIndex":0,"zIndex":0,"selectedRelationships":[""],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[{"x":352.0,"y":280.0}],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"labels":[],"funnels":[{"identifier":"94c722c5-94a3-3d20-8a1b-ecd4b6948149","instanceIdentifier":"e26fbc81-0192-1000-5c4e-2b3b19088532","position":{"x":-278.89053176231346,"y":50.36534866201407},"componentType":"FUNNEL","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"},{"identifier":"17a37393-376f-3399-865d-665853b10d47","instanceIdentifier":"e26fc51d-0192-1000-2ada-2db85f6623fb","position":{"x":390.4401741769484,"y":57.74767262457948},"componentType":"FUNNEL","groupIdentifier":"d846f5a8-343e-3bda-8c49-4413dde11686"}],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","componentType":"PROCESS_GROUP","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","flowFileConcurrency":"UNBOUNDED"},"externalControllerServices":{},"parameterContexts":{},"flowEncodingVersion":"1.0","parameterProviders":{},"latest":false}
\ No newline at end of file