From 179675f0b42e61ce125932fcf57086024d5e6f57 Mon Sep 17 00:00:00 2001 From: Matthieu Cauffiez Date: Mon, 11 May 2020 10:59:40 -0400 Subject: [PATCH] NIFI-7380 - fix for controller service validation in NiFi Stateless This closes #4264. Signed-off-by: Matthieu Cauffiez Signed-off-by: Mark Payne --- .../core/StatelessConfigurationContext.java | 40 +++++++++++++------ ...atelessControllerServiceConfiguration.java | 13 +++--- .../StatelessControllerServiceLookup.java | 33 ++++++++++----- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessConfigurationContext.java index 15ec3311a2..669e1c5a55 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessConfigurationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessConfigurationContext.java @@ -16,7 +16,8 @@ */ package org.apache.nifi.stateless.core; -import org.apache.nifi.parameter.ParameterLookup; +import org.apache.nifi.controller.PropertyConfiguration; +import org.apache.nifi.parameter.ParameterContext; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.components.PropertyValue; import org.apache.nifi.controller.ConfigurationContext; @@ -24,43 +25,56 @@ import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.ControllerServiceLookup; import org.apache.nifi.registry.VariableRegistry; -import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; public class StatelessConfigurationContext implements ConfigurationContext { - private final Map properties; + private final Map properties; private final ControllerServiceLookup serviceLookup; private final ControllerService service; private final VariableRegistry variableRegistry; - private final ParameterLookup parameterLookup; + private final ParameterContext parameterContext; public StatelessConfigurationContext(final ControllerService service, - final Map properties, + final Map properties, final ControllerServiceLookup serviceLookup, final VariableRegistry variableRegistry, - final ParameterLookup parameterLookup) { + final ParameterContext parameterLookup) { this.service = service; this.properties = properties; this.serviceLookup = serviceLookup; this.variableRegistry = variableRegistry; - this.parameterLookup = parameterLookup; + this.parameterContext = parameterLookup; } @Override public PropertyValue getProperty(final PropertyDescriptor property) { - String value = properties.get(property); - if (value == null) { - value = getActualDescriptor(property).getDefaultValue(); - } - return new StatelessPropertyValue(value, serviceLookup, parameterLookup, variableRegistry); + final PropertyConfiguration setPropertyValue = properties.get(property); + final String propValue = (setPropertyValue == null) ? getActualDescriptor(property).getDefaultValue() : setPropertyValue.getEffectiveValue(parameterContext); + return new StatelessPropertyValue(propValue, serviceLookup, parameterContext, variableRegistry); } @Override public Map getProperties() { - return new HashMap<>(this.properties); + final List supported = service.getPropertyDescriptors(); + + final Map effectiveValues = new LinkedHashMap<>(); + for (final PropertyDescriptor descriptor : supported) { + effectiveValues.put(descriptor, null); + } + + for (final Map.Entry entry : properties.entrySet()) { + final PropertyDescriptor descriptor = entry.getKey(); + final PropertyConfiguration configuration = entry.getValue(); + final String value = configuration.getEffectiveValue(parameterContext); + + effectiveValues.put(descriptor, value); + } + + return effectiveValues; } @Override diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceConfiguration.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceConfiguration.java index 5cd32cf43e..cc977483b5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceConfiguration.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceConfiguration.java @@ -18,6 +18,7 @@ package org.apache.nifi.stateless.core; import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.controller.PropertyConfiguration; import java.util.Collections; import java.util.HashMap; @@ -31,7 +32,7 @@ public class StatelessControllerServiceConfiguration { private final AtomicBoolean enabled = new AtomicBoolean(false); private String annotationData; - private Map properties = new HashMap<>(); + private Map properties = new HashMap<>(); public StatelessControllerServiceConfiguration(final ControllerService service, final String name) { this.service = service; @@ -50,20 +51,20 @@ public class StatelessControllerServiceConfiguration { return this.enabled.get(); } - public void setProperties(final Map props) { + public void setProperties(final Map props) { this.properties = new HashMap<>(props); } - public void setProperty(final PropertyDescriptor key, final String value) { + public void setProperty(final PropertyDescriptor key, final PropertyConfiguration value) { this.properties.put(key, value); } public String getProperty(final PropertyDescriptor descriptor) { - final String value = properties.get(descriptor); + final PropertyConfiguration value = properties.get(descriptor); if (value == null) { return descriptor.getDefaultValue(); } else { - return value; + return value.getRawValue(); } } @@ -75,7 +76,7 @@ public class StatelessControllerServiceConfiguration { return annotationData; } - public Map getProperties() { + public Map getProperties() { return Collections.unmodifiableMap(properties); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceLookup.java index 2544ee52d4..0ab562f3d5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceLookup.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-stateless/src/main/java/org/apache/nifi/stateless/core/StatelessControllerServiceLookup.java @@ -25,7 +25,12 @@ import org.apache.nifi.components.state.StateManager; import org.apache.nifi.controller.ConfigurationContext; import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.controller.PropertyConfiguration; +import org.apache.nifi.parameter.ExpressionLanguageAgnosticParameterParser; +import org.apache.nifi.parameter.ExpressionLanguageAwareParameterParser; import org.apache.nifi.parameter.ParameterContext; +import org.apache.nifi.parameter.ParameterParser; +import org.apache.nifi.parameter.ParameterTokenList; import org.apache.nifi.registry.VariableRegistry; import org.apache.nifi.reporting.InitializationException; @@ -128,10 +133,10 @@ public class StatelessControllerServiceLookup implements ControllerServiceLookup public void enableControllerServices(final VariableRegistry variableRegistry) { for (final StatelessControllerServiceConfiguration config : controllerServiceMap.values()) { final ControllerService service = config.getService(); - final Collection validationResults = validate(service, config.getName(), variableRegistry); + final Collection validationResults = validate(service, config, variableRegistry); if (!validationResults.isEmpty()) { throw new RuntimeException("Failed to enable Controller Service {id=" + service.getIdentifier() + ", name=" + config.getName() + ", type=" + service.getClass() + "} because " + - "validation failed: " + validationResults); + "validation failed: " + validationResults); } try { @@ -142,10 +147,11 @@ public class StatelessControllerServiceLookup implements ControllerServiceLookup } } - public Collection validate(final ControllerService service, final String serviceName, final VariableRegistry variableRegistry) { + public Collection validate(final ControllerService service, final StatelessControllerServiceConfiguration serviceName, final VariableRegistry variableRegistry) { final StateManager stateManager = controllerServiceStateManagers.get(service.getIdentifier()); final SLF4JComponentLog logger = controllerServiceLoggers.get(service.getIdentifier()); - final StatelessProcessContext processContext = new StatelessProcessContext(service, this, serviceName, logger, stateManager, variableRegistry, parameterContext); + final StatelessProcessContext processContext = new StatelessProcessContext(service, this, serviceName.getName(), logger, stateManager, variableRegistry, parameterContext); + serviceName.getProperties().forEach((k,v) -> processContext.setProperty(k,v.getRawValue())); final StatelessValidationContext validationContext = new StatelessValidationContext(processContext, this, stateManager, variableRegistry, parameterContext); return service.validate(validationContext); } @@ -192,18 +198,27 @@ public class StatelessControllerServiceLookup implements ControllerServiceLookup throw new IllegalStateException("Controller service " + service + " has not been added to this TestRunner via the #addControllerService method"); } - final ValidationContext validationContext = new StatelessValidationContext(context, this, serviceStateManager, registry, parameterContext).getControllerServiceValidationContext(service); + final ValidationContext validationContext = new StatelessValidationContext(context, this, serviceStateManager, registry, parameterContext); final ValidationResult validationResult = property.validate(value, validationContext); final StatelessControllerServiceConfiguration configuration = getControllerServiceConfigToUpdate(service); - final String oldValue = configuration.getProperties().get(property); - configuration.setProperty(property, value); + final PropertyConfiguration oldValue = configuration.getProperties().get(property); + final PropertyConfiguration propertyConfiguration = createPropertyConfiguration(value, property.isExpressionLanguageSupported()); + configuration.setProperty(property, propertyConfiguration); - if ((value == null && oldValue != null) || (value != null && !value.equals(oldValue))) { - service.onPropertyModified(property, oldValue, value); + if (oldValue == null && value != null) { + service.onPropertyModified(property, null, value); + } else if ((value == null && oldValue.getRawValue() != null) || (value != null && !value.equals(oldValue.getRawValue()))) { + service.onPropertyModified(property, oldValue.getRawValue(), value); } return validationResult; } + private PropertyConfiguration createPropertyConfiguration(final String value, final boolean supportsEl) { + final ParameterParser parameterParser = supportsEl ? new ExpressionLanguageAwareParameterParser() : new ExpressionLanguageAgnosticParameterParser(); + final ParameterTokenList parameterTokenList = parameterParser.parseTokens(value); + return new PropertyConfiguration(value, parameterTokenList, parameterTokenList.toReferenceList()); + } + }