NIFI-7380 - fix for controller service validation in NiFi Stateless

This closes #4264.

Signed-off-by: Matthieu Cauffiez <matthieu.cauffiez@bell.ca>
Signed-off-by: Mark Payne <markap14@hotmail.com>
This commit is contained in:
Matthieu Cauffiez 2020-05-11 10:59:40 -04:00 committed by Mark Payne
parent a3cc2c58ff
commit 179675f0b4
3 changed files with 58 additions and 28 deletions

View File

@ -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<PropertyDescriptor, String> properties;
private final Map<PropertyDescriptor, PropertyConfiguration> 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<PropertyDescriptor, String> properties,
final Map<PropertyDescriptor, PropertyConfiguration> 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<PropertyDescriptor, String> getProperties() {
return new HashMap<>(this.properties);
final List<PropertyDescriptor> supported = service.getPropertyDescriptors();
final Map<PropertyDescriptor, String> effectiveValues = new LinkedHashMap<>();
for (final PropertyDescriptor descriptor : supported) {
effectiveValues.put(descriptor, null);
}
for (final Map.Entry<PropertyDescriptor, PropertyConfiguration> 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

View File

@ -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<PropertyDescriptor, String> properties = new HashMap<>();
private Map<PropertyDescriptor, PropertyConfiguration> 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<PropertyDescriptor, String> props) {
public void setProperties(final Map<PropertyDescriptor, PropertyConfiguration> 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<PropertyDescriptor, String> getProperties() {
public Map<PropertyDescriptor, PropertyConfiguration> getProperties() {
return Collections.unmodifiableMap(properties);
}

View File

@ -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<ValidationResult> validationResults = validate(service, config.getName(), variableRegistry);
final Collection<ValidationResult> 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<ValidationResult> validate(final ControllerService service, final String serviceName, final VariableRegistry variableRegistry) {
public Collection<ValidationResult> 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());
}
}