Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ParameterDTO {
private Boolean valueRemoved;
private Set<AffectedComponentEntity> referencingComponents;
private ParameterContextReferenceEntity parameterContext;
private Boolean inherited;

@ApiModelProperty("The name of the Parameter")
public String getName() {
Expand All @@ -42,6 +43,15 @@ public void setName(final String name) {
this.name = name;
}

@ApiModelProperty(value = "Whether or not the Parameter is inherited from another context", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public Boolean getInherited() {
return inherited;
}

public void setInherited(final Boolean inherited) {
this.inherited = inherited;
}

@ApiModelProperty("The description of the Parameter")
public String getDescription() {
return description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -123,7 +124,7 @@ public void setParameters(final Map<String, Parameter> updatedParameters) {

final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);

verifyCanSetParameters(effectiveParameterUpdates);
verifyCanSetParameters(effectiveParameterUpdates, true);

// Update the actual parameters
updateParameters(parameters, updatedParameters, true);
Expand Down Expand Up @@ -339,6 +340,17 @@ public Map<ParameterDescriptor, Parameter> getEffectiveParameters() {
}
}

@Override
public Map<String, Parameter> getEffectiveParameterUpdates(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts) {
Objects.requireNonNull(parameterUpdates, "Parameter Updates must be specified");
Objects.requireNonNull(inheritedParameterContexts, "Inherited parameter contexts must be specified");

final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts, getProposedParameters(parameterUpdates), new HashMap<>());

return getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
}

/**
* Constructs an effective view of the parameters, including nested parameters, assuming the given map of parameters.
* This allows an inspection of what parameters would be available if the given parameters were set in this ParameterContext.
Expand Down Expand Up @@ -373,15 +385,23 @@ private Map<ParameterDescriptor, Parameter> getEffectiveParameters(final List<Pa
// Loop backwards so that the first ParameterContext in the list will override any parameters later in the list
for(int i = parameterContexts.size() - 1; i >= 0; i--) {
ParameterContext parameterContext = parameterContexts.get(i);
allOverrides.putAll(overrideParameters(effectiveParameters, parameterContext.getEffectiveParameters(), parameterContext));
combineOverrides(allOverrides, overrideParameters(effectiveParameters, parameterContext.getEffectiveParameters(), parameterContext));
}

// Finally, override all child parameters with our own
allOverrides.putAll(overrideParameters(effectiveParameters, proposedParameters, this));
combineOverrides(allOverrides, overrideParameters(effectiveParameters, proposedParameters, this));

return effectiveParameters;
}

private void combineOverrides(final Map<ParameterDescriptor, List<Parameter>> existingOverrides, final Map<ParameterDescriptor, List<Parameter>> newOverrides) {
for (final Map.Entry<ParameterDescriptor, List<Parameter>> entry : newOverrides.entrySet()) {
final ParameterDescriptor key = entry.getKey();
final List<Parameter> existingOverrideList = existingOverrides.computeIfAbsent(key, k -> new ArrayList<>());
existingOverrideList.addAll(entry.getValue());
}
}

private Map<ParameterDescriptor, List<Parameter>> overrideParameters(final Map<ParameterDescriptor, Parameter> existingParameters,
final Map<ParameterDescriptor, Parameter> overridingParameters,
final ParameterContext overridingContext) {
Expand Down Expand Up @@ -456,32 +476,49 @@ private void verifyNoCycles(final Stack<String> traversedIds, final List<Paramet
}
}

@Override
public void verifyCanUpdateParameterContext(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts) {
verifyCanUpdateParameterContext(parameterUpdates, inheritedParameterContexts, false);
}

private void verifyCanUpdateParameterContext(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts, final boolean duringUpdate) {
if (inheritedParameterContexts == null) {
return;
}
verifyNoCycles(inheritedParameterContexts);

final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts, getProposedParameters(parameterUpdates), new HashMap<>());
final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);

try {
verifyCanSetParameters(currentEffectiveParameters, effectiveParameterUpdates, duringUpdate);
} catch (final IllegalStateException e) {
// Wrap with a more accurate message
throw new IllegalStateException(String.format("Could not update inherited Parameter Contexts for Parameter Context [%s] because: %s",
name, e.getMessage()), e);
}
}

@Override
public void setInheritedParameterContexts(final List<ParameterContext> inheritedParameterContexts) {
if (inheritedParameterContexts.equals(this.inheritedParameterContexts)) {
if (inheritedParameterContexts == null || inheritedParameterContexts.equals(this.inheritedParameterContexts)) {
// No changes
return;
}

verifyCanUpdateParameterContext(Collections.emptyMap(), inheritedParameterContexts, true);

final Map<String, ParameterUpdate> parameterUpdates = new HashMap<>();

writeLock.lock();
try {
this.version++;
verifyNoCycles(inheritedParameterContexts);

final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts);
final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);

try {
verifyCanSetParameters(currentEffectiveParameters, effectiveParameterUpdates);
} catch (final IllegalStateException e) {
// Wrap with a more accurate message
throw new IllegalStateException(String.format("Could not update inherited Parameter Contexts for Parameter Context [%s] because: %s",
name, e.getMessage()), e);
}

this.inheritedParameterContexts.clear();

this.inheritedParameterContexts.addAll(inheritedParameterContexts);
Expand Down Expand Up @@ -570,17 +607,30 @@ public boolean inheritsFrom(final String parameterContextId) {

@Override
public void verifyCanSetParameters(final Map<String, Parameter> updatedParameters) {
verifyCanSetParameters(parameters, updatedParameters);
verifyCanSetParameters(updatedParameters, false);
}

public void verifyCanSetParameters(final Map<ParameterDescriptor, Parameter> currentParameters, final Map<String, Parameter> updatedParameters) {
/**
* Ensures that it is legal to update the Parameters for this Parameter Context to match the given set of Parameters
* @param updatedParameters the updated set of parameters, keyed by Parameter name
* @param duringUpdate If true, this check will be treated as if a ParameterContext update is imminent, meaning
* referencing components may not be active even for updated values. If false, a parameter
* value update will be valid even if referencing components are active because it will be
* assumed that these will be stopped prior to the actual update.
* @throws IllegalStateException if setting the given set of Parameters is not legal
*/
public void verifyCanSetParameters(final Map<String, Parameter> updatedParameters, final boolean duringUpdate) {
verifyCanSetParameters(parameters, updatedParameters, duringUpdate);
}

public void verifyCanSetParameters(final Map<ParameterDescriptor, Parameter> currentParameters, final Map<String, Parameter> updatedParameters, final boolean duringUpdate) {
// Ensure that the updated parameters will not result in changing the sensitivity flag of any parameter.
for (final Map.Entry<String, Parameter> entry : updatedParameters.entrySet()) {
final String parameterName = entry.getKey();
final Parameter parameter = entry.getValue();
if (parameter == null) {
// parameter is being deleted.
validateReferencingComponents(parameterName, null,"remove");
validateReferencingComponents(parameterName, null, duringUpdate);
continue;
}

Expand All @@ -589,7 +639,7 @@ public void verifyCanSetParameters(final Map<ParameterDescriptor, Parameter> cur
}

validateSensitiveFlag(currentParameters, parameter);
validateReferencingComponents(parameterName, parameter, "update");
validateReferencingComponents(parameterName, parameter, duringUpdate);
}
}

Expand All @@ -611,11 +661,12 @@ private void validateSensitiveFlag(final Map<ParameterDescriptor, Parameter> cur
}
}


private void validateReferencingComponents(final String parameterName, final Parameter parameter, final String parameterAction) {
private void validateReferencingComponents(final String parameterName, final Parameter parameter, final boolean duringUpdate) {
final boolean isDeletion = (parameter == null);
final String action = isDeletion ? "remove" : "update";
for (final ProcessorNode procNode : parameterReferenceManager.getProcessorsReferencing(this, parameterName)) {
if (procNode.isRunning()) {
throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by " + procNode + ", which is currently running");
if (procNode.isRunning() && (isDeletion || duringUpdate)) {
throw new IllegalStateException("Cannot " + action + " parameter '" + parameterName + "' because it is referenced by " + procNode + ", which is currently running");
}

if (parameter != null) {
Expand All @@ -625,9 +676,9 @@ private void validateReferencingComponents(final String parameterName, final Par

for (final ControllerServiceNode serviceNode : parameterReferenceManager.getControllerServicesReferencing(this, parameterName)) {
final ControllerServiceState serviceState = serviceNode.getState();
if (serviceState != ControllerServiceState.DISABLED) {
throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by "
+ serviceNode + ", which currently has a state of " + serviceState);
if (serviceState != ControllerServiceState.DISABLED && (isDeletion || duringUpdate)) {
throw new IllegalStateException("Cannot " + action + " parameter '" + parameterName + "' because it is referenced by "
+ serviceNode + ", which currently has a state of " + serviceState);
}

if (parameter != null) {
Expand Down Expand Up @@ -678,6 +729,22 @@ public void authorize(final Authorizer authorizer, final RequestAction action, f
}
}

@Override
public boolean isAuthorized(final Authorizer authorizer, final RequestAction action, final NiFiUser user) {
boolean isAuthorized = ParameterContext.super.isAuthorized(authorizer, action, user);

if (RequestAction.READ == action) {
for (final ParameterContext parameterContext : inheritedParameterContexts) {
isAuthorized &= parameterContext.isAuthorized(authorizer, action, user);
if (!isAuthorized) {
break;
}
}
}

return isAuthorized;
}

@Override
public Authorizable getParentAuthorizable() {
return new Authorizable() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,29 @@ public interface ParameterContext extends ParameterLookup, ComponentAuthorizable
*/
Map<ParameterDescriptor, Parameter> getEffectiveParameters();

/**
* Returns the resulting map of effective parameter updates if the given parameter updates and inherited parameter contexts were to be applied.
* This allows potential changes to be detected before actually applying the parameter updates.
* @param parameterUpdates A map from parameter name to updated parameter (null if removal is desired)
* @param inheritedParameterContexts An ordered list of parameter contexts to inherit from
* @return The effective map of parameter updates that would result if these changes were applied. This includes only parameters that would
* be effectively updated or removed, and is mapped by parameter name
*/
Map<String, Parameter> getEffectiveParameterUpdates(Map<String, Parameter> parameterUpdates, List<ParameterContext> inheritedParameterContexts);

/**
* Returns the ParameterReferenceManager that is associated with this ParameterContext
* @return the ParameterReferenceManager that is associated with this ParameterContext
*/
ParameterReferenceManager getParameterReferenceManager();

/**
* Verifies whether the parameter context can be updated with the provided parameters and inherited parameter contexts.
* @param parameterUpdates A map from parameter name to updated parameter (null if removal is desired)
* @param inheritedParameterContexts the list of ParameterContexts from which to inherit parameters
*/
void verifyCanUpdateParameterContext(Map<String, Parameter> parameterUpdates, List<ParameterContext> inheritedParameterContexts);

/**
* Updates the ParameterContexts within this context to match the given list of ParameterContexts. All parameter in these
* ParameterContexts are inherited by this ParameterContext, and can be referenced as if they were actually in this ParameterContext.
Expand All @@ -125,7 +142,7 @@ public interface ParameterContext extends ParameterLookup, ComponentAuthorizable
*
* @param inheritedParameterContexts the list of ParameterContexts from which to inherit parameters, in priority order first to last
* @throws IllegalStateException if the list of ParameterContexts is invalid (in case of a circular reference or
* in case {@link #verifyCanSetParameters(Map)} verifyCanSetParameters} would throw an exception)
* in case {@link #verifyCanSetParameters(Map) verifyCanSetParameters} would throw an exception)
*/
void setInheritedParameterContexts(List<ParameterContext> inheritedParameterContexts);

Expand Down
Loading