blob: f4bea5e264c04922262428cddf605a9be0e95f93 [file] [log] [blame]
/*
* 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.components;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public abstract class AbstractConfigurableComponent implements ConfigurableComponent {
/**
* Allows subclasses to perform their own validation on the already set
* properties. Since each property is validated as it is set this allows
* validation of groups of properties together. Default return is an empty
* set.
*
* This method will be called only when it has been determined that all
* property values are valid according to their corresponding
* PropertyDescriptor's validators.
*
* @param validationContext provides a mechanism for obtaining externally
* managed values, such as property values and supplies convenience methods
* for operating on those values
*
* @return Collection of ValidationResult objects that will be added to any
* other validation findings - may be null
*/
protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
return Collections.emptySet();
}
/**
* Returns a PropertyDescriptor for the name specified that is fully
* populated
*
* @param descriptorName
* @return
*/
@Override
public final PropertyDescriptor getPropertyDescriptor(final String descriptorName) {
final PropertyDescriptor specDescriptor = new PropertyDescriptor.Builder().name(descriptorName).build();
return getPropertyDescriptor(specDescriptor);
}
private PropertyDescriptor getPropertyDescriptor(final PropertyDescriptor specDescriptor) {
PropertyDescriptor descriptor = null;
//check if property supported
final List<PropertyDescriptor> supportedDescriptors = getSupportedPropertyDescriptors();
if (supportedDescriptors != null) {
for (final PropertyDescriptor desc : supportedDescriptors) { //find actual descriptor
if (specDescriptor.equals(desc)) {
return desc;
}
}
}
descriptor = getSupportedDynamicPropertyDescriptor(specDescriptor.getName());
if (descriptor != null && !descriptor.isDynamic()) {
descriptor = new PropertyDescriptor.Builder().fromPropertyDescriptor(descriptor).dynamic(true).build();
}
if (descriptor == null) {
descriptor = new PropertyDescriptor.Builder().fromPropertyDescriptor(specDescriptor).addValidator(Validator.INVALID).dynamic(true).build();
}
return descriptor;
}
@Override
public final Collection<ValidationResult> validate(final ValidationContext context) {
// goes through supported properties
final Collection<ValidationResult> results = new ArrayList<>();
final List<PropertyDescriptor> supportedDescriptors = getSupportedPropertyDescriptors();
if (null != supportedDescriptors) {
for (final PropertyDescriptor descriptor : supportedDescriptors) {
String value = context.getProperty(descriptor).getValue();
if (value == null) {
value = descriptor.getDefaultValue();
}
if (value == null && descriptor.isRequired()) {
results.add(new ValidationResult.Builder().valid(false).input(null).subject(descriptor.getName()).explanation(descriptor.getName() + " is required").build());
continue;
} else if (value == null) {
continue;
}
final ValidationResult result = descriptor.validate(value, context);
if (!result.isValid()) {
results.add(result);
}
}
}
// validate any dynamic properties
for (final Map.Entry<PropertyDescriptor, String> entry : context.getProperties().entrySet()) {
final PropertyDescriptor descriptor = entry.getKey();
final String value = entry.getValue();
if (supportedDescriptors != null && !supportedDescriptors.contains(descriptor)) {
final ValidationResult result = descriptor.validate(value, context);
if (!result.isValid()) {
results.add(result);
}
}
}
// only run customValidate if regular validation is successful. This allows Processor developers to not have to check
// if values are null or invalid so that they can focus only on the interaction between the properties, etc.
if (results.isEmpty()) {
final Collection<ValidationResult> customResults = customValidate(context);
if (null != customResults) {
for (final ValidationResult result : customResults) {
if (!result.isValid()) {
results.add(result);
}
}
}
}
return results;
}
/**
* Hook method allowing subclasses to eagerly react to a configuration
* change for the given property descriptor. As an alternative to using this
* method a processor may simply get the latest value whenever it needs it
* and if necessary lazily evaluate it.
*
* @param descriptor
* @param oldValue non-null property value (previous)
* @param newValue the new property value or if null indicates the property
* was removed
*/
public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
}
/**
* <p>
* Used to allow subclasses to determine what PropertyDescriptor if any to
* use when a property is requested for which a descriptor is not already
* registered. By default this method simply returns a null descriptor. By
* overriding this method processor implementations can support dynamic
* properties since this allows them to register properties on demand. It is
* acceptable for a dynamically generated property to indicate it is
* required so long as it is understood it is only required once set.
* Dynamic properties by definition cannot be required until used.</p>
*
* <p>
* This method should be side effect free in the subclasses in terms of how
* often it is called for a given property name because there is guarantees
* how often it will be called for a given property name.</p>
*
* <p>
* Default is null.
*
* @param propertyDescriptorName
* @return new property descriptor if supported
*/
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
return null;
}
/**
* Allows subclasses to register which property descriptor objects are
* supported. Default return is an empty set.
*
* @return PropertyDescriptor objects this processor currently supports
*/
@SuppressWarnings("unchecked")
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return Collections.EMPTY_LIST;
}
public final List<PropertyDescriptor> getPropertyDescriptors() {
final List<PropertyDescriptor> descriptors = new ArrayList<>();
descriptors.addAll(getSupportedPropertyDescriptors());
return descriptors;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ConfigurableComponent)) {
return false;
}
final ConfigurableComponent other = (ConfigurableComponent) obj;
return getIdentifier().equals(other.getIdentifier());
}
@Override
public int hashCode() {
return 235 + getIdentifier().hashCode();
}
@Override
public String toString() {
return getClass().getSimpleName() + "[id=" + getIdentifier() + "]";
}
}