NIFI-8491: Adding support for configuring parameter context inheritance (#5371)

* NIFI-8491:
- Adding support for configuring parameter context inheritance.

* NIFI-8491:
- Allowing changes to the parameter context inheritance to drive Apply disabled state.

* NIFI-8491: Updating StandardParameterContext#isAuthorized check

* NIFI-8491:
- Showing selected inherited parameter contexts in ready only form when appropriate.
- Allowing available parameter contexts to be inherited by double clicking.
- Removing support for rendering unauthorized inherited parameter contexts as they can no longer be opened.

* NIFI-8491: Adding inherited param context verification earlier

* NIFI-8491:
- Addressing CI failures by rolling back to some order JS language spec to allow yui-compress to minify and compress.

* NIFI-8491:
- Ensuring selected context sort order is honored.
- Ensuring the Apply button is correctly enabled.
- Showing Pending Apply message when selected Parameter Context changes.
- Ensuring the Parameter's tab is selected now that there is a third tab.

* Updates to inherited param context verification

* Improving validation between parameters/inherited parameters

* NIFI-8491:
- Ensuring the available parameter contexts are loaded whether the edit dialog is opened from the listing or outside of the listing.

* NIFI-8491:
- Fixing conditions we check if the parameter context listing is currently open.

* NIFI-8491:
- Waiting for the parameter contexts to load prior to rendering the parameter context inheritance tab and showing the dialog.

* NIFI-8491:
- Fixing pending apply message clipping.
- Hiding pending apply message after clicking Apply.

Co-authored-by: Joe Gresock <jgresock@gmail.com>

This closes #5371 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
index 12a0262..86701b5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
@@ -32,6 +32,7 @@
     private Boolean valueRemoved;
     private Set<AffectedComponentEntity> referencingComponents;
     private ParameterContextReferenceEntity parameterContext;
+    private Boolean inherited;
 
     @ApiModelProperty("The name of the Parameter")
     public String getName() {
@@ -42,6 +43,15 @@
         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;
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
index e311770..51e5664 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
@@ -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;
@@ -123,7 +124,7 @@
 
             final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
 
-            verifyCanSetParameters(effectiveParameterUpdates);
+            verifyCanSetParameters(effectiveParameterUpdates, true);
 
             // Update the actual parameters
             updateParameters(parameters, updatedParameters, true);
@@ -339,6 +340,17 @@
         }
     }
 
+    @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.
@@ -373,15 +385,23 @@
         // 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) {
@@ -457,31 +477,48 @@
     }
 
     @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);
@@ -570,17 +607,30 @@
 
     @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;
             }
 
@@ -589,7 +639,7 @@
             }
 
             validateSensitiveFlag(currentParameters, parameter);
-            validateReferencingComponents(parameterName, parameter, "update");
+            validateReferencingComponents(parameterName, parameter, duringUpdate);
         }
     }
 
@@ -611,11 +661,12 @@
         }
     }
 
-
-    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) {
@@ -625,9 +676,9 @@
 
         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) {
@@ -679,6 +730,22 @@
     }
 
     @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() {
             @Override
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
index 670c9ce..62641a6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
@@ -110,12 +110,29 @@
     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.
      * The order of the list specifies the priority of parameter overriding, where parameters in the first ParameterContext in the list have
@@ -125,7 +142,7 @@
      *
      * @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);
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index cfe7941..4d31a11 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -91,6 +91,14 @@
 import org.apache.nifi.controller.status.ProcessorStatus;
 import org.apache.nifi.diagnostics.SystemDiagnostics;
 import org.apache.nifi.events.BulletinFactory;
+import org.apache.nifi.flow.VersionedComponent;
+import org.apache.nifi.flow.VersionedConfigurableComponent;
+import org.apache.nifi.flow.VersionedConnection;
+import org.apache.nifi.flow.VersionedControllerService;
+import org.apache.nifi.flow.VersionedFlowCoordinates;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.flow.VersionedProcessor;
+import org.apache.nifi.flow.VersionedPropertyDescriptor;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.ProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroup;
@@ -119,19 +127,11 @@
 import org.apache.nifi.registry.flow.FlowRegistryClient;
 import org.apache.nifi.registry.flow.RestBasedFlowRegistry;
 import org.apache.nifi.registry.flow.VersionControlInformation;
-import org.apache.nifi.flow.VersionedComponent;
-import org.apache.nifi.flow.VersionedConfigurableComponent;
-import org.apache.nifi.flow.VersionedConnection;
-import org.apache.nifi.flow.VersionedControllerService;
 import org.apache.nifi.registry.flow.VersionedFlow;
-import org.apache.nifi.flow.VersionedFlowCoordinates;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
 import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
 import org.apache.nifi.registry.flow.VersionedFlowState;
 import org.apache.nifi.registry.flow.VersionedParameterContext;
-import org.apache.nifi.flow.VersionedProcessGroup;
-import org.apache.nifi.flow.VersionedProcessor;
-import org.apache.nifi.flow.VersionedPropertyDescriptor;
 import org.apache.nifi.registry.flow.diff.ComparableDataFlow;
 import org.apache.nifi.registry.flow.diff.ConciseEvolvingDifferenceDescriptor;
 import org.apache.nifi.registry.flow.diff.DifferenceType;
@@ -1315,6 +1315,8 @@
             group -> group.getParameterContext() != null && (group.getParameterContext().getIdentifier().equals(parameterContextDto.getId())
                     || group.getParameterContext().inheritsFrom(parameterContext.getIdentifier())));
 
+        setEffectiveParameterUpdates(parameterContextDto);
+
         final Set<String> updatedParameterNames = getUpdatedParameterNames(parameterContextDto);
 
         // Clear set of Affected Components for each Parameter. This parameter is read-only and it will be populated below.
@@ -1371,6 +1373,39 @@
         return dtoFactory.createAffectedComponentEntities(affectedComponents, revisionManager);
     }
 
+    private void setEffectiveParameterUpdates(final ParameterContextDTO parameterContextDto) {
+        final ParameterContext parameterContext = parameterContextDAO.getParameterContext(parameterContextDto.getId());
+
+        final Map<String, Parameter> parameterUpdates = parameterContextDAO.getParameters(parameterContextDto, parameterContext);
+        final List<ParameterContext> inheritedParameterContexts = parameterContextDAO.getInheritedParameterContexts(parameterContextDto);
+        final Map<String, Parameter> proposedParameterUpdates = parameterContext.getEffectiveParameterUpdates(parameterUpdates, inheritedParameterContexts);
+        final Map<String, ParameterEntity> parameterEntities = parameterContextDto.getParameters().stream()
+                .collect(Collectors.toMap(entity -> entity.getParameter().getName(), Function.identity()));
+        parameterContextDto.getParameters().clear();
+
+        for (final Map.Entry<String, Parameter> entry : proposedParameterUpdates.entrySet()) {
+            final String parameterName = entry.getKey();
+            final Parameter parameter = entry.getValue();
+            final ParameterEntity parameterEntity;
+            if (parameterEntities.containsKey(parameterName)) {
+                parameterEntity = parameterEntities.get(parameterName);
+            } else if (parameter == null) {
+                parameterEntity = new ParameterEntity();
+                final ParameterDTO parameterDTO = new ParameterDTO();
+                parameterDTO.setName(parameterName);
+                parameterEntity.setParameter(parameterDTO);
+            } else {
+                parameterEntity = dtoFactory.createParameterEntity(parameterContext, parameter, revisionManager, parameterContextDAO);
+            }
+
+            // Parameter is inherited if either this is the removal of a parameter not directly in this context, or it's parameter not specified directly in the DTO
+            final boolean isInherited = (parameter == null && !parameterContext.getParameters().containsKey(new ParameterDescriptor.Builder().name(parameterName).build()))
+                    || (parameter != null && !parameterEntities.containsKey(parameterName));
+            parameterEntity.getParameter().setInherited(isInherited);
+            parameterContextDto.getParameters().add(parameterEntity);
+        }
+    }
+
     private void addReferencingComponents(final ControllerServiceNode service, final Set<ComponentNode> affectedComponents, final List<ParameterDTO> affectedParameterDtos,
                                           final boolean includeInactive) {
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
index 1d6d8bb..9d5ad9e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
@@ -575,7 +575,7 @@
         final ParameterContextEntity entity = new ParameterContextEntity();
         entity.setRevision(revision);
         if (dto != null) {
-            entity.setPermissions(permissions);;
+            entity.setPermissions(permissions);
             entity.setId(dto.getId());
 
             if (permissions != null && permissions.getCanRead()) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
index 07daab6..51f6d96 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
@@ -16,10 +16,13 @@
  */
 package org.apache.nifi.web.dao;
 
+import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterContextLookup;
 import org.apache.nifi.web.api.dto.ParameterContextDTO;
 
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 public interface ParameterContextDAO extends ParameterContextLookup {
@@ -40,6 +43,14 @@
     ParameterContext createParameterContext(ParameterContextDTO parameterContextDto);
 
     /**
+     * Returns a map from parameter name to intended parameter, given the DTO.
+     * @param parameterContextDto A parameter context DTO containing parameter updates
+     * @param context The existing parameter context
+     * @return The resulting parameter map containing updated parameters (or removals)
+     */
+    Map<String, Parameter> getParameters(ParameterContextDTO parameterContextDto, ParameterContext context);
+
+    /**
      * Gets all of the parameter contexts.
      *
      * @return The parameter contexts
@@ -55,6 +66,13 @@
     ParameterContext updateParameterContext(ParameterContextDTO parameterContextDto);
 
     /**
+     * Returns a list of the inherited parameter contexts proposed by the DTO.
+     * @param parameterContextDto The parameter context DTO
+     * @return a list of the inherited parameter contexts proposed by the DTO
+     */
+    List<ParameterContext> getInheritedParameterContexts(ParameterContextDTO parameterContextDto);
+
+    /**
      * Determines whether this parameter context can be updated.
      *
      * @param parameterContextDto dto
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
index 1c8c3a2..d4a69ac 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
@@ -20,11 +20,8 @@
 import org.apache.nifi.authorization.RequestAction;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserUtils;
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.controller.ComponentNode;
 import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
-import org.apache.nifi.controller.PropertyConfiguration;
 import org.apache.nifi.controller.flow.FlowManager;
 import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceState;
@@ -32,8 +29,6 @@
 import org.apache.nifi.parameter.Parameter;
 import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterDescriptor;
-import org.apache.nifi.parameter.ParameterReference;
-import org.apache.nifi.parameter.ParameterReferenceManager;
 import org.apache.nifi.web.ResourceNotFoundException;
 import org.apache.nifi.web.api.dto.ParameterContextDTO;
 import org.apache.nifi.web.api.dto.ParameterContextReferenceDTO;
@@ -73,7 +68,7 @@
         if (inheritedParameterContexts != null) {
             resolveInheritedParameterContexts(parameterContextDto);
             // This will throw an exception if one is not found
-            inheritedParameterContexts.stream().forEach(entity -> flowManager.getParameterContextManager()
+            inheritedParameterContexts.forEach(entity -> flowManager.getParameterContextManager()
                     .getParameterContext(entity.getComponent().getId()));
         }
         authorizeReferences(parameterContextDto);
@@ -136,7 +131,8 @@
         }
     }
 
-    private Map<String, Parameter> getParameters(final ParameterContextDTO parameterContextDto, final ParameterContext context) {
+    @Override
+    public Map<String, Parameter> getParameters(final ParameterContextDTO parameterContextDto, final ParameterContext context) {
         final Set<ParameterEntity> parameterEntities = parameterContextDto.getParameters();
         if (parameterEntities == null) {
             return Collections.emptyMap();
@@ -146,6 +142,11 @@
         for (final ParameterEntity parameterEntity : parameterEntities) {
             final ParameterDTO parameterDto = parameterEntity.getParameter();
 
+            // Inherited parameters are only included for referencing components, but we should not save them as direct parameters
+            if (parameterDto.getInherited() != null && parameterDto.getInherited()) {
+                continue;
+            }
+
             if (parameterDto.getName() == null) {
                 throw new IllegalArgumentException("Cannot specify a Parameter without a name");
             }
@@ -226,7 +227,8 @@
         return context;
     }
 
-    private List<ParameterContext> getInheritedParameterContexts(final ParameterContextDTO parameterContextDto) {
+    @Override
+    public List<ParameterContext> getInheritedParameterContexts(final ParameterContextDTO parameterContextDto) {
         resolveInheritedParameterContexts(parameterContextDto);
 
         final List<ParameterContext> inheritedParameterContexts = new ArrayList<>();
@@ -243,76 +245,10 @@
         verifyInheritedParameterContextRefs(parameterContextDto);
 
         final ParameterContext currentContext = getParameterContext(parameterContextDto.getId());
-        for (final ParameterEntity parameterEntity : parameterContextDto.getParameters()) {
-            final ParameterDTO parameterDto = parameterEntity.getParameter();
-            final String parameterName = parameterDto.getName();
-            final ParameterReferenceManager referenceManager = currentContext.getParameterReferenceManager();
 
-            for (final ProcessorNode processor : referenceManager.getProcessorsReferencing(currentContext, parameterName)) {
-                verifyParameterUpdate(parameterDto, processor, currentContext.getName(), verifyComponentStates, processor.isRunning(), "Processor that is running");
-            }
-
-            final Set<ControllerServiceNode> referencingServices = referenceManager.getControllerServicesReferencing(currentContext, parameterName);
-            for (final ControllerServiceNode serviceNode : referencingServices) {
-                final ControllerServiceState serviceState = serviceNode.getState();
-                final boolean serviceActive = serviceState != ControllerServiceState.DISABLED;
-
-                verifyParameterUpdate(parameterDto, serviceNode, currentContext.getName(), verifyComponentStates, serviceActive,
-                    "Controller Service [id=" + serviceNode.getIdentifier() + "] with a state of " + serviceState + " (state expected to be DISABLED)");
-            }
-        }
-    }
-
-    private void verifyParameterUpdate(final ParameterDTO parameterDto, final ComponentNode component, final String contextName,
-                                            final boolean verifyComponentStates, final boolean active, final String activeExplanation) {
-
-        final String parameterName = parameterDto.getName();
-        final Boolean parameterSensitive = parameterDto.getSensitive();
-        final boolean parameterDeletion = parameterDto.getDescription() == null && parameterDto.getSensitive() == null && parameterDto.getValue() == null;
-
-        // For any parameter that is added or modified, we need to ensure that the new configuration will not result in a Sensitive Parameter being referenced by a non-Sensitive Property
-        // or a Non-Sensitive Parameter being referenced by a Sensitive Property.
-        // Additionally, if 'verifyComponentStates' or parameter is being deleted, we must ensure that any component that references a value that is to be updated
-        // is stopped (if a processor) or disabled (if a controller service).
-        for (final Map.Entry<PropertyDescriptor, PropertyConfiguration> entry : component.getProperties().entrySet()) {
-            final PropertyConfiguration configuration = entry.getValue();
-            if (configuration == null) {
-                continue;
-            }
-
-            for (final ParameterReference reference : configuration.getParameterReferences()) {
-                final String referencedParameterName = reference.getParameterName();
-                if (referencedParameterName.equals(parameterName)) {
-                    if (entry.getKey().isSensitive() && !parameterDeletion && !Boolean.TRUE.equals(parameterSensitive)) {
-                        throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the update would add a Non-Sensitive Parameter " +
-                            "named '" + parameterName + "' but this Parameter already is referenced by a Sensitive Property.");
-                    }
-
-                    if (!entry.getKey().isSensitive() && !parameterDeletion && Boolean.TRUE.equals(parameterSensitive)) {
-                        throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the update would add a Sensitive Parameter named " +
-                            "'" + parameterName + "' but this Parameter already is referenced by a Non-Sensitive Property.");
-                    }
-
-                    if (active && (verifyComponentStates || parameterDeletion)) {
-                        if (parameterDeletion) {
-                            // First check if the actual parameter context is now missing the parameter: it may not be,
-                            // if the parameter is inherited from another context
-                            final ProcessGroup processGroup = flowManager.getGroup(component.getProcessGroupIdentifier());
-                            final ParameterContext parameterContext = processGroup.getParameterContext();
-                            final ParameterDescriptor parameterDescriptor = new ParameterDescriptor.Builder()
-                                    .name(parameterName).build();
-                            if (!parameterContext.hasEffectiveValueIfRemoved(parameterDescriptor)) {
-                                throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the " + parameterName + " Parameter is being referenced by a " +
-                                        activeExplanation + ".");
-                            }
-                        } else {
-                            throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because it has Parameters that are being referenced by a " +
-                                    activeExplanation + ".");
-                        }
-                    }
-                }
-            }
-        }
+        final List<ParameterContext> inheritedParameterContexts = getInheritedParameterContexts(parameterContextDto);
+        final Map<String, Parameter> parameters = parameterContextDto.getParameters() == null ? Collections.emptyMap() : getParameters(parameterContextDto, currentContext);
+        currentContext.verifyCanUpdateParameterContext(parameters, inheritedParameterContexts);
     }
 
     /**
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
index d1e10a5..c788921 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
@@ -20,7 +20,7 @@
     <div class="parameter-context-tab-container dialog-content">
         <div id="parameter-context-tabs" class="tab-container"></div>
         <div id="parameter-context-tabs-content">
-            <div id="parameter-context-standard-settings-tab-content" class="configuration-tab">
+            <div id="parameter-context-standard-settings-tab-content" class="split-65-35 configuration-tab">
                 <div class="settings-left">
                     <div id="parameter-context-id-setting" class="setting hidden">
                         <div class="setting-name">Id</div>
@@ -57,7 +57,7 @@
                     </div>
                 </div>
             </div>
-            <div id="parameter-context-parameters-tab-content" class="configuration-tab">
+            <div id="parameter-context-parameters-tab-content" class="split-65-35 configuration-tab">
                 <div class="settings-left">
                     <div class="edit-mode">
                         <div id="add-parameter"><button class="button fa fa-plus"></button></div>
@@ -96,8 +96,51 @@
                     </div>
                 </div>
             </div>
+            <div id="parameter-context-inheritance-tab-content" class="configuration-tab">
+                <div id="parameter-context-inheritance-container">
+                    <div class="settings-left">
+                        <div class="setting">
+                            <div class="setting-name">
+                                Available Parameter Contexts
+                                <div class="fa fa-question-circle" alt="Info" title="Available Parameter Contexts that could be inherited from."></div>
+                            </div>
+                            <div class="setting-field">
+                                <ol id="parameter-context-available"></ol>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="spacer">&nbsp;</div>
+                    <div class="settings-right">
+                        <div class="setting">
+                            <div class="setting-name">
+                                Selected Parameter Context
+                                <div class="fa fa-question-circle" alt="Info" title="Parameter Contexts selected for inheritance."></div>
+                            </div>
+                            <div class="setting-field">
+                                <ol id="parameter-context-selected"></ol>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div id="parameter-context-inheritance-container-read-only" style="display: none;">
+                    <div class="settings-left">
+                        <div class="setting">
+                            <div class="setting-name">
+                                Selected Parameter Context
+                                <div class="fa fa-question-circle" alt="Info" title="Parameter Contexts selected for inheritance."></div>
+                            </div>
+                            <div class="setting-field">
+                                <ol id="parameter-context-selected-read-only"></ol>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
+    <div id="inherited-parameter-contexts-message" class="ellipsis hidden">
+        Inherited Parameter Contexts have been modified. Updated listing of Parameters is pending apply.
+    </div>
 </div>
 <div id="parameter-dialog" class="dialog cancellable hidden">
     <div class="dialog-content">
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
index 32b2d5c..3fa0107 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
@@ -333,6 +333,7 @@
     top: 550px;
     left: 20px;
     font-size: 13px;
+    color: #775351;
     max-width: calc(100% - 230px);
     z-index: 999;
 }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
index d24281d..8b4ef45 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
@@ -32,12 +32,12 @@
     display: none;
 }
 
-#parameter-context-dialog div.settings-left {
+.parameter-context-tabs-content.split-65-35 div.settings-left {
     float: left;
     width: 65%;
 }
 
-#parameter-context-dialog div.settings-right {
+.parameter-context-tabs-content.split-65-35 div.settings-right {
     float: left;
     width: 33%;
     height: 100%;
@@ -134,3 +134,79 @@
     white-space: nowrap;
     margin-left: 6px;
 }
+
+/* inheritance */
+
+#parameter-context-available, #parameter-context-selected {
+    list-style-type: none;
+    margin: 0px;
+    background: #eaeef0;
+    min-height: 32px;
+    margin-bottom: 5px;
+    padding: 2px;
+}
+
+#parameter-context-available li, #parameter-context-selected li {
+    margin: 2px;
+    padding: 5px;
+    cursor: pointer;
+    font-size: 1em;
+    height: 12px;
+    overflow: hidden;
+}
+
+#parameter-context-available li.unauthorized, #parameter-context-selected li.unauthorized {
+    color: #a8a8a8;
+    cursor: not-allowed;
+}
+
+#parameter-context-available li {
+    background: #fff;
+    border-top: 1px solid #CCDADB;
+    border-right: 1px solid #CCDADB;
+    border-bottom: 1px solid #CCDADB;
+    border-left: 1px solid #CCDADB;
+    color: #004849;
+    font-weight: 500;
+    cursor: grab;
+    line-height: 32px;
+    height: 32px;
+    padding: 0px 10px;
+    box-shadow:0 1px 1px rgba(0,0,0,0.4);
+}
+
+#parameter-context-selected li {
+    background: #355B6A;
+    border-top: 1px solid #AAC1CE;
+    border-right: 1px solid #85A6B8;
+    border-bottom: 1px solid #618BA3;
+    border-left: 1px solid #85A6B8;
+    color: #fff;
+    font-weight: bold;
+    line-height: 32px;
+    height: 32px;
+    padding: 0px 10px;
+}
+
+#parameter-context-selected .draggable-control {
+    float: right;
+}
+
+#parameter-context-selected .fa-remove, #parameter-context-selected .fa-question-circle {
+    color: #fff;
+}
+
+#parameter-context-selected-read-only {
+    list-style: inside none decimal;
+}
+
+#inherited-parameter-contexts-message {
+    position: absolute;
+    top: 550px;
+    left: 20px;
+    font-size: 13px;
+    line-height: normal;
+    color: #775351;
+    max-width: calc(100% - 230px);
+    z-index: 999;
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
index d8d3a59..cae8a55 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
@@ -219,6 +219,7 @@
         parameterData.setItems([]);
 
         resetUsage();
+        resetInheritance();
 
         // reset the last selected parameter
         lastSelectedId = null;
@@ -301,6 +302,24 @@
     };
 
     /**
+     * Marshals the inherited parameter contexts.
+     */
+    var marshalInheritedParameterContexts = function () {
+        var inheritedParameterContextIds = $('#parameter-context-selected').sortable('toArray');
+
+        return inheritedParameterContextIds.map(function (id) {
+            var name = $('#parameter-context-selected').find('li#' + id + ' span').text();
+            return {
+                id: id,
+                component: {
+                    id: id,
+                    name: name
+                }
+            }
+        });
+    };
+
+    /**
      * Handles outstanding changes.
      *
      * @returns {deferred}
@@ -1062,10 +1081,19 @@
         var parameters = marshalParameters();
         var proposedParamContextName = $('#parameter-context-name').val();
         var proposedParamContextDesc = $('#parameter-context-description-field').val();
+        var inheritedParameterContexts = marshalInheritedParameterContexts();
+
+        var inheritedParameterContextEquals = isInheritedParameterContextEquals(parameterContextEntity, inheritedParameterContexts);
+        if (inheritedParameterContextEquals) {
+            $('#inherited-parameter-contexts-message').addClass('hidden');
+        } else {
+            $('#inherited-parameter-contexts-message').removeClass('hidden');
+        }
 
         if (_.isEmpty(parameters) &&
             proposedParamContextName === _.get(parameterContextEntity, 'component.name') &&
-            proposedParamContextDesc === _.get(parameterContextEntity, 'component.description')) {
+            proposedParamContextDesc === _.get(parameterContextEntity, 'component.description') &&
+            inheritedParameterContextEquals) {
 
             return false;
         } else {
@@ -1074,6 +1102,24 @@
     };
 
     /**
+     * Determines if the proposed inherited parameter contexts are equal to the current configuration.
+     *
+     * @param parameterContextEntity
+     * @param proposedInheritedParameterContexts
+     * @returns {*}
+     */
+    var isInheritedParameterContextEquals = function (parameterContextEntity, proposedInheritedParameterContexts) {
+        var configuredInheritedParameterContexts = parameterContextEntity.component.inheritedParameterContexts.map(function (inheritedParameterContext) {
+            return inheritedParameterContext.id;
+        });
+        var mappedProposedInheritedParameterContexts = proposedInheritedParameterContexts.map(function (proposedInheritedParameterContext) {
+            return proposedInheritedParameterContext.id;
+        });
+
+        return _.isEqual(configuredInheritedParameterContexts, mappedProposedInheritedParameterContexts);
+    }
+
+    /**
      * Updates parameter contexts by issuing an update request and polling until it's completion.
      *
      * @param parameterContextEntity
@@ -1081,12 +1127,15 @@
      */
     var updateParameterContext = function (parameterContextEntity) {
         var parameters = marshalParameters();
+        var inheritedParameterContexts = marshalInheritedParameterContexts();
 
         if (parameters.length === 0) {
             // nothing to update
             parameterContextEntity.component.parameters = [];
-            if ($('#parameter-context-name').val() === parameterContextEntity.component.name &&
-                $('#parameter-context-description-field').val() === parameterContextEntity.component.description) {
+
+            if ($('#parameter-context-name').val() === _.get(parameterContextEntity, 'component.name') &&
+                $('#parameter-context-description-field').val() === _.get(parameterContextEntity, 'component.description') &&
+                isInheritedParameterContextEquals(parameterContextEntity, inheritedParameterContexts)) {
                 close();
 
                 return;
@@ -1095,6 +1144,9 @@
             parameterContextEntity.component.parameters = parameters;
         }
 
+        // include the inherited parameter contexts
+        parameterContextEntity.component.inheritedParameterContexts = inheritedParameterContexts;
+
         parameterContextEntity.component.name = $('#parameter-context-name').val();
         parameterContextEntity.component.description = $('#parameter-context-description-field').val();
 
@@ -1249,8 +1301,8 @@
                             });
 
                             // update the parameter context table if displayed
-                            var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
-                            if (nfCommon.isDefinedAndNotNull(parameterContextGrid)) {
+                            if ($('#parameter-contexts-table').is(':visible')) {
+                                var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
                                 var parameterContextData = parameterContextGrid.getData();
 
                                 $.extend(parameterContextEntity, {
@@ -1289,6 +1341,9 @@
                 $('#parameter-context-tabs').hide();
                 $('#parameter-context-update-status').show();
 
+                // hide the pending apply message for parameter context
+                $('#inherited-parameter-contexts-message').addClass('hidden')
+
                 pollUpdateRequest(response);
             }).fail(handleAjaxFailure);
         }).promise();
@@ -1530,6 +1585,57 @@
         }
     };
 
+    /**
+     * Load the parameter context inheritance tab for the current parameterContextEntity. The current parameterContextEntity could be
+     * null if this is a new parameter context.
+     *
+     * @param parameterContextEntity    the parameter context being edited or null if new
+     * @param readOnly                  whether the controls should be read only
+     * @param parameterContexts         all parameter contexts
+     */
+    var loadParameterContextInheritance = function (parameterContextEntity, readOnly, parameterContexts) {
+        // consider each parameter context and add to the listing of available or selected contexts based on the supplied parameterContextEntity
+        $.each(parameterContexts, function (i, availableParameterContext) {
+            // don't support inheriting from the current context
+            var isCurrent = nfCommon.isNull(parameterContextEntity) ? false : availableParameterContext.id === parameterContextEntity.id;
+
+            // determine if this available parameter context is already selected
+            var isSelected = nfCommon.isNull(parameterContextEntity) ? false : parameterContextEntity.component.inheritedParameterContexts.some(function (selectedParameterContext) {
+                return availableParameterContext.id === selectedParameterContext.id;
+            });
+
+            if (readOnly) {
+                if (isSelected) {
+                    $('#parameter-context-selected-read-only').append($('<li></li>').text(availableParameterContext.component.name));
+                }
+            } else {
+                if (isSelected) {
+                    addParameterContextInheritanceControl(availableParameterContext, true);
+                } else if (!isCurrent) {
+                    addParameterContextInheritanceControl(availableParameterContext, false);
+                }
+            }
+        });
+
+        if (!nfCommon.isNull(parameterContextEntity)) {
+            sortSelectedParameterContexts(parameterContextEntity);
+        }
+
+        sortAvailableParameterContexts();
+
+        if (readOnly) {
+            if ($('#parameter-context-selected-read-only').is(':empty')) {
+                $('#parameter-context-selected-read-only').append($('<span class="unset">No value set</span>'));
+            }
+
+            $('#parameter-context-inheritance-container-read-only').show();
+            $('#parameter-context-inheritance-container').hide();
+        } else {
+            $('#parameter-context-inheritance-container-read-only').hide();
+            $('#parameter-context-inheritance-container').show();
+        }
+    };
+
     var resetUsage = function () {
         // empty the containers
         var processorContainer = $('.parameter-context-referencing-processors');
@@ -1562,6 +1668,125 @@
     };
 
     /**
+     * Reset the inheritance tab.
+     */
+    var resetInheritance = function () {
+        $('#parameter-context-available').empty();
+        $('#parameter-context-selected').empty();
+        $('#parameter-context-selected-read-only').empty();
+        $('#inherited-parameter-contexts-message').addClass('hidden');
+    };
+
+    /**
+     * Sorts the available parameter contexts.
+     */
+    var sortAvailableParameterContexts = function () {
+        var availableParameterContextList = $('#parameter-context-available');
+        availableParameterContextList.children('li')
+            .detach()
+            .sort(function (aElement, bElement) {
+                var a = $(aElement);
+                var b = $(bElement);
+
+                // put unauthorized last
+                if (a.hasClass('unauthorized') && b.hasClass('unauthorized')) {
+                    return 0;
+                } else if (a.hasClass('unauthorized')) {
+                    return 1;
+                } else if (b.hasClass('unauthorized')) {
+                    return -1;
+                }
+
+                var nameA = a.text();
+                var nameB = b.text();
+                return nameA.localeCompare(nameB);
+            })
+            .appendTo(availableParameterContextList);
+    };
+
+    /**
+     * Sorts the selected parameter context array based on the current parameter context entity.
+     *
+     * @param {object} selectedParameterContexts
+     */
+    var sortSelectedParameterContexts = function (parameterContextEntity) {
+        var selectedInheritedParameterContexts = parameterContextEntity.component.inheritedParameterContexts;
+
+        var selectedParameterContextList = $('#parameter-context-selected');
+        selectedParameterContextList.children('li')
+            .detach()
+            .sort(function (aElement, bElement) {
+                var a = $(aElement);
+                var b = $(bElement);
+
+                var findA = function (selectedInheritedParameterContext) {
+                    return a.attr('id') === selectedInheritedParameterContext.id;
+                };
+                var findB = function (selectedInheritedParameterContext) {
+                    return b.attr('id') === selectedInheritedParameterContext.id;
+                };
+
+                return selectedInheritedParameterContexts.findIndex(findA) - selectedInheritedParameterContexts.findIndex(findB);
+            })
+            .appendTo(selectedParameterContextList);
+    }
+
+    /**
+     * Adds the specified parameter context to the list of available parameter contexts.
+     *
+     * @argument {jQuery} container                  The container for the parameter context
+     * @argument {object} parameterContext           An available parameter context
+     * @argument {boolean} isSelected                 Whether the parameter context is selected (which is used to decide whether to provide a remove control)
+     */
+    var addParameterContextInheritanceControl = function (parameterContext, isSelected) {
+        var label = parameterContext.id;
+        if (parameterContext.permissions.canRead) {
+            label = parameterContext.component.name;
+        }
+
+        // add the parameter context to the specified list
+        var parameterContextElement = $('<li></li>').append($('<span style="float: left;"></span>').text(label)).attr('id', parameterContext.id).addClass('ui-state-default');
+        if (!parameterContext.permissions.canRead) {
+            parameterContextElement.addClass('unauthorized');
+        } else {
+            // add the description if applicable
+            if (!nfCommon.isBlank(parameterContext.component.description)) {
+                $('<div class="fa fa-question-circle"></div>').appendTo(parameterContextElement).qtip($.extend({
+                    content: nfCommon.escapeHtml(parameterContext.component.description)
+                }, nfCommon.config.tooltipConfig));
+            }
+
+            if (isSelected) {
+                addControlsForSelectedParameterContext(parameterContextElement);
+            }
+        }
+        parameterContextElement.appendTo(isSelected ? '#parameter-context-selected' : '#parameter-context-available');
+    };
+
+    /**
+     * Adds the controls to the specified selected draggable element.
+     *
+     * @argument {jQuery} draggableElement
+     */
+    var addControlsForSelectedParameterContext = function (draggableElement) {
+        var removeIcon = $('<div class="draggable-control"><div class="fa fa-remove"></div></div>')
+            .on('click', function () {
+                // remove the remove ice
+                removeIcon.remove();
+
+                // restore to the available parameter contexts
+                $('#parameter-context-available').append(draggableElement);
+
+                // resort the available parameter contexts
+                sortAvailableParameterContexts();
+
+                // update the buttons to possibly trigger the disabled state
+                $('#parameter-context-dialog').modal('refreshButtons');
+            })
+            .appendTo(draggableElement);
+    }
+
+    /**
      * Performs the filtering.
      *
      * @param {object} item     The item subject to filtering
@@ -1914,6 +2139,9 @@
             }, {
                 name: 'Parameters',
                 tabContentId: 'parameter-context-parameters-tab-content'
+            }, {
+                name: 'Inheritance',
+                tabContentId: 'parameter-context-inheritance-tab-content'
             }],
             select: function () {
                 // update the parameters table size in case this is the first time its rendered
@@ -2125,22 +2353,23 @@
     };
 
     /**
+     * Fetches the available Parameter Contexts.
+     */
+    var fetchParameterContexts = function () {
+        return $.ajax({
+                type: 'GET',
+                url: '../nifi-api/flow/parameter-contexts',
+                dataType: 'json'
+            });
+    };
+
+    /**
      * Loads the parameter contexts.
      *
      * @param parameterContextToSelect   id of the parameter context to select in the grid
      */
     var loadParameterContexts = function (parameterContextToSelect) {
-        var parameterContexts = $.Deferred(function (deferred) {
-            $.ajax({
-                type: 'GET',
-                url: '../nifi-api/flow/parameter-contexts',
-                dataType: 'json'
-            }).done(function (response) {
-                deferred.resolve(response);
-            }).fail(function (xhr, status, error) {
-                deferred.reject(xhr, status, error);
-            });
-        }).promise();
+        var parameterContexts = fetchParameterContexts();
 
         // return a deferred for all parts of the parameter contexts
         return $.when(parameterContexts).done(function (response) {
@@ -2382,6 +2611,7 @@
             // create a new parameter context
             $('#new-parameter-context').on('click', function () {
                 resetUsage();
+                resetInheritance();
 
                 // new parameter contexts do not have an ID to show
                 if (!$('#parameter-context-id-setting').hasClass('hidden')) {
@@ -2424,10 +2654,64 @@
                     }
                 }]).modal('show');
 
+                var parameterContextsGrid = $('#parameter-contexts-table').data('gridInstance');
+                var parameterContextsData = parameterContextsGrid.getData();
+                var parameterContexts = parameterContextsData.getItems();
+
+                loadParameterContextInheritance(null, false, parameterContexts);
+
                 // set the initial focus
                 $('#parameter-context-name').focus();
             });
 
+            // work around for https://bugs.jqueryui.com/ticket/6054
+            var shouldAllowDrop = true;
+
+            // make the parameter context containers sortable
+            $('#parameter-context-available').sortable({
+                containment: $('#parameter-context-inheritance-tab-content'),
+                cancel: '.unauthorized',
+                connectWith: '#parameter-context-selected',
+                placeholder: 'available',
+                scroll: true,
+                opacity: 0.6,
+                beforeStop: function (event, ui) {
+                    if ($('#parameter-context-available').find('.ui-sortable-placeholder').length) {
+                        shouldAllowDrop = false;
+                    }
+                },
+                stop: function (event, ui) {
+                    const allowDrop = shouldAllowDrop;
+                    shouldAllowDrop = true;
+                    return allowDrop;
+                }
+            });
+            $('#parameter-context-selected').sortable({
+                containment: $('#parameter-context-inheritance-tab-content'),
+                cancel: '.unauthorized',
+                placeholder: 'selected',
+                scroll: true,
+                opacity: 0.6,
+                receive: function (event, ui) {
+                    addControlsForSelectedParameterContext(ui.item);
+                },
+                update: function (event, ui) {
+                    // update the buttons to possibly trigger the disabled state
+                    $('#parameter-context-dialog').modal('refreshButtons');
+                }
+            });
+            $('#parameter-context-available, #parameter-context-selected').disableSelection();
+
+            // add a listener that will handle dblclick for all available authorized parameter context children
+            $(document).on('dblclick', '#parameter-context-available li:not(".unauthorized")', function() {
+                var availableParameterContextElement = $(this).detach().appendTo($('#parameter-context-selected'));
+
+                addControlsForSelectedParameterContext(availableParameterContextElement);
+
+                // update the buttons to possibly trigger the disabled state
+                $('#parameter-context-dialog').modal('refreshButtons');
+            });
+
             // initialize the new parameter context dialog
             initNewParameterContextDialog();
 
@@ -2437,7 +2721,7 @@
                 if ($('#parameter-referencing-components-container').is(':visible')) {
                     updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
                 }
-            })
+            });
         },
 
         /**
@@ -2460,6 +2744,9 @@
                 })
             };
 
+            // include the inherited parameter contexts
+            parameterContextEntity.component.inheritedParameterContexts = marshalInheritedParameterContexts();
+
             var addContext = $.ajax({
                 type: 'POST',
                 url: config.urls.parameterContexts,
@@ -2467,10 +2754,9 @@
                 dataType: 'json',
                 contentType: 'application/json'
             }).done(function (parameterContextEntity) {
-                // add the item
-                var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
-
-                if (nfCommon.isDefinedAndNotNull(parameterContextGrid)) {
+                // update the table if displayed
+                if ($('#parameter-contexts-table').is(':visible')) {
+                    var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
                     var parameterContextData = parameterContextGrid.getData();
                     parameterContextData.addItem(parameterContextEntity);
 
@@ -2539,6 +2825,19 @@
                 dataType: 'json'
             });
 
+            var parameterContextsDeferred;
+            if ($('#parameter-contexts-table').is(':visible')) {
+                parameterContextsDeferred = $.Deferred(function (deferred) {
+                    var parameterContextsGrid = $('#parameter-contexts-table').data('gridInstance');
+                    var parameterContextsData = parameterContextsGrid.getData();
+                    deferred.resolve({
+                        parameterContexts: parameterContextsData.getItems()
+                    });
+                }).promise();
+            } else {
+                parameterContextsDeferred = fetchParameterContexts();
+            }
+
             // once everything is loaded, show the dialog
             reloadContext.done(function (parameterContextEntity) {
                 var canWrite = _.get(parameterContextEntity, 'permissions.canWrite', false);
@@ -2579,71 +2878,74 @@
 
                 loadParameters(parameterContextEntity, parameterToSelect, readOnly || !canWrite);
 
-                var editModeButtonModel = [{
-                    buttonText: 'Apply',
-                    color: {
-                        base: '#728E9B',
-                        hover: '#004849',
-                        text: '#ffffff'
-                    },
-                    disabled: function () {
-                        if ($('#parameter-context-name').val() !== '' && hasParameterContextChanged(currentParameterContextEntity)) {
+                // load the parameter contexts in order to render all available parameter contexts
+                parameterContextsDeferred.done(function (response) {
+                    loadParameterContextInheritance(parameterContextEntity, readOnly || !canWrite, response.parameterContexts);
+
+                    var editModeButtonModel = [{
+                        buttonText: 'Apply',
+                        color: {
+                            base: '#728E9B',
+                            hover: '#004849',
+                            text: '#ffffff'
+                        },
+                        disabled: function () {
+                            if ($('#parameter-context-name').val() !== '' && hasParameterContextChanged(currentParameterContextEntity)) {
+                                return false;
+                            }
+                            return true;
+                        },
+                        handler: {
+                            click: function () {
+                                updateParameterContext(currentParameterContextEntity);
+                            }
+                        }
+                    }, {
+                        buttonText: 'Cancel',
+                        color: {
+                            base: '#E3E8EB',
+                            hover: '#C7D2D7',
+                            text: '#004849'
+                        },
+                        handler: {
+                            click: function () {
+                                $(this).modal('hide');
+                            }
+                        }
+                    }];
+
+                    var readOnlyButtonModel = [{
+                        buttonText: 'Ok',
+                        color: {
+                            base: '#728E9B',
+                            hover: '#004849',
+                            text: '#ffffff'
+                        },
+                        disabled: function () {
                             return false;
+                        },
+                        handler: {
+                            click: function () {
+                                $(this).modal('hide');
+                            }
                         }
-                        return true;
-                    },
-                    handler: {
-                        click: function () {
-                            updateParameterContext(currentParameterContextEntity);
-                        }
-                    }
-                }, {
-                    buttonText: 'Cancel',
-                    color: {
-                        base: '#E3E8EB',
-                        hover: '#C7D2D7',
-                        text: '#004849'
-                    },
-                    handler: {
-                        click: function () {
-                            $(this).modal('hide');
-                        }
-                    }
-                }];
+                    }];
 
-                var readOnlyButtonModel = [{
-                    buttonText: 'Ok',
-                    color: {
-                        base: '#728E9B',
-                        hover: '#004849',
-                        text: '#ffffff'
-                    },
-                    disabled: function () {
-                        return false;
-                    },
-                    handler: {
-                        click: function () {
-                            $(this).modal('hide');
-                        }
-                    }
-                }];
+                    // show the context
+                    $('#parameter-context-dialog')
+                        .modal('setHeaderText', canWrite ? 'Update Parameter Context' : 'View Parameter Context')
+                        .modal('setButtonModel', canWrite ? editModeButtonModel : readOnlyButtonModel)
+                        .modal('show');
 
+                    // select the parameters tab
+                    $('#parameter-context-tabs').find('li:eq(1)').click();
 
-                // show the context
-                $('#parameter-context-dialog')
-                    .modal('setHeaderText', canWrite ? 'Update Parameter Context' : 'View Parameter Context')
-                    .modal('setButtonModel', canWrite ? editModeButtonModel : readOnlyButtonModel)
-                    .modal('show');
+                    // check if border is necessary
+                    updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
 
-                // select the parameters tab
-                $('#parameter-context-tabs').find('li:last').click();
-
-                // check if border is necessary
-                updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
-
-                // show the border if necessary
-                updateReferencingComponentsBorder(referencingComponentsContainer);
-
+                    // show the border if necessary
+                    updateReferencingComponentsBorder(referencingComponentsContainer);
+                }).fail(nfErrorHandler.handleAjaxError);
             }).fail(nfErrorHandler.handleAjaxError);
         },