exceptionfactory commented on code in PR #5369: URL: https://github.com/apache/nifi/pull/5369#discussion_r957437253
########## nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/parameter/StandardParameterProviderNode.java: ########## @@ -0,0 +1,609 @@ +/* + * 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.controller.parameter; + +import org.apache.nifi.annotation.behavior.Restricted; +import org.apache.nifi.annotation.documentation.DeprecationNotice; +import org.apache.nifi.authorization.Resource; +import org.apache.nifi.authorization.resource.Authorizable; +import org.apache.nifi.authorization.resource.ResourceFactory; +import org.apache.nifi.authorization.resource.ResourceType; +import org.apache.nifi.bundle.Bundle; +import org.apache.nifi.bundle.BundleCoordinate; +import org.apache.nifi.components.ClassloaderIsolationKeyProvider; +import org.apache.nifi.components.ConfigVerificationResult; +import org.apache.nifi.components.ConfigVerificationResult.Outcome; +import org.apache.nifi.components.ConfigurableComponent; +import org.apache.nifi.components.ValidationContext; +import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.components.validation.ValidationStatus; +import org.apache.nifi.components.validation.ValidationTrigger; +import org.apache.nifi.controller.AbstractComponentNode; +import org.apache.nifi.controller.ConfigurationContext; +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.controller.LoggableComponent; +import org.apache.nifi.controller.ParameterProviderNode; +import org.apache.nifi.controller.ParametersApplication; +import org.apache.nifi.controller.ReloadComponent; +import org.apache.nifi.controller.TerminationAwareLogger; +import org.apache.nifi.controller.ValidationContextFactory; +import org.apache.nifi.controller.service.ControllerServiceProvider; +import org.apache.nifi.controller.service.StandardConfigurationContext; +import org.apache.nifi.logging.ComponentLog; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.InstanceClassLoader; +import org.apache.nifi.nar.NarCloseable; +import org.apache.nifi.parameter.Parameter; +import org.apache.nifi.parameter.ParameterContext; +import org.apache.nifi.parameter.ParameterDescriptor; +import org.apache.nifi.parameter.ParameterGroup; +import org.apache.nifi.parameter.ParameterGroupConfiguration; +import org.apache.nifi.parameter.ParameterLookup; +import org.apache.nifi.parameter.ParameterProvider; +import org.apache.nifi.parameter.ParameterSensitivity; +import org.apache.nifi.parameter.VerifiableParameterProvider; +import org.apache.nifi.registry.ComponentVariableRegistry; +import org.apache.nifi.util.CharacterFilterUtils; +import org.apache.nifi.util.FormatUtils; +import org.apache.nifi.util.file.classloader.ClassLoaderUtils; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class StandardParameterProviderNode extends AbstractComponentNode implements ParameterProviderNode { + + private static final Pattern PARAMETER_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9-_. ]+$"); + + private final AtomicReference<ParameterProviderDetails> parameterProviderRef; + private final ControllerServiceLookup serviceLookup; + + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + private final Lock readLock = rwLock.readLock(); + private final Lock writeLock = rwLock.writeLock(); + + private final Set<ParameterContext> referencingParameterContexts; + + private final List<ParameterGroup> fetchedParameterGroups = new ArrayList<>(); + + private volatile String comment; + + private final Authorizable parentAuthorizable; + + public StandardParameterProviderNode(final LoggableComponent<ParameterProvider> parameterProvider, final String id, final Authorizable parentAuthorizable, + final ControllerServiceProvider controllerServiceProvider, final ValidationContextFactory validationContextFactory, + final ComponentVariableRegistry variableRegistry, final ReloadComponent reloadComponent, final ExtensionManager extensionManager, + final ValidationTrigger validationTrigger) { + + this(parameterProvider, id, parentAuthorizable, controllerServiceProvider, validationContextFactory, + parameterProvider.getComponent().getClass().getSimpleName(), parameterProvider.getComponent().getClass().getCanonicalName(), + variableRegistry, reloadComponent, extensionManager, validationTrigger, false); + } + + public StandardParameterProviderNode(final LoggableComponent<ParameterProvider> parameterProvider, final String id, final Authorizable parentAuthorizable, + final ControllerServiceProvider controllerServiceProvider, final ValidationContextFactory validationContextFactory, + final String componentType, final String canonicalClassName, final ComponentVariableRegistry variableRegistry, + final ReloadComponent reloadComponent, final ExtensionManager extensionManager, final ValidationTrigger validationTrigger, final boolean isExtensionMissing) { + super(id, validationContextFactory, controllerServiceProvider, componentType, canonicalClassName, variableRegistry, reloadComponent, + extensionManager, validationTrigger, isExtensionMissing); + this.parameterProviderRef = new AtomicReference<>(new ParameterProviderDetails(parameterProvider)); + this.serviceLookup = controllerServiceProvider; + this.referencingParameterContexts = new HashSet<>(); + this.parentAuthorizable = parentAuthorizable; + } + + @Override + public Authorizable getParentAuthorizable() { + return parentAuthorizable; + } + + @Override + public Resource getResource() { + return ResourceFactory.getComponentResource(ResourceType.ParameterProvider, getIdentifier(), getName()); + } + + @Override + public boolean isRestricted() { + return getParameterProvider().getClass().isAnnotationPresent(Restricted.class); + } + + @Override + public Class<?> getComponentClass() { + return getParameterProvider().getClass(); + } + + @Override + public boolean isDeprecated() { + return getParameterProvider().getClass().isAnnotationPresent(DeprecationNotice.class); + } + + @Override + protected ParameterContext getParameterContext() { + return null; + } + @Override + public ConfigurableComponent getComponent() { + return parameterProviderRef.get().getParameterProvider(); + } + + @Override + public BundleCoordinate getBundleCoordinate() { + return parameterProviderRef.get().getBundleCoordinate(); + } + + @Override + public TerminationAwareLogger getLogger() { + return parameterProviderRef.get().getComponentLog(); + } + + @Override + public ParameterProvider getParameterProvider() { + return parameterProviderRef.get().getParameterProvider(); + } + + @Override + public void setParameterProvider(final LoggableComponent<ParameterProvider> parameterProvider) { + this.parameterProviderRef.set(new ParameterProviderDetails(parameterProvider)); + } + + @Override + public void reload(final Set<URL> additionalUrls) throws ParameterProviderInstantiationException { + final String additionalResourcesFingerprint = ClassLoaderUtils.generateAdditionalUrlsFingerprint(additionalUrls, determineClasloaderIsolationKey()); + setAdditionalResourcesFingerprint(additionalResourcesFingerprint); + getReloadComponent().reload(this, getCanonicalClassName(), getBundleCoordinate(), additionalUrls); + } + + @Override + public boolean isValidationNecessary() { + return true; + } + + @Override + public ConfigurationContext getConfigurationContext() { + return new StandardConfigurationContext(this, serviceLookup, null, getVariableRegistry()); + } + + @Override + public void verifyModifiable() throws IllegalStateException { + + } + + @Override + public String getComments() { + return comment; + } + + @Override + public void setComments(final String comment) { + this.comment = CharacterFilterUtils.filterInvalidXmlCharacters(comment); + } + + @Override + public void verifyCanClearState() { + + } + + @Override + public String toString() { + return "ParameterProvider[id=" + getIdentifier() + "]"; + } + + @Override + public String getProcessGroupIdentifier() { + return null; + } + + @Override + public ParameterLookup getParameterLookup() { + return ParameterLookup.EMPTY; + } + + @Override + public Set<ParameterContext> getReferences() { + readLock.lock(); + try { + return Collections.unmodifiableSet(referencingParameterContexts); + } finally { + readLock.unlock(); + } + } + + @Override + public void addReference(final ParameterContext reference) { + writeLock.lock(); + try { + referencingParameterContexts.add(reference); + } finally { + writeLock.unlock(); + } + } + + @Override + public void removeReference(final ParameterContext reference) { + writeLock.lock(); + try { + referencingParameterContexts.remove(reference); + } finally { + writeLock.unlock(); + } + } + + @Override + protected List<ValidationResult> validateConfig() { + return Collections.emptyList(); + } + + @Override + public void verifyCanFetchParameters() { + final ValidationStatus validationStatus = performValidation(); + if (validationStatus != ValidationStatus.VALID) { + throw new IllegalStateException(String.format("Parameter Provider [%s] cannot fetch parameters while validation state is %s", + getIdentifier(), validationStatus)); + } + } + + @Override + public void fetchParameters() { + final ParameterProvider parameterProvider = parameterProviderRef.get().getParameterProvider(); + final ConfigurationContext configurationContext = getConfigurationContext(); + List<ParameterGroup> fetchedParameterGroups; + try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(getExtensionManager(), parameterProvider.getClass(), parameterProvider.getIdentifier())) { + fetchedParameterGroups = parameterProvider.fetchParameters(configurationContext); + } catch (final IOException e) { + throw new RuntimeException(String.format("Error fetching parameters for Parameter Provider [%s]", getName()), e); + } + + if (fetchedParameterGroups == null || fetchedParameterGroups.isEmpty()) { + return; + } + + final Set<String> parameterGroupNames = new HashSet<>(); + + writeLock.lock(); + try { + this.fetchedParameterGroups.clear(); + for (final ParameterGroup group : fetchedParameterGroups) { + final String groupName = group.getGroupName(); + if (parameterGroupNames.contains(groupName)) { + throw new IllegalStateException(String.format("Parameter group [%s] is provided twice, which is not allowed", groupName)); + } + final Collection<Parameter> parameters = group.getParameters(); + + if (parameters == null) { + continue; + } + final List<Parameter> validParameters = new ArrayList<>(); + final Set<String> parameterNames = new HashSet<>(); + for (final Parameter parameter : parameters) { + final ParameterDescriptor descriptor = parameter.getDescriptor(); + if (descriptor == null) { + throw new IllegalStateException("All fetched parameters require a ParameterDescriptor"); + } + final String parameterName = descriptor.getName(); + if (parameterNames.contains(parameterName)) { + throw new IllegalStateException(String.format("Parameter [%s] is provided in group [%s] twice, which is not allowed", + parameterName, groupName)); + } + + if (parameter.getValue() == null) { + getLogger().warn("Skipping parameter [{}], which is missing a value", new Object[] {parameterName}); Review Comment: `TerminationAwareLogger` implements `ComponentLog`, which supports variable arguments, even though the implementation methods do not explicitly implement those signatures, so it should work unless I am missing something. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
