You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2022/09/24 12:43:34 UTC

[nifi] branch main updated: NIFI-9514 NIFI-9515: Add UI support for Parameter Providers in Controller Services (#5671)

This is an automated email from the ASF dual-hosted git repository.

mcgilman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new d82ce18f88 NIFI-9514 NIFI-9515: Add UI support for Parameter Providers in Controller Services (#5671)
d82ce18f88 is described below

commit d82ce18f885f51a1e38cff58f9d211e9affcbed9
Author: M Tien <56...@users.noreply.github.com>
AuthorDate: Sat Sep 24 05:43:27 2022 -0700

    NIFI-9514 NIFI-9515: Add UI support for Parameter Providers in Controller Services (#5671)
    
    * NIFI-9514 - Add UI support for Parameter Providers in Controller Services
    
    * - Fix a dialog closing issue
    
    * - Hide fetch parameters button if there are config errors
    
    * - Update Fetch Parameters dialog with new UX
    - Implement checkbox logic for Selectable Parameters table
    
    * - add createNewParameterContext property to track if group will be created as a new parameter context
    - update fetch parameters dialog views for editing and after applying changes
    - show affected referencing components after applying changes
    - disable apply button if no parameter contexts will be updated
    - refactor showing and hiding fetched parameters and selectable parameters tables logic
    - add tooltip for parameter context icon
    
    * - update parameter contexts view for provided contexts
    
    * - add affected referencing components after applying fetched parameters
    - add referencing parameter contexts to the provider settings tab
    - update checkbox style in the selectable parameters table
    - fix updating the referencing components border
    - fix lint errors
    
    * - fix selectable parameters table checkbox styles
    
    * - fix hasParameterContextChanged checks
    - checkbox style tweaks
    
    * - add status bar to fetch parameters dialog
    
    * - more style tweaks and remove unnecessary code
    
    * - fix the selectable parameters table not saving and loading correctly
    
    * - add tooltips
    - fix styles
    
    * - link from the parameter context to the associated parameter provider
    
    * 9514: support navigating to the controller service created in parameter providers
    
    * fix bulletins format in status bar
    
    * address review feedback
    - manage permissions for parameter providers
    - disable/enable Fetch Parameters dialog Apply button
    
    * manage authorizations to access parameter providers and parameter contexts
    
    * fix lint errors
    
    * fix and refactor hasGroupsChanged to determine the Apply button state
    
    * new parameter context name input cannot be empty in Fetch Parameters dialog
    
    * address review feedback
    
    * fix duplicate code
    
    * more fixes
    
    * update fetch parameters checkbox states
    
    * replace slickgrid checkbox selection plugin with custom implementation for different selection types
    - check for affected parameters using the parameter status
    
    * fixed showing the correct referencing components when switching groups
    
    * fixed selectable parameters table shifting contents on row selection
    
    * leverage slickgrid onClick to set user-selected parameter sensitivities in the selectable parameters table
    
    * fix statusbar bulletins in processor details
    - fix updating borders for referencing components
    - update referencing parameter icon
    - update table title in fetch parameters dialog
    
    * address review feedback
    
    * populate controller services referencing components of a referenced provider
    - update controller services tab name
    
    * showing parameter providers in CS controller service
    
    * update tooltip messages in fetch parameters dialog
    - fix dialog height to make room for the status bar
    
    * reload the parameter providers table after a referenced parameter provider change in controller services
    - fix conditions for enabling/disabling fetch parameters dialog Apply button
    - add affected components no permissions message in the dialog
    
    * fix always reloading the parameter providers table
    - fix disableApplyButton checks
    - add dialog message for a missing new parameter context name
    
    * remove extra reload
    
    * more fixes for disableApplyButton
    - fixed styles for dialog message
    
    * add a check in disableApplyButton for changed parameter values that are not referenced
    - add changed value icon for parameters that are not referenced
    - fix border on the referencing affected components container
    
    * removed unnused plugins
    
    This closes #5671
---
 .../nifi-framework/nifi-web/nifi-web-ui/pom.xml    |    3 +
 .../src/main/resources/filters/canvas.properties   |    1 +
 .../src/main/webapp/WEB-INF/pages/canvas.jsp       |    3 +
 .../partials/canvas/fetch-parameters-dialog.jsp    |  204 ++
 .../canvas/new-parameter-context-dialog.jsp        |    1 +
 .../canvas/new-parameter-provider-dialog.jsp       |   61 +
 .../canvas/parameter-provider-configuration.jsp    |   82 +
 .../WEB-INF/partials/canvas/policy-management.jsp  |   10 +
 .../WEB-INF/partials/canvas/settings-content.jsp   |    3 +
 .../nifi-web-ui/src/main/webapp/css/canvas.css     |    2 +
 .../nifi-web-ui/src/main/webapp/css/dialog.css     |    4 +-
 .../webapp/css/new-parameter-provider-dialog.css   |  105 +
 .../src/main/webapp/css/parameter-provider.css     |  496 +++
 .../js/jquery/statusbar/jquery.statusbar.css       |   16 +-
 .../webapp/js/jquery/statusbar/jquery.statusbar.js |  123 +-
 .../webapp/js/nf/canvas/nf-canvas-bootstrap.js     |   19 +-
 .../webapp/js/nf/canvas/nf-controller-service.js   |  100 +-
 .../webapp/js/nf/canvas/nf-parameter-contexts.js   |  126 +-
 .../webapp/js/nf/canvas/nf-parameter-provider.js   | 3317 ++++++++++++++++++++
 .../webapp/js/nf/canvas/nf-policy-management.js    |   54 +
 .../js/nf/canvas/nf-processor-configuration.js     |   19 +-
 .../src/main/webapp/js/nf/canvas/nf-settings.js    |  993 +++++-
 .../src/main/webapp/js/nf/nf-processor-details.js  |   12 +-
 23 files changed, 5617 insertions(+), 137 deletions(-)

diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
index 77c7023610..8610087831 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/pom.xml
@@ -541,6 +541,7 @@
                                                 <include>${staging.dir}/js/nf/canvas/nf-controller-service.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-controller-services.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-reporting-task.js</include>
+                                                <include>${staging.dir}/js/nf/canvas/nf-parameter-provider.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/nf-settings.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js</include>
                                                 <include>${staging.dir}/js/nf/canvas/controllers/nf-ng-canvas-header-controller.js</include>
@@ -734,6 +735,7 @@
                                             <output>${project.build.directory}/${project.build.finalName}/css/nf-canvas-all.css</output>
                                             <includes>
                                                 <include>${staging.dir}/css/reporting-task.css</include>
+                                                <include>${staging.dir}/css/parameter-provider.css</include>
                                                 <include>${staging.dir}/css/controller-service.css</include>
                                                 <include>${staging.dir}/css/processor-configuration.css</include>
                                                 <include>${staging.dir}/css/processor-details.css</include>
@@ -753,6 +755,7 @@
                                                 <include>${staging.dir}/css/new-port-dialog.css</include>
                                                 <include>${staging.dir}/css/new-controller-service-dialog.css</include>
                                                 <include>${staging.dir}/css/new-reporting-task-dialog.css</include>
+                                                <include>${staging.dir}/css/new-parameter-provider-dialog.css</include>
                                                 <include>${staging.dir}/css/new-parameter-context-dialog.css</include>
                                                 <include>${staging.dir}/css/graph.css</include>
                                                 <include>${staging.dir}/css/header.css</include>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
index ae9b533d2b..664389c0b7 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/resources/filters/canvas.properties
@@ -71,6 +71,7 @@ nf.canvas.script.tags=<script type="text/javascript" src="js/nf/nf-ng-bridge.js?
 <script type="text/javascript" src="js/nf/canvas/nf-controller-service.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-controller-services.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-reporting-task.js?${project.version}"></script>\n\
+<script type="text/javascript" src="js/nf/canvas/nf-parameter-provider.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/nf-settings.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/controllers/nf-ng-breadcrumbs-controller.js?${project.version}"></script>\n\
 <script type="text/javascript" src="js/nf/canvas/controllers/nf-ng-canvas-header-controller.js?${project.version}"></script>\n\
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
index 7a017e3986..f9459225f8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
@@ -110,8 +110,10 @@
         <jsp:include page="/WEB-INF/partials/canvas/search-users-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/disable-controller-service-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/enable-controller-service-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/fetch-parameters-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-controller-service-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-reporting-task-dialog.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/new-parameter-provider-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-processor-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/new-port-dialog.jsp"/>
@@ -141,6 +143,7 @@
         <jsp:include page="/WEB-INF/partials/canvas/shell.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/controller-service-configuration.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/reporting-task-configuration.jsp"/>
+        <jsp:include page="/WEB-INF/partials/canvas/parameter-provider-configuration.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/processor-configuration.jsp"/>
         <jsp:include page="/WEB-INF/partials/processor-details.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/variable-configuration.jsp"/>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/fetch-parameters-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/fetch-parameters-dialog.jsp
new file mode 100644
index 0000000000..9872d7f7af
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/fetch-parameters-dialog.jsp
@@ -0,0 +1,204 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="fetch-parameters-dialog" layout="column" class="hidden large-dialog">
+    <div id="fetch-parameters-status-bar"></div>
+    <div class="dialog-content">
+        <%--settings-left--%>
+        <div class="settings-left">
+            <div id="fetch-parameters-provider-groups-container" class="setting">
+                <div class="setting-name">Name</div>
+                <div class="setting-field">
+                    <span id="fetch-parameters-id" class="hidden"></span>
+                    <div id="fetch-parameters-name"></div>
+                    <div class="clear"></div>
+                </div>
+            </div>
+            <div id="fetch-parameters-usage-container" class="setting">
+                <div class="setting-name">
+                    Select to configure a parameter group
+                    <div class="fa fa-question-circle" alt="Info" title="Discovered parameter groups from this parameter provider. Select a group to create a parameter context, then configure its parameter sensitivities."></div>
+                </div>
+                <div id="parameter-groups-table"></div>
+            </div>
+            <div id="apply-groups-container" class="setting hidden">
+                <div class="setting-name">Parameter Groups</div>
+                <div class="setting-field">
+                    <div id="apply-groups-list"></div>
+                </div>
+            </div>
+        </div>
+        <%--end settings-left--%>
+
+        <div class="spacer">&nbsp;</div>
+
+        <%--settings-center--%>
+        <div class="settings-center">
+            <div id="parameters-container" class="setting">
+                <div id="create-parameter-context-checkbox-container" class="setting-field"></div>
+                <div id="fetched-parameters-container" class="setting">
+                    <div class="setting-name">
+                        Fetched parameters
+                        <div class="fa fa-question-circle" alt="Info" title="Discovered parameters from the selected parameter group."></div>
+                    </div>
+                    <div id="fetched-parameters-listing-container" class="setting-field">
+                        <ol id="fetched-parameters-listing"></ol>
+                    </div>
+                </div>
+                <div id="selectable-parameters-container" class="setting">
+                    <div class="setting-name">
+                        Select parameters to be set as sensitive
+                        <div class="fa fa-question-circle" alt="Info" title="Only parameters that are not referenced can be modified."></div>
+                    </div>
+                    <div id="selectable-parameters-buttons">
+                        <button id="select-all-fetched-parameters" class="selectable-parameters-buttons">
+                            <div class="fa fa-check-square-o"></div>
+                            <span>Select all</span>
+                        </button>
+                        <button id="deselect-all-fetched-parameters" class="selectable-parameters-buttons">
+                            <div class="fa fa-minus-square-o"></div>
+                            <span>Deselect all</span>
+                        </button>
+                        <div class="clear"></div>
+                    </div>
+                    <div id="selectable-parameters-table" class="setting-field"></div>
+                </div>
+            </div>
+            <div id="fetch-parameters-update-status-container" class="setting">
+                <div id="fetch-parameters-update-status" class="hidden">
+                    <div class="setting">
+                        <div class="setting-name">
+                            Steps to update parameters
+                        </div>
+                        <div class="setting-field">
+                            <ol id="fetch-parameters-update-steps"></ol>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <%--end settings-center--%>
+
+        <div class="spacer">&nbsp;</div>
+
+        <%--settings-right--%>
+        <div class="settings-right">
+            <div class="setting">
+                <div class="setting-name">
+                    Parameter Contexts To Create
+                    <div class="fa fa-question-circle" alt="Info" title="Parameter groups set to be created as parameter contexts, pending apply."></div>
+                </div>
+                <div class="setting-field">
+                    <div id="parameter-contexts-to-create-container" class="ellipsis"></div>
+                </div>
+            </div>
+            <div class="setting">
+                <div class="setting-name">
+                    Parameter Contexts To Update
+                    <div class="fa fa-question-circle" alt="Info" title="Synced parameter contexts to be updated, pending apply."></div>
+                    <div class="referencing-components-loading"></div>
+                </div>
+                <div class="setting-field">
+                    <div id="parameter-contexts-to-update-container" class="ellipsis"></div>
+                </div>
+            </div>
+
+            <div id="fetch-parameters-referencing-components-container" class="setting hidden">
+                <div class="setting-name">
+                    Referencing Components
+                    <div class="fa fa-question-circle" alt="Info" title="Components referencing this selected parameter."></div>
+                    <div class="referencing-components-loading"></div>
+                </div>
+                <div id="fetch-parameter-referencing-components-container" class="setting-field">
+                </div>
+            </div>
+            <div id="fetch-parameters-referencing-components-template" class="fetch-parameters-referencing-components-template hidden clear">
+                <div class="setting">
+                    <div class="setting-name">Referencing Processors
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-processors"></ul>
+                    </div>
+                </div>
+                <div class="setting">
+                    <div class="setting-name">
+                        Referencing Controller Services
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-controller-services"></ul>
+                    </div>
+                </div>
+                <div class="setting">
+                    <div class="setting-name">
+                        Unauthorized referencing components
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-unauthorized-components"></ul>
+                    </div>
+                </div>
+            </div>
+
+            <div id="fetch-parameters-affected-referencing-components-container" class="setting">
+                <div class="setting-name">
+                    Affected Referencing Components
+                    <div class="fa fa-question-circle" alt="Info" title="Affected components referencing this parameter provider."></div>
+                    <div class="referencing-components-loading"></div>
+                </div>
+                <div id="affected-referencing-components-container" class="setting-field">
+                </div>
+            </div>
+            <div id="affected-referencing-components-template" class="affected-referencing-components-template hidden clear">
+                <div class="setting">
+                    <div class="setting-name">Referencing Processors
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-processors"></ul>
+                    </div>
+                </div>
+                <div class="setting">
+                    <div class="setting-name">
+                        Referencing Controller Services
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-controller-services"></ul>
+                    </div>
+                </div>
+                <div class="setting">
+                    <div class="setting-name">
+                        Unauthorized referencing components
+                    </div>
+                    <div class="setting-field">
+                        <ul class="fetch-parameters-referencing-unauthorized-components"></ul>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <%--end settings-right--%>
+    <div id="fetch-parameters-permissions-parameter-contexts-message" class="ellipsis fetch-parameters-dialog-message hidden">
+        You do not have permissions to modify one or more synced parameter contexts.
+    </div>
+    <div id="fetch-parameters-permissions-affected-components-message" class="ellipsis fetch-parameters-dialog-message hidden">
+        You do not have permissions to modify one or more affected components.
+    </div>
+    <div id="fetch-parameters-missing-context-name-message" class="ellipsis fetch-parameters-dialog-message hidden">
+        Missing parameter context name.
+    </div>
+    <div class="fetch-parameters-canceling hidden unset">
+        Canceling...
+    </div>
+</div>
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 c788921fdf..b7bcb2ee34 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
@@ -28,6 +28,7 @@
                             <div id="parameter-context-id-field" class="ellipsis"></div>
                         </div>
                     </div>
+                    <div id="parameter-context-provider-setting" class="setting hidden"></div>
                     <div class="setting">
                         <div class="setting-name">Name</div>
                         <div id="parameter-context-name-container" class="setting-field">
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-provider-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-provider-dialog.jsp
new file mode 100644
index 0000000000..fc0acb6d0b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-provider-dialog.jsp
@@ -0,0 +1,61 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="new-parameter-provider-dialog" layout="column" class="hidden">
+    <div class="dialog-content">
+        <div layout="row" style="padding-bottom:0">
+            <div flex="25" layout="row" layout-align="start center">
+                <div class="setting-name" style="margin-top: 10px;">Source</div>
+            </div>
+            <div flex layout="row" layout-align="space-between center" id="parameter-provider-type-filter-controls" class="filter-status">
+                <div id="parameter-provider-type-filter-status">
+                    Displaying&nbsp;<span id="displayed-parameter-provider-types"></span>&nbsp;of&nbsp;<span id="total-parameter-provider-types"></span>
+                </div>
+                <div id="controller-service-type-filter-container">
+                    <input type="text" id="parameter-provider-type-filter" placeholder="Filter"/>
+                </div>
+            </div>
+        </div>
+        <div flex layout="row" style="padding-top:0;height: 90%;">
+            <div flex="25" id="parameter-provider-tag-cloud-container">
+                <div class="setting">
+                    <div class="setting-field">
+                        <div id="parameter-provider-bundle-group-combo"></div>
+                    </div>
+                </div>
+                <div class="setting">
+                    <div class="setting-field">
+                        <div id="parameter-provider-tag-cloud"></div>
+                    </div>
+                </div>
+            </div>
+            <div layout="column" flex id="parameter-provider-types-container">
+                <div id="parameter-provider-types-table" class="unselectable"></div>
+                <div id="parameter-provider-type-container">
+                    <div id="parameter-provider-type-name"></div>
+                    <div id="parameter-provider-type-bundle"></div>
+                </div>
+                <div id="parameter-provider-description-container" class="hidden">
+                    <div id="parameter-provider-type-description" class="ellipsis multiline"></div>
+                    <span class="hidden" id="selected-parameter-provider-name"></span>
+                    <span class="hidden" id="selected-parameter-provider-type"></span>
+                </div>
+            </div>
+        </div>
+        <div class="clear"></div>
+    </div>
+</div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/parameter-provider-configuration.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/parameter-provider-configuration.jsp
new file mode 100644
index 0000000000..0b23b869fc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/parameter-provider-configuration.jsp
@@ -0,0 +1,82 @@
+<%--
+ 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.
+--%>
+<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %>
+<div id="parameter-provider-configuration" class="hidden large-dialog">
+    <div class="parameter-provider-configuration-tab-container dialog-content">
+        <div id="parameter-provider-configuration-tabs" class="tab-container"></div>
+        <div id="parameter-provider-configuration-tabs-content">
+            <div id="parameter-provider-standard-settings-tab-content" class="configuration-tab">
+                <div class="settings-left">
+                    <div class="setting">
+                        <div class="setting-name">Name</div>
+                        <div class="parameter-provider-editable setting-field">
+                            <input type="text" id="parameter-provider-name" name="parameter-provider-name"/>
+                        </div>
+                        <div class="parameter-provider-read-only setting-field hidden">
+                            <span id="read-only-parameter-provider-name"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Id</div>
+                        <div class="setting-field">
+                            <span id="parameter-provider-id"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Type</div>
+                        <div class="setting-field">
+                            <span id="parameter-provider-type"></span>
+                        </div>
+                    </div>
+                    <div class="setting">
+                        <div class="setting-name">Bundle</div>
+                        <div id="parameter-provider-bundle" class="setting-field"></div>
+                    </div>
+                </div>
+                <div class="spacer">&nbsp;</div>
+                <div class="settings-right">
+                    <div class="setting">
+                        <div class="setting-name">
+                            Referencing Components
+                            <div class="fa fa-question-circle" alt="Info" title="Other components referencing this parameter provider."></div>
+                        </div>
+                        <div class="setting-field">
+                            <div id="parameter-provider-referencing-components"></div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div id="parameter-provider-properties-tab-content" class="configuration-tab">
+                <div id="parameter-provider-properties"></div>
+                <div id="parameter-provider-properties-verification-results" class="verification-results">
+                    <div class="verification-results-header">Verification Results</div>
+                    <div id="parameter-provider-properties-verification-results-listing" class="verification-results-listing"></div>
+                </div>
+            </div>
+            <div id="parameter-provider-comments-tab-content" class="configuration-tab">
+                <textarea cols="30" rows="4" id="parameter-provider-comments" name="parameter-provider-comments" class="parameter-provider-editable setting-input"></textarea>
+                <div class="setting parameter-provider-read-only hidden">
+                    <div class="setting-name">Comments</div>
+                    <div class="setting-field">
+                        <span id="read-only-parameter-provider-comments"></span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="new-parameter-provider-property-container"></div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
index 024c7ba5f7..28a3c74c3a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/policy-management.jsp
@@ -84,6 +84,16 @@
                 </div>
                 <div class="clear"></div>
             </div>
+            <div id="policy-selected-parameter-provider-container" class="hidden policy-selected-component-container">
+                <div class="policy-selected-component-type-icon">
+                    <i class="icon icon-drop"></i>
+                </div>
+                <div class="policy-selected-component-details-container">
+                    <div class="policy-selected-component-name"></div>
+                    <div class="policy-selected-component-type">Parameter Provider</div>
+                </div>
+                <div class="clear"></div>
+            </div>
             <div id="selected-policy-component-id" class="hidden"></div>
             <div id="selected-policy-component-type" class="hidden"></div>
             <div id="component-policy-target"></div>
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
index 57f43c650c..4b19da272d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/settings-content.jsp
@@ -65,6 +65,9 @@
             <div id="registries-tab-content" class="configuration-tab controller-settings-table">
                 <div id="registries-table" class="settings-table"></div>
             </div>
+            <div id="parameter-providers-tab-content" class="configuration-tab controller-settings-table">
+                <div id="parameter-providers-table" class="settings-table"></div>
+            </div>
         </div>
     </div>
     <div id="settings-refresh-container">
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
index 8159795426..a9de76f303 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/canvas.css
@@ -23,6 +23,7 @@
 @import url(controller-service.css);
 @import url(component-state.css);
 @import url(reporting-task.css);
+@import url(parameter-provider.css);
 @import url(port-configuration.css);
 @import url(port-details.css);
 @import url(label-configuration.css);
@@ -34,6 +35,7 @@
 @import url(new-port-dialog.css);
 @import url(new-controller-service-dialog.css);
 @import url(new-reporting-task-dialog.css);
+@import url(new-parameter-provider-dialog.css);
 @import url(new-parameter-context-dialog.css);
 @import url(graph.css);
 @import url(header.css);
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 e28d68088e..2baa79bf27 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
@@ -478,7 +478,9 @@ div.variable-step {
     margin-bottom: 2px;
 }
 
-div.parameter-context-step, div.referencing-components-loading {
+div.parameter-context-step,
+div.referencing-components-loading,
+div.fetch-parameters-step {
     min-width: 16px;
     width: 16px;
     height: 16px;
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-provider-dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-provider-dialog.css
new file mode 100644
index 0000000000..6dbf19c0bc
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-provider-dialog.css
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+/* 
+    New parameter provider dialog.
+*/
+
+#new-parameter-provider-dialog {
+    min-height:620px;
+    height: 620px;
+    width: 800px;
+    min-width: 760px;
+}
+
+#parameter-provider-types-container {
+    padding: 0px;
+}
+
+#parameter-provider-bundle-group-combo {
+    margin-right: 2px;
+}
+
+#parameter-provider-description-container {
+    padding: 0px;
+}
+
+#parameter-provider-type-container {
+    padding: 20px 0 5px 0;
+    display: flex;
+}
+
+#parameter-provider-type-name {
+    color: #775351;
+    font-size: 16px;
+    font-weight: 500;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+#parameter-provider-type-bundle {
+    margin-left: 15px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    font-size: 13px;
+    line-height: 16px;
+    color: #666;
+    letter-spacing: 0.25px;
+    min-width: 0;
+}
+
+#parameter-provider-type-description {
+    height: 80px;
+    font-size: 13px;
+    line-height: 17px;
+    padding-top: 10px;
+}
+
+#parameter-provider-types-table {
+    min-height: 150px;
+    overflow: hidden;
+    height: 70%;
+    width: 100%;
+}
+
+#parameter-provider-types-table-container th {
+    vertical-align: middle;
+}
+
+#parameter-provider-types-table div.slick-viewport {
+    overflow-x: hidden !important;
+}
+
+/*
+    Processor type table filter
+*/
+
+#parameter-provider-type-filter-controls {
+    min-height: 32px;
+    padding: 0px;
+    margin: 0px;
+}
+
+#parameter-provider-type-filter {
+    width: 200px;
+}
+
+#parameter-provider-tag-cloud {
+    margin-top: -2px;
+}
\ No newline at end of file
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/parameter-provider.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/parameter-provider.css
new file mode 100644
index 0000000000..368ddb433b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/parameter-provider.css
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+/*
+    Parameter provider configuration styles.
+*/
+
+#parameter-provider-configuration {
+    position: absolute;
+    overflow: hidden;
+    width: 800px;
+    height: 450px;
+    font-size: 10px;
+    z-index: 1301;
+    display: none;
+}
+
+#parameter-provider-configuration-advanced {
+    display: none;
+}
+
+#parameter-provider-configuration div.configuration-tab {
+    display: none;
+}
+
+.parameter-provider-editable.setting-field {
+    height: 32px;
+}
+
+#parameter-provider-referencing-components {
+    padding: 2px;
+}
+
+/* parameter-provider settings */
+
+#parameter-provider-name {
+    width: 250px;
+    float: left;
+}
+
+#parameter-provider-bundle {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+}
+
+/*
+    Comments
+*/
+
+#parameter-provider-comments {
+    height: 100%;
+}
+
+#read-only-parameter-provider-comments {
+    height: 100%;
+    white-space: pre-wrap;
+}
+
+/*
+    Fetch parameters dialog
+*/
+
+#fetch-parameters-dialog {
+    position: absolute;
+    overflow: hidden;
+    min-width: 1048px;
+    height: 62%;
+    font-size: 10px;
+    z-index: 1301;
+    display: none;
+}
+
+#fetch-parameters-dialog .dialog-content {
+    bottom: 48px;
+}
+
+#fetch-parameters-dialog div.create-parameter-context-label {
+    display: inline-block;
+    line-height: normal;
+}
+
+#fetch-parameters-dialog .dialog-content div.settings-left {
+    float: left;
+    width: 33%;
+}
+
+#fetch-parameters-dialog .dialog-content div.settings-center {
+    float: left;
+    width: 33%;
+}
+
+#fetch-parameters-dialog .dialog-content div.settings-right {
+    float: left;
+    width: 30%;
+    height: 100%;
+    position: absolute;
+    left: calc(72% - 20px);
+}
+
+#parameter-groups-table {
+    position: relative;
+    height: 300px;
+}
+
+#fetch-parameters-referencing-components {
+    border: 0 solid #CCCCCC;
+    overflow: auto;
+    padding: 2px;
+}
+
+#fetch-parameters-update-steps li {
+    width: 300px;
+    margin-bottom: 2px;
+}
+
+#fetched-parameters-listing-container {
+    position: relative;
+    overflow-x: auto;
+    border: 0 solid #CCCCCC;
+    padding: 2px;
+    min-height: 100px;
+    max-height: 343px;
+    min-width: 150px;
+    max-width: 326px;
+}
+
+#fetched-parameters-listing li,
+#fetched-parameters-listing span {
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+}
+
+ol#fetched-parameters-listing ul {
+    padding-bottom: 4px;
+}
+
+ul.fetched-parameters {
+    padding: 0 0 2px 0;
+    list-style-type: disc;
+}
+
+div.referencing-component-block {
+    margin-bottom: 6px;
+}
+
+span.referencing-component-title {
+    margin-left: 5px;
+    font-weight: bold
+}
+
+span.referencing-component-count {
+    margin-left: 5px;
+    color: #aaa;
+}
+
+ul.referencing-component-listing {
+    margin-left: 20px;
+    margin-bottom: 6px;
+}
+
+ul.referencing-component-listing li {
+    margin-bottom: 6px;
+    white-space: nowrap;
+}
+
+div.referencing-component-state {
+    width: 13px;
+}
+
+div.referencing-component-state.disabled:before {
+    content: '\e802';
+    font-family: flowfont;
+}
+
+div.referencing-component-state.enabled:before {
+    content: '\f0e7';
+    font-family: FontAwesome;
+}
+
+div.referencing-component-bulletins {
+    float: left;
+    margin-left: 5px;
+    width: 12px;
+    height: 12px;
+    background-color: transparent;
+    display: none;
+}
+
+div.referencing-component-bulletins:before {
+    font-family: FontAwesome;
+    content: "\f24a";
+    font-size: 16px;
+    color: #004849;
+}
+
+div.service.expansion-button {
+    float: left;
+    margin-right: 4px;
+    width: 10px;
+    height: 10px;
+}
+
+span.referencing-component-active-thread-count {
+    margin-left: 5px;
+    float: left;
+}
+
+span.referencing-component-name {
+    margin-left: 5px;
+    margin-right: 5px;
+}
+
+span.referencing-component-type {
+    color: #aaa;
+    font-style: italic;
+}
+
+div.referencing-component-references {
+    margin-left: 2px;
+    margin-top: 0;
+    clear: left;
+}
+
+.fetch-parameters-referencing-components-template {
+    padding-left: 14px;
+}
+
+div.fetch-parameters-canceling {
+    position: absolute;
+    bottom: 10px;
+    right: 8px;
+}
+
+#fetch-parameters-name {
+    float: left;
+    max-width: 280px;
+    text-overflow: ellipsis;
+}
+
+#fetch-parameters-bulletins {
+    float: left;
+    margin-left: 5px;
+    width: 12px;
+    height: 12px;
+    background-color: transparent;
+    display: none;
+}
+
+#fetch-parameters-referencing-components {
+    border: 0 solid #CCCCCC;
+    padding: 2px;
+    overflow: auto;
+    white-space: nowrap;
+}
+
+div.fetch-parameters-referencing-components {
+    width: 16px;
+    height: 16px;
+    background-color: transparent;
+    float: right;
+}
+
+#fetch-parameters-progress {
+    margin-top: 2px;
+}
+
+#fetch-parameters-progress li {
+    line-height: 16px;
+}
+
+#affected-referencing-components-container,
+#fetch-parameter-referencing-components-container {
+    position: absolute;
+    bottom: 10px;
+    top: 108px;
+    width: calc(100% - 6px);
+    overflow: auto;
+    border: 0 solid #CCCCCC;
+    padding: 2px;
+}
+
+#affected-referencing-components-container div.referencing-component-references {
+    margin: 0;
+}
+
+.affected-referencing-components-template {
+    position: relative;
+    left: 15px;
+    width: calc(100% - 15px);
+}
+
+span.parameter-provider-referencing-component-name {
+    margin-left: 5px;
+    max-width: calc(100% - 30px);
+}
+
+#checkbox-container {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+}
+
+/*
+    Slickgrid selectable parameters table
+*/
+#selectable-parameters-table {
+    position: absolute;
+    height: 267px;
+    width: 332px;
+}
+
+#selectable-parameters-table .slick-cell-checkboxsel {
+    padding: 1px 2px 5px 4px;
+}
+
+#selectable-parameters-table .slick-cell-checkboxsel.selected {
+    padding: 0px 2px 6px 4px;
+}
+
+#selectable-parameters-table .slick-column-name input {
+    margin: 5px 3px 3px 4px;
+}
+
+#selectable-parameters-table .slick-pane-left .slick-viewport {
+    overflow-x: hidden;
+}
+
+#selectable-parameters-table input[type="checkbox"] {
+    margin-top: 2px;
+    width: 0;
+}
+
+#selectable-parameters-table input[type="checkbox"]:focus {
+    box-shadow: none;
+    border: none;
+}
+
+#selectable-parameters-table .slick-column-name input[type="checkbox"] {
+    margin-top: 5px;
+}
+
+#selectable-parameters-table .slick-column-name input[checked="checked"]:before {
+    margin-top: 2px;
+}
+
+#selectable-parameters-table input[type="checkbox"]:before {
+    content: url(../images/inputCheckbox.png);
+    cursor: pointer;
+    clip-path: polygon(0% -16%, 50% -16%, 50% 104%, 0% 104%);
+    position: absolute;
+    margin-left: 1px;
+    margin-top: 3px;
+}
+
+#selectable-parameters-table input[checked="checked"]:before {
+    content: url(../images/inputCheckbox.png);
+    cursor: pointer;
+    clip-path: polygon(50% -16%, 100% -16%, 100% 116%, 50% 116%);
+    margin-left: -11px;
+    margin-top: 3px;
+}
+
+#selectable-parameters-table input[type="checkbox"].disabled:before {
+    content: url(../images/inputCheckbox.png);
+    opacity: .5;
+    cursor: not-allowed;
+    clip-path: polygon(0% -16%, 50% -16%, 50% 104%, 0% 104%);
+    position: absolute;
+    margin-left: 1px;
+    margin-top: 3px;
+}
+
+#selectable-parameters-table input[checked="checked"].disabled:before {
+    content: url(../images/inputCheckbox.png);
+    opacity: .5;
+    cursor: not-allowed;
+    clip-path: polygon(50% -16%, 100% -16%, 100% 116%, 50% 116%);
+    margin-left: -11px;
+    margin-top: 3px;
+}
+
+#parameter-groups-table .slick-pane-top .slick-viewport-top .grid-canvas-top,
+#selectable-parameters-table .slick-pane-top .slick-viewport .grid-canvas-top {
+    width: 100% !important;
+}
+
+#selectable-parameters-table .slick-pane-top .slick-viewport-top .grid-canvas-top .slick-cell.l1 {
+    padding-top: 1px;
+}
+
+#selectable-parameters-table .slick-pane-top .slick-viewport-top .slick-cell.l1:not(.selected) .table-cell {
+    margin-top: 5px;
+}
+
+#fetched-parameters-listing .slick-cell-checkboxsel {
+    background: #f0f0f0;
+    border-right-color: silver;
+    border-right-style: solid;
+}
+
+.slick-columnpicker {
+    border: 1px solid #718BB7;
+    background: #f0f0f0;
+    padding: 6px;
+    -moz-box-shadow: 2px 2px 2px silver;
+    -webkit-box-shadow: 2px 2px 2px silver;
+    box-shadow: 2px 2px 2px silver;
+    min-width: 100px;
+    cursor: default;
+}
+
+.slick-columnpicker li {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    background: none;
+}
+
+.slick-columnpicker input {
+    margin: 4px;
+}
+
+.slick-columnpicker li a {
+    display: block;
+    padding: 4px;
+    font-weight: bold;
+}
+
+.slick-columnpicker li a:hover {
+    background: white;
+}
+
+#selectable-parameters-buttons {
+    position: relative;
+    padding-bottom: 4px;
+}
+
+.selectable-parameters-buttons {
+    line-height: 30px;
+    border: 1px solid #CCDADB;
+    background-color: rgba(249,250,251,1);
+    color: #004849;
+    padding: 0 7px;
+    float: left;
+    width: inherit;
+    margin-right: 8px;
+    display: flex;
+    align-items: center;
+    flex-direction: row;
+}
+
+.selectable-parameters-buttons span {
+    font-size: 12px;
+    text-transform: uppercase;
+}
+
+.selectable-parameters-buttons .fa {
+    font-size: 14px;
+    margin-left: -2px;
+    margin-right: 4px;
+}
+
+button.selectable-parameters-buttons:hover {
+    border: 1px solid #004849;
+}
+
+#create-parameter-context-container {
+    line-height: 12px;
+}
+
+.fetch-parameters-dialog-message {
+    position: absolute;
+    top: 537px;
+    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/jquery/statusbar/jquery.statusbar.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.css
index c13f7c025d..ddff90653f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.css
@@ -25,11 +25,12 @@
     height : 32px;
 }
 
-.dialog-status-bar[state] + .dialog-content {
+.dialog-status-bar[state] + .dialog-content,
+.dialog-status-bar[alerts] + .dialog-content {
     top: 104px;
 }
 
-.dialog-status-bar:not([state]),
+.dialog-status-bar:not([state]):not([alerts]),
 .dialog-status-bar:not([state]) text.run-status-icon,
 .dialog-status-bar-bulletins-content,
 .dialog-status-bar-bulletins[count="0"],
@@ -123,11 +124,12 @@
     float : right;
     margin-left : 22px;
     margin-top : 5px;
-    background-color : rgba(36,36,36,0.75);
-    font-family: Arial;
+    background-color: rgba(0, 0, 0, 0.80) !important;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25);
+    font-family: Roboto;
     font-weight : bold;
-    font-size : 12px;
-    padding  : 10px 10px 10px 20px;
+    font-size : 13px;
+    padding  : 10px;
     width : auto;
     max-width : 490px;
 }
@@ -138,4 +140,4 @@
 
 .dialog-status-bar-buttons *.disabled-button {
     display:none;
-}
\ No newline at end of file
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.js
index c16e314271..c7c9afa745 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/jquery/statusbar/jquery.statusbar.js
@@ -27,8 +27,7 @@
     // static key path variables
     var PROCESSOR_ID_KEY = 'component.id',
         ACTIVE_THREAD_COUNT_KEY = 'status.aggregateSnapshot.activeThreadCount',
-        RUN_STATUS_KEY = 'status.aggregateSnapshot.runStatus',
-        BULLETINS_KEY = 'bulletins';
+        RUN_STATUS_KEY = 'status.aggregateSnapshot.runStatus'
 
     var isUndefined = function (obj) {
         return typeof obj === 'undefined';
@@ -52,18 +51,28 @@
 
         /**
          * Initializes the status bar.
+         *
+         * @param type - the type of component or service for the status bus
          */
-        init: function () {
+        init: function (type) {
+
             // get the combo
             var bar = $(this).addClass('dialog-status-bar');
 
-            bar.html('<text class="run-status-icon"></text>'+
-                     '<span class="dialog-status-bar-state"></span>'+
-                     '<span class="dialog-status-bar-threads" count="0"></span>'+
-                     '<div class="dialog-status-bar-bulletins fa fa-sticky-note-o" count="0">'+
-                         '<div class="dialog-status-bar-bulletins-content"></div>'+
-                     '</div>'+
-                     '<div class="dialog-status-bar-buttons"></div>');
+            if (type === 'processor') {
+                bar.html('<text class="run-status-icon"></text>'+
+                    '<span class="dialog-status-bar-state"></span>'+
+                    '<span class="dialog-status-bar-threads" count="0"></span>'+
+                    '<div class="dialog-status-bar-bulletins fa fa-sticky-note-o" count="0">'+
+                    '<div class="dialog-status-bar-bulletins-content"></div>'+
+                    '</div>'+
+                    '<div class="dialog-status-bar-buttons"></div>');
+            } else { // parameter provider
+                bar.html('<div class="dialog-status-bar-bulletins fa fa-sticky-note-o" count="0">'+
+                    '<div class="dialog-status-bar-bulletins-content"></div>'+
+                    '</div>');
+            }
+
             return bar;
         },
 
@@ -93,28 +102,36 @@
         /**
          * Initializes the synchronization process to the canvas element
          *
-         * @param id - id value of the processor to observe
+         * @param data - object of type or service to observe
          * @param cb - callback to execute when a mutation is detected
          */
-        observe: function(id,cb) {
+        observe: function(data,cb) {
             var bar = $(this);
-            var g = document.querySelector('g[id="id-'+id+'"]');
 
-            //perform the initial set
-            bar.statusbar('set',id);
+            if (data.processor) {
+                // id value of the processor to observe
+                var id = data.processor;
+                var g = document.querySelector('g[id="id-'+id+'"]');
 
-            //create and store an observer
-            bar.data('observer',new MutationObserver(function(mutations){
-                bar.statusbar('set',id);
-                if(typeof cb == 'function'){
-                    cb();
-                }
-            }));
+                //perform the initial set
+                bar.statusbar('set', data);
+
+                //create and store an observer
+                bar.data('observer',new MutationObserver(function(mutations){
+                    bar.statusbar('set',data);
+                    if(typeof cb == 'function'){
+                        cb();
+                    }
+                }));
 
-            //initialize the observer
-            bar.data('observer').observe(g,{attributes:true,childList:true,subtree:true});
+                //initialize the observer
+                bar.data('observer').observe(g,{attributes:true,childList:true,subtree:true});
 
-            return bar.data('observer');
+                return bar.data('observer');
+            } else { // parameter provider bulletins
+                //perform the initial set
+                return bar.statusbar('set', data);
+            }
         },
 
         /**
@@ -240,38 +257,48 @@
         /**
          * Set the status bar display values
          *
-         * @param id - processor id to evaluate
+         * @param data - object of type or service to set
          */
-        set : function(id) {
+        set : function(data) {
             var bar = $(this),
-                obj = d3.select('#id-' + id).datum(),
-                bulletinList = $("<ul></ul>"),
-                runStatus = getKeyValue(obj,RUN_STATUS_KEY),
-                activeThreadCount = getKeyValue(obj,ACTIVE_THREAD_COUNT_KEY),
-                bulletins = getKeyValue(obj,BULLETINS_KEY);
-
-            //set the values
-            if(isDefinedAndNotNull(runStatus) &&
-                isDefinedAndNotNull(activeThreadCount) &&
-                isDefinedAndNotNull(bulletins) &&
-                Array.isArray(bulletins)) {
+                processorId,
+                obj,
+                runStatus,
+                activeThreadCount,
+                bulletins;
+
+            if (data.processor) {
+                processorId = data.processor;
+                obj = d3.select('#id-' + processorId).datum();
+                runStatus = getKeyValue(obj,RUN_STATUS_KEY);
+                activeThreadCount = getKeyValue(obj,ACTIVE_THREAD_COUNT_KEY);
+                bulletins = data.bulletins;
+            } else if (data.provider) {
+                bulletins = data.provider;
+            }
 
+            // set the values
+            if (isDefinedAndNotNull(runStatus) && isDefinedAndNotNull(activeThreadCount)) {
                 bar.attr('state',runStatus.toUpperCase());
+                bar.attr('alerts', 'true');
                 bar.find('.dialog-status-bar-state').text(runStatus);
                 bar.find('.dialog-status-bar-threads').attr('count',activeThreadCount);
                 bar.find('.dialog-status-bar-threads').attr('title',activeThreadCount+' active threads');
                 bar.find('.dialog-status-bar-threads').text('('+activeThreadCount+')');
-                $.each(bulletins, function(i,item){
-                    if(item.canRead){
-                       bulletinList.append($('<li>'+item.bulletin.timestamp+' '+item.bulletin.level+'<br/>'+item.bulletin.message+'<br>&nbsp;</li>'));
-                    }
-                });
-                var bulletinCount = bulletinList.find('li').length;
-                bar.find('.dialog-status-bar-bulletins-content').html((bulletinCount > 0)?bulletinList:'');
+            }
+
+            // set the bulletins
+            if (isDefinedAndNotNull(bulletins)) {
+                var bulletinCount = bulletins.find('li').length;
+                bar.find('.dialog-status-bar-bulletins-content').html((bulletinCount > 0)?bulletins:'');
                 bar.find('.dialog-status-bar-bulletins').attr('count',bulletinCount);
 
-                //update the button state
-                bar.statusbar('refreshButtons');
+                if (data.processor) {
+                    // update the button state
+                    bar.statusbar('refreshButtons');
+                } else {
+                    bar.attr('alerts', 'true');
+                }
             }
             return bar;
         }
@@ -285,4 +312,4 @@
         }
     };
 
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
index e51f3634a7..530893367d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas-bootstrap.js
@@ -49,6 +49,7 @@
                 'nf.ConnectionConfiguration',
                 'nf.ControllerService',
                 'nf.ReportingTask',
+                'nf.ParameterProvider',
                 'nf.PolicyManagement',
                 'nf.ProcessorConfiguration',
                 'nf.ProcessGroupConfiguration',
@@ -84,8 +85,8 @@
                 'nf.ng.Canvas.OperateCtrl',
                 'nf.ng.BreadcrumbsDirective',
                 'nf.ng.DraggableDirective'],
-            function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProc [...]
-                return factory($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguratio [...]
+            function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfParameterProvider, nfPolicyManagement, nfProcessor [...]
+                return factory($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfParameterProvider, nfPolicyManagement, nf [...]
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = factory(require('jquery'),
@@ -118,6 +119,7 @@
             require('nf.ConnectionConfiguration'),
             require('nf.ControllerService'),
             require('nf.ReportingTask'),
+            require('nf.ParameterProvider'),
             require('nf.PolicyManagement'),
             require('nf.ProcessorConfiguration'),
             require('nf.ProcessGroupConfiguration'),
@@ -184,6 +186,7 @@
             root.nf.ConnectionConfiguration,
             root.nf.ControllerService,
             root.nf.ReportingTask,
+            root.nf.ParameterProvider,
             root.nf.PolicyManagement,
             root.nf.ProcessorConfiguration,
             root.nf.ProcessGroupConfiguration,
@@ -220,7 +223,7 @@
             root.nf.ng.BreadcrumbsDirective,
             root.nf.ng.DraggableDirective);
     }
-}(this, function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfPolicyManagement, nfProcessorConfiguration, nfProcessG [...]
+}(this, function ($, angular, nfCommon, nfCanvasUtils, nfErrorHandler, nfClient, nfDialog, nfStorage, nfCanvas, nfGraph, nfContextMenu, nfQuickSelect, nfShell, nfParameterContexts, nfSettings, nfActions, nfSnippet, nfQueueListing, nfVerify, nfVariableRegistry, nfComponentState, nfFlowVersion, nfComponentVersion, nfDraggable, nfConnectable, nfStatusHistory, nfBirdseye, nfConnectionConfiguration, nfControllerService, nfReportingTask, nfParameterProvider, nfPolicyManagement, nfProcessorConf [...]
 
     var config = {
         urls: {
@@ -349,7 +352,7 @@
                     nfShell.init(nfContextMenu);
                     nfNgBridge.injector.get('headerCtrl').init();
                     nfSettings.init();
-                    nfParameterContexts.init();
+                    nfParameterContexts.init(nfParameterProvider);
                     nfActions.init();
                     nfQueueListing.init();
                     nfVerify.init();
@@ -370,8 +373,14 @@
 
                     // initialize the connection config and invert control of the birdseye and graph
                     nfConnectionConfiguration.init(nfBirdseye, nfGraph, configDetails.defaultBackPressureObjectThreshold, configDetails.defaultBackPressureDataSizeThreshold);
-                    nfControllerService.init(nfControllerServices, nfReportingTask);
+                    nfControllerService.init(nfControllerServices, nfReportingTask, nfParameterProvider);
                     nfReportingTask.init(nfSettings);
+                    nfParameterProvider.init({
+                        nfSettings: nfSettings,
+                        statusBarOptions: {
+                            supportsStatusBar : true
+                        }
+                    });
                     nfPolicyManagement.init();
                     nfProcessorConfiguration.init({
                         supportsStatusBar : true,
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
index 086f31f7ff..72d37daa22 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-controller-service.js
@@ -68,7 +68,9 @@
 }(this, function ($, d3, nfErrorHandler, nfCommon, nfDialog, nfStorage, nfClient, nfSettings, nfUniversalCapture, nfCustomUi, nfVerify, nfCanvasUtils, nfProcessor) {
     'use strict';
 
-    var nfControllerServices, nfReportingTask;
+    var nfControllerServices,
+        nfReportingTask,
+        nfParameterProvider;
 
     var config = {
         edit: 'edit',
@@ -251,6 +253,15 @@
                 if (referencingComponentState.length) {
                     updateReferencingSchedulableComponentState(referencingComponentState, reference);
                 }
+            } else if (reference.referenceType === 'ParameterProvider') {
+                // reload
+                nfParameterProvider.reload(reference.id);
+
+                // update the validation errors of this parameter provider
+                var referencingComponentState = $('div.' + reference.id + '-state');
+                if (referencingComponentState.length) {
+                    updateValidationErrors(referencingComponentState, reference);
+                }
             } else {
                 // reload the referencing services
                 reloadControllerService(serviceTable, reference.id);
@@ -391,6 +402,42 @@
         });
     };
 
+    /**
+     * Updates validation errors. This is used for parameter providers and registry clients.
+     *
+     * @param validationErrorIcon
+     * @param referencingComponent
+     */
+    var updateValidationErrors = function (validationErrorIcon, referencingComponent) {
+        var currentValidationErrors = validationErrorIcon.data('validation-errors');
+
+        // update the validation errors if necessary
+        if (!_.isEqual(currentValidationErrors, referencingComponent.validationErrors)) {
+            validationErrorIcon.data('validation-errors', referencingComponent.validationErrors);
+
+            // if there are validation errors update them
+            if (!nfCommon.isEmpty(referencingComponent.validationErrors)) {
+                var list = nfCommon.formatUnorderedList(referencingComponent.validationErrors);
+
+                // update existing tooltip or initialize a new one if appropriate
+                if (validationErrorIcon.data('qtip')) {
+                    validationErrorIcon.qtip('option', 'content.text', list);
+                } else {
+                    validationErrorIcon.show().qtip($.extend({},
+                        nfCanvasUtils.config.systemTooltipConfig,
+                        {
+                            content: list
+                        }));
+                }
+            } else {
+                if (validationErrorIcon.data('qtip')) {
+                    validationErrorIcon.removeData('validation-errors').qtip('api').destroy(true);
+                }
+                validationErrorIcon.hide();
+            }
+        }
+    };
+
     /**
      * Updates the bulletins for all referencing components.
      *
@@ -444,6 +491,7 @@
         var processors = $('<ul class="referencing-component-listing clear"></ul>');
         var services = $('<ul class="referencing-component-listing clear"></ul>');
         var tasks = $('<ul class="referencing-component-listing clear"></ul>');
+        var providers = $('<ul class="referencing-component-listing clear"></ul>');
         var unauthorized = $('<ul class="referencing-component-listing clear"></ul>');
         $.each(referencingComponents, function (_, referencingComponentEntity) {
             // check the access policy for this referencing component
@@ -582,6 +630,46 @@
                     // reporting task
                     var reportingTaskItem = $('<li></li>').append(reportingTaskState).append(reportingTaskBulletins).append(reportingTaskLink).append(reportingTaskType).append(reportingTaskActiveThreadCount);
                     tasks.append(reportingTaskItem);
+                } else if (referencingComponent.referenceType === 'ParameterProvider') {
+                    var parameterProviderLink = $('<span class="referencing-component-name link"></span>').text(referencingComponent.name).on('click', function () {
+                        var parameterProvidersGrid = $('#parameter-providers-table').data('gridInstance');
+                        var parameterProvidersData = parameterProvidersGrid.getData();
+
+                        // select the selected row
+                        var row = parameterProvidersData.getRowById(referencingComponent.id);
+                        parameterProvidersGrid.setSelectedRows([row]);
+                        parameterProvidersGrid.scrollRowIntoView(row);
+
+                        // close the dialog and shell
+                        referenceContainer.closest('.dialog').modal('hide');
+
+                        $('#settings-tabs').find('li:eq(4)').click();
+
+                        // adjust the table size
+                        parameterProvidersGrid.resizeCanvas();
+                    });
+
+                    // provider state - used to show the validation errors
+                    var providerState = $('<div class="referencing-component-state invalid"></div>').addClass(referencingComponent.id + '-state');
+                    if (nfCommon.isEmpty(referencingComponent.validationErrors)) {
+                        providerState.hide();
+                    } else {
+                        updateValidationErrors(providerState, referencingComponent);
+                    }
+
+                    // bulletin
+                    var providerBulletins = $('<div class="referencing-component-bulletins"></div>').addClass(referencingComponent.id + '-bulletins');
+                    if (!nfCommon.isEmpty(referencingComponentEntity.bulletins)) {
+                        updateBulletins(referencingComponentEntity.bulletins, providerBulletins);
+                    }
+
+                    // type
+                    var providerType = $('<span class="referencing-component-type"></span>').text(referencingComponent.type);
+
+                    // parameter provider
+                    var providerItem = $('<li></li>').append(providerState).append(providerBulletins).append(parameterProviderLink).append(providerType);
+
+                    providers.append(providerItem);
                 }
             }
         });
@@ -614,6 +702,7 @@
         createReferenceBlock('Processors', processors);
         createReferenceBlock('Reporting Tasks', tasks);
         createReferenceBlock('Controller Services', services);
+        createReferenceBlock('Parameter Providers', providers);
         createReferenceBlock('Unauthorized', unauthorized);
 
         // now that the dom elements are in place, we can show the bulletin icons.
@@ -734,7 +823,7 @@
                     referencingComponentRevisions[referencingComponentEntity.id] = nfClient.getRevision(referencingComponentEntity);
                 }
             } else {
-                if (referencingComponent.referenceType !== 'ControllerService') {
+                if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask') {
                     referencingComponentRevisions[referencingComponentEntity.id] = nfClient.getRevision(referencingComponentEntity);
                 }
             }
@@ -948,7 +1037,7 @@
             var referencingComponents = service.referencingComponents;
             $.each(referencingComponents, function (_, referencingComponentEntity) {
                 var referencingComponent = referencingComponentEntity.component;
-                if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask') {
+                if (referencingComponent.referenceType === 'Processor' || referencingComponent.referenceType === 'ReportingTask' || referencingComponent.referenceType === 'ParameterProvider') {
                     referencingSchedulableComponents.push(referencingComponent.id);
                 }
             });
@@ -1753,9 +1842,10 @@
         /**
          * Initializes the controller service configuration dialog.
          */
-        init: function (nfControllerServicesRef, nfReportingTaskRef) {
+        init: function (nfControllerServicesRef, nfReportingTaskRef, nfParameterProviderRef) {
             nfControllerServices = nfControllerServicesRef;
             nfReportingTask = nfReportingTaskRef;
+            nfParameterProvider = nfParameterProviderRef;
 
             // initialize the configuration dialog tabs
             $('#controller-service-configuration-tabs').tabbs({
@@ -1862,7 +1952,7 @@
                     }
                 }
             });
-            
+
             // initialize the bulletin combo
             $('#controller-service-bulletin-level-combo').combo({
                 options: [{
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 62b972196b..6b4bfc30c6 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
@@ -86,6 +86,8 @@
 }(this, function ($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfNgBridge, nfErrorHandler, nfFilteredDialogCommon, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement, nfProcessor, nfProcessGroup, nfProcessGroupConfiguration, _) {
     'use strict';
 
+    var nfParameterProvider;
+
     var config = {
         urls: {
             parameterContexts: '../nifi-api/parameter-contexts'
@@ -132,6 +134,29 @@
         return nfCommon.escapeHtml(dataContext.component.name);
     };
 
+    /**
+     * Formatter for the provider column.
+     *
+     * @param {type} row
+     * @param {type} cell
+     * @param {type} value
+     * @param {type} columnDef
+     * @param {type} dataContext
+     * @returns {String}
+     */
+    var providerFormatter = function (row, cell, value, columnDef, dataContext) {
+        var component = dataContext.component ? dataContext.component : null;
+
+        if (component && component.parameterProviderConfiguration) {
+            if (!dataContext.permissions.canRead) {
+                return '<span class="blank">' + nfCommon.escapeHtml(component.parameterProviderConfiguration.id) + '</span>';
+            }
+
+            return nfCommon.escapeHtml(component.parameterProviderConfiguration.component.parameterGroupName +
+                ' from ' + component.parameterProviderConfiguration.component.parameterProviderName);
+        }
+    };
+
     /**
      * Sorts the specified data using the specified sort details.
      *
@@ -216,6 +241,8 @@
         $('#parameter-process-group-id').text('').removeData('revision');
         $('#parameter-referencing-components-context').removeClass('unset').attr('title', '').text('');
 
+        $('#parameter-context-provider-setting').hide();
+
         var parameterGrid = $('#parameter-table').data('gridInstance');
         var parameterData = parameterGrid.getData();
         parameterGrid.setSelectedRows([]);
@@ -1053,7 +1080,7 @@
                 referencingComponents: originalParameter.referencingComponents,
                 previousValue: originalParameter.value,
                 previousDescription: originalParameter.description,
-                isEditable: originalParameter.isEditable,
+                isEditable: originalParameter.isEditable || originalParameter.provided !== true,
                 isEmptyStringSet: serializedParam.isEmptyStringSet,
                 isNew: originalParameter.isNew,
                 hasValueChanged: serializedParam.hasValueChanged,
@@ -1086,12 +1113,11 @@
         $('#parameter-context-dialog').modal('refreshButtons');
     };
 
-
     var hasParameterContextChanged = function (parameterContextEntity) {
-        var parameters = marshalParameters();
         var proposedParamContextName = $('#parameter-context-name').val();
         var proposedParamContextDesc = $('#parameter-context-description-field').val();
         var inheritedParameterContexts = marshalInheritedParameterContexts();
+        var componentDescription = _.get(parameterContextEntity, 'component.description', '');
 
         var inheritedParameterContextEquals = isInheritedParameterContextEquals(parameterContextEntity, inheritedParameterContexts);
         if (inheritedParameterContextEquals) {
@@ -1100,15 +1126,23 @@
             $('#inherited-parameter-contexts-message').removeClass('hidden');
         }
 
-        if (_.isEmpty(parameters) &&
-            proposedParamContextName === _.get(parameterContextEntity, 'component.name') &&
-            proposedParamContextDesc === _.get(parameterContextEntity, 'component.description') &&
-            inheritedParameterContextEquals) {
-
-            return false;
+        if (parameterContextEntity.component.parameterProviderConfiguration) {
+            if (proposedParamContextName === _.get(parameterContextEntity, 'component.name') &&
+                proposedParamContextDesc === componentDescription &&
+                inheritedParameterContextEquals) {
+                return false;
+            }
         } else {
-            return true;
+            var parameters = marshalParameters();
+            if (_.isEmpty(parameters) &&
+                proposedParamContextName === _.get(parameterContextEntity, 'component.name') &&
+                proposedParamContextDesc === _.get(parameterContextEntity, 'component.description') &&
+                inheritedParameterContextEquals) {
+                return false;
+            }
         }
+
+        return true;
     };
 
     /**
@@ -1560,7 +1594,7 @@
                     description: parameterEntity.parameter.description,
                     previousValue: parameterEntity.parameter.value,
                     previousDescription: parameterEntity.parameter.description,
-                    isEditable: _.defaultTo(readOnly, false) ? false : parameterEntity.canWrite,
+                    isEditable: _.defaultTo(readOnly, false) || parameterEntity.parameter.provided ? false : parameterEntity.canWrite,
                     referencingComponents: parameterEntity.parameter.referencingComponents,
                     parameterContext: containingParameterContext,
                     isInherited: (containingParameterContext.id !== parameterContext.component.id),
@@ -2496,6 +2530,10 @@
                 markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key"></div>';
             }
 
+            if (canRead && dataContext.component.parameterProviderConfiguration) {
+                markup += '<div title="Go To Parameter Provider" class="pointer go-to-provider fa fa-long-arrow-right"></div>';
+            }
+
             return markup;
         };
 
@@ -2537,6 +2575,13 @@
                 resizable: true,
                 formatter: nameFormatter
             },
+            {
+                id: 'parameterProviderConfiguration',
+                name: 'Provider',
+                sortable: true,
+                resizable: true,
+                formatter: providerFormatter
+            },
             {
                 id: 'description',
                 name: 'Description',
@@ -2600,6 +2645,13 @@
 
                     // close the settings dialog
                     $('#shell-close-button').click();
+                } else if (target.hasClass('go-to-provider')) {
+                    if (parameterContextEntity.component.parameterProviderConfiguration) {
+                        nfParameterProvider.showParameterProvider(parameterContextEntity.component.parameterProviderConfiguration.id);
+
+                        // close the settings dialog
+                        $('#shell-close-button').click();
+                    }
                 }
             } else if (parameterContextsGrid.getColumns()[args.cell].id === 'info') {
                 if (target.hasClass('view-parameter-context')) {
@@ -2657,8 +2709,12 @@
     var nfParameterContexts = {
         /**
          * Initializes the parameter contexts page.
+         *
+         * @param nfParameterProviderRef    The nfParameterProvider module.
          */
-        init: function () {
+        init: function (nfParameterProviderRef) {
+            nfParameterProvider = nfParameterProviderRef;
+
             // parameter context refresh button
             $('#parameter-contexts-refresh-button').on('click', function () {
                 loadParameterContexts();
@@ -2926,6 +2982,36 @@
                     .prop('title', parameterContextEntity.id)
                     .text(parameterContextEntity.id);
 
+                // if provided, show the provider setting
+                if (parameterContextEntity.component.parameterProviderConfiguration) {
+                    $('#parameter-context-provider-setting').show();
+
+                    var parameterContextProviderSetting = $('#parameter-context-provider-setting').empty();
+                    var providerContent = nfCommon.escapeHtml(parameterContextEntity.component.parameterProviderConfiguration.component.parameterGroupName +
+                        ' from ' + parameterContextEntity.component.parameterProviderConfiguration.component.parameterProviderName);
+
+                    $('<div class="setting-name">Provider</div>').appendTo(parameterContextProviderSetting);
+                    var settingEl = $('<div class="setting-field"></div>');
+
+                    // provider name
+                    var providerLinkEl = $('<span id="parameter-context-parameter-provider-name" class="parameter-context-parameter-provider-name link ellipsis"></span>')
+                        .prop('title', providerContent)
+                        .text(providerContent)
+                        .on('click', function () {
+                            // check if there are outstanding changes
+                            handleOutstandingChanges().done(function () {
+                                // close the shell
+                                $('#shell-dialog').modal('hide');
+
+                                // show the provider in question
+                                nfParameterProvider.showParameterProvider(parameterContextEntity.component.parameterProviderConfiguration.id)
+                            });
+                        });
+
+                    providerLinkEl.appendTo(settingEl);
+                    settingEl.appendTo(parameterContextProviderSetting);
+                }
+
                 // get the reference container
                 var referencingComponentsContainer = $('#parameter-context-referencing-components');
 
@@ -2996,6 +3082,22 @@
                     // select the parameters tab
                     $('#parameter-context-tabs').find('li:eq(1)').click();
 
+                    // if parameters are provided
+                    if (parameterContextEntity.component.parameterProviderConfiguration) {
+                        // hide the add parameter button
+                        $('#add-parameter').hide();
+                    } else {
+                        // hide the provider details
+                        if (!$('#parameter-context-provider-setting').hasClass('hidden')) {
+                            $('#parameter-context-provider-setting')
+                                .empty()
+                                .addClass('hidden');
+                        }
+
+                        // show the add parameter button
+                        $('#add-parameter').show();
+                    }
+
                     // check if border is necessary
                     updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
 
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-provider.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-provider.js
new file mode 100644
index 0000000000..6e4ff661e2
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-provider.js
@@ -0,0 +1,3317 @@
+/*
+ * 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.
+ */
+
+/* global define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['jquery',
+                'Slick',
+                'nf.ErrorHandler',
+                'nf.Common',
+                'nf.CanvasUtils',
+                'nf.Dialog',
+                'nf.Storage',
+                'nf.Client',
+                'nf.ControllerService',
+                'nf.ControllerServices',
+                'nf.UniversalCapture',
+                'nf.CustomUi',
+                'nf.Verify',
+                'nf.Processor',
+                'nf.ProcessGroup',
+                'nf.ParameterContexts',
+                'nf.ProcessGroupConfiguration',
+                'lodash'],
+            function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, nfStorage, nfClient, nfControllerService, nfControllerServices, nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, nfParameterContexts, nfProcessGroupConfiguration, _) {
+                return (nf.ParameterProvider = factory($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, nfStorage, nfClient, nfControllerService, nfControllerServices, nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, nfParameterContexts, nfProcessGroupConfiguration, _));
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ParameterProvider =
+            factory(require('jquery'),
+                require('Slick'),
+                require('nf.ErrorHandler'),
+                require('nf.Common'),
+                require('nf.CanvasUtils'),
+                require('nf.Dialog'),
+                require('nf.Storage'),
+                require('nf.Client'),
+                require('nf.ControllerService'),
+                require('nf.ControllerServices'),
+                require('nf.UniversalCapture'),
+                require('nf.CustomUi'),
+                require('nf.Verify'),
+                require('nf.Processor'),
+                require('nf.ProcessGroup'),
+                require('nf.ParameterContexts'),
+                require('nf.ProcessGroupConfiguration'),
+                require('lodash')));
+    } else {
+        nf.ParameterProvider = factory(root.$,
+            root.Slick,
+            root.nf.ErrorHandler,
+            root.nf.Common,
+            root.nf.CanvasUtils,
+            root.nf.Dialog,
+            root.nf.Storage,
+            root.nf.Client,
+            root.nf.ControllerService,
+            root.nf.ControllerServices,
+            root.nf.UniversalCapture,
+            root.nf.CustomUi,
+            root.nf.Verify,
+            root.nf.Processor,
+            root.nf.ProcessGroup,
+            root.nf.ParameterContexts,
+            root.nf.ProcessGroupConfiguration,
+            root._);
+    }
+}(this, function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, nfStorage, nfClient, nfControllerService, nfControllerServices, nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, nfParameterContexts, nfProcessGroupConfiguration, _) {
+    'use strict';
+
+    var nfSettings;
+    var fetchParameterProviderOptions;
+
+    var config = {
+        edit: 'edit',
+        readOnly: 'read-only',
+        urls: {
+            parameterProviders: '../nifi-api/parameter-providers',
+            api: '../nifi-api'
+        }
+    };
+
+    // load the controller services
+    var controllerServicesUri = config.urls.api + '/flow/controller/controller-services';
+
+    var groupCount = 0;
+    var parameterCount = 0;
+
+    var SENSITIVE = 'SENSITIVE';
+    var NON_SENSITIVE = 'NON_SENSITIVE';
+
+    var parameterGroupsGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24
+    };
+
+    var selectableParametersGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24,
+        asyncEditorLoading: false
+    };
+
+    // the last submitted referenced attributes
+    var referencedAttributes = null;
+
+    /**
+     * Gets the controller services table.
+     *
+     * @returns {*|jQuery|HTMLElement}
+     */
+    var getControllerServicesTable = function () {
+        return $('#controller-services-table');
+    };
+
+    /**
+     * Determines whether the user has made any changes to the parameter provider configuration
+     * that needs to be saved.
+     */
+    var isSaveRequired = function () {
+        var entity = $('#parameter-provider-configuration').data('parameterProviderDetails');
+
+        // determine if any parameter provider settings have changed
+        if ($('#parameter-provider-name').val() !== entity.component['name']) {
+            return true;
+        }
+        if ($('#parameter-provider-comments').val() !== entity.component['comments']) {
+            return true;
+        }
+
+        // defer to the properties
+        return $('#parameter-provider-properties').propertytable('isSaveRequired');
+    };
+
+
+    /**
+     * Marshals the data that will be used to update the parameter provider's configuration.
+     */
+    var marshalDetails = function () {
+        // properties
+        var properties = $('#parameter-provider-properties').propertytable('marshalProperties');
+
+        // create the parameter provider dto
+        var parameterProviderDto = {};
+        parameterProviderDto['id'] = $('#parameter-provider-id').text();
+        parameterProviderDto['name'] = $('#parameter-provider-name').val();
+        parameterProviderDto['comments'] = $('#parameter-provider-comments').val();
+
+        // set the properties
+        if ($.isEmptyObject(properties) === false) {
+            parameterProviderDto['properties'] = properties;
+        }
+
+        // create the parameter provider entity
+        var parameterProviderEntity = {};
+        parameterProviderEntity['component'] = parameterProviderDto;
+
+        // return the marshaled details
+        return parameterProviderEntity;
+    };
+
+    /**
+     * Marshals the parameter groups in the table.
+     */
+    var marshalParameterGroups = function () {
+        var groups = [];
+        var table = $('#parameter-groups-table');
+        var groupsGrid = table.data('gridInstance');
+        var groupsData = groupsGrid.getData();
+        $.each(groupsData.getItems(), function (_, g) {
+            if (g.isParameterContext || g.createNewParameterContext) {
+                var group = {
+                    'groupName': g.name,
+                    'parameterContextName': g.parameterContextName,
+                    'synchronized': true,
+                    'parameterSensitivities': g.parameterSensitivities
+                }
+
+                groups.push(group);
+            }
+        })
+
+        return groups;
+    };
+
+    /**
+     * Validates the specified details.
+     *
+     * @param providerDetails the parameter provider details to validate
+     * @param originalProviderDetails the original parameter provider details to compare changes
+     * @param existingParametersProviders existing parameter providers to verify there are no duplicates
+     * @return {boolean}
+     */
+    var validateDetails = function (providerDetails, existingParametersProviders, originalProviderDetails) {
+        var parameterProvider = providerDetails['component'];
+
+        if (parameterProvider.name === '') {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider must be specified.'
+            });
+            return false;
+        }
+
+        // make sure the parameter provider name does not use any unsupported characters
+        var parameterProviderNameRegex = /^[a-zA-Z0-9-_. ]+$/;
+        if (!parameterProviderNameRegex.test(parameterProvider.name)) {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider appears to have an invalid character or characters. Only alpha-numeric characters (a-z, A-Z, 0-9), hyphens (-), underscores (_), periods (.), and spaces ( ) are accepted.'
+            });
+            return false;
+        }
+
+        // validate the parameter provider is not a duplicate
+        var matchingParameterProvider;
+        var match;
+
+        if (nfCommon.isUndefinedOrNull(matchingParameterProvider) && originalProviderDetails.component.name !== providerDetails.component.name){
+            $.each(existingParametersProviders, function (i, provider) {
+                if (nfCommon.isUndefinedOrNull(match)) {
+                    match = _.find(provider, {name: parameterProvider.name});
+                    if (match) {
+                        matchingParameterProvider = match;
+                    }
+                }
+
+            });
+        }
+
+        if (_.isNil(matchingParameterProvider)) {
+            return true;
+        } else {
+            nfDialog.showOkDialog({
+                headerText: 'Parameter Provider Exists',
+                dialogContent: 'A Parameter Provider with this name already exists.'
+            });
+        }
+        return false;
+    };
+
+    /**
+     * Renders the specified parameter provider.
+     *
+     * @param {object} parameterProviderEntity parameter provider entity
+     */
+    var renderParameterProvider = function (parameterProviderEntity) {
+        // get the table and update the row accordingly
+        var parameterProviderGrid = $('#parameter-providers-table').data('gridInstance');
+        var parameterProviderData = parameterProviderGrid.getData();
+        var currentParameterProvider = parameterProviderData.getItemById(parameterProviderEntity.id);
+        parameterProviderData.updateItem(parameterProviderEntity.id, $.extend({
+            type: 'ParameterProvider',
+            bulletins: currentParameterProvider.bulletins
+        }, parameterProviderEntity));
+    };
+
+    /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#parameter-provider-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nfDialog.showYesNoDialog({
+                    headerText: 'Save',
+                    dialogContent: 'Save changes before going to this Controller Service?',
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var parameterProvider = $('#parameter-provider-configuration').data('parameterProviderDetails');
+                        saveParameterProvider(parameterProvider).done(function () {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+
+    /**
+     * Saves the specified parameter provider.
+     *
+     * @param {type} parameterProviderEntity parameter provider entity
+     */
+    var saveParameterProvider = function (parameterProviderEntity) {
+        // save the original provider to detect a name change
+        var originalParameterProvider = parameterProviderEntity;
+
+        // marshal the settings and properties and update the parameter provider
+        var updatedParameterProvider = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        var parameterProvidersGrid = $('#parameter-providers-table').data('gridInstance');
+        var parameterProvidersData = parameterProvidersGrid.getData();
+
+        if (validateDetails(updatedParameterProvider, parameterProvidersData.getItems(), originalParameterProvider)) {
+            updatedParameterProvider['revision'] = nfClient.getRevision(parameterProviderEntity);
+            updatedParameterProvider['disconnectedNodeAcknowledged'] = nfStorage.isDisconnectionAcknowledged();
+
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedParameterProvider),
+                url: parameterProviderEntity.uri,
+                dataType: 'json',
+                contentType: 'application/json'
+            }).done(function (response) {
+                // update the parameter provider
+                renderParameterProvider(response);
+            }).fail(nfErrorHandler.handleConfigurationUpdateAjaxError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
+
+    /**
+     * Gets a property descriptor for the parameter provider currently being configured.
+     *
+     * @param {type} propertyName property descriptor name
+     * @param {type} sensitive requested sensitive status
+     */
+    var getParameterProviderPropertyDescriptor = function (propertyName, sensitive) {
+        var details = $('#parameter-provider-configuration').data('parameterProviderDetails');
+        return $.ajax({
+            type: 'GET',
+            url: details.uri + '/descriptors',
+            data: {
+                propertyName: propertyName,
+                sensitive: sensitive
+            },
+            dataType: 'json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Handles verification results.
+     */
+    var handleVerificationResults = function (verificationResults, referencedAttributeMap) {
+        // record the most recently submitted referenced attributes
+        referencedAttributes = referencedAttributeMap;
+
+        var verificationResultsContainer = $('#parameter-provider-properties-verification-results');
+
+        // expand the dialog to make room for the verification result
+        if (verificationResultsContainer.is(':visible') === false) {
+            // show the verification results
+            $('#parameter-provider-properties').css('bottom', '40%').propertytable('resetTableSize')
+            verificationResultsContainer.show();
+        }
+
+        // show borders if appropriate
+        var verificationResultsListing = $('#parameter-provider-properties-verification-results-listing');
+        if (verificationResultsListing.get(0).scrollHeight > Math.round(verificationResultsListing.innerHeight())) {
+            verificationResultsListing.css('border-width', '1px');
+        }
+    };
+
+    /**
+     * Applies the fetched parameters of a specified Parameter Provider.
+     *
+     * @param parameterProviderEntity
+     * @returns {*}
+     */
+    var applyParametersHandler = function (parameterProviderEntity) {
+        var currentParameterProviderEntity = parameterProviderEntity;
+        var fetchParametersDialog = $('#fetch-parameters-dialog');
+
+        // clean up any tooltips that may have been generated
+        nfCommon.cleanUpTooltips($('#parameter-table'), 'div.fa-question-circle, div.fa-info');
+
+        var groups = marshalParameterGroups();
+        currentParameterProviderEntity.component.parameterGroupConfigurations = groups;
+
+        return $.Deferred(function (deferred) {
+            // updates the button model to show the close button
+            var updateToCloseButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Close',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            closeModal('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var updateToApplyOrCancelButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Apply',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            applyParametersHandler(currentParameterProviderEntity).done(function () {
+                                // reload the parameter provider
+                                nfParameterProvider.reload(parameterProviderEntity.id);
+                            });
+                        }
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    color: {
+                        base: '#E3E8EB',
+                        hover: '#C7D2D7',
+                        text: '#004849'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            confirmCancelDialog('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var cancelled = false;
+
+            // update the button model to show the cancel button
+            fetchParametersDialog.modal('setButtonModel', [{
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        cancelled = true;
+                        updateToCloseButtonModel();
+                    }
+                }
+            }]);
+
+            var requestId;
+            var handleAjaxFailure = function (xhr, status, error) {
+                // delete the request if possible
+                if (nfCommon.isDefinedAndNotNull(requestId)) {
+                    deleteUpdateRequest(currentParameterProviderEntity.id, requestId);
+                }
+
+                // update the step status
+                $('#fetch-parameters-update-steps')
+                    .find('div.fetch-parameters-step.ajax-loading')
+                    .removeClass('ajax-loading')
+                    .addClass('ajax-error');
+
+                if ($('#affected-referencing-components-container').is(':visible')) {
+                    updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                }
+
+                // update the button model
+                updateToApplyOrCancelButtonModel();
+            };
+
+            submitUpdateRequest(currentParameterProviderEntity).done(function (response) {
+                var pollUpdateRequest = function (updateRequestEntity) {
+                    var updateRequest = updateRequestEntity.request;
+                    var errored = nfCommon.isDefinedAndNotNull(updateRequest.failureReason);
+
+                    // get the request id
+                    requestId = updateRequest.requestId;
+
+                    // update the affected referencing components
+                    populateAffectedReferencingComponents(updateRequest.referencingComponents);
+
+                    // update the progress/steps
+                    populateFetchParametersUpdateStep(updateRequest.updateSteps, cancelled, errored);
+
+                    // if this request was cancelled, remove the update request
+                    if (cancelled) {
+                        deleteUpdateRequest(currentParameterProviderEntity.id, requestId);
+                    } else {
+                        // show update steps
+                        $('#fetch-parameters-update-status-container').show();
+
+                        if (updateRequest.complete === true) {
+                            if (errored) {
+                                nfDialog.showOkDialog({
+                                    headerText: 'Apply Parameter Provider Error',
+                                    dialogContent: 'Unable to complete parameter provider update request: ' + nfCommon.escapeHtml(updateRequest.failureReason)
+                                });
+                            }
+
+                            // reload referencing processors
+                            $.each(updateRequest.referencingComponents, function (_, referencingComponentEntity) {
+                                if (referencingComponentEntity.permissions.canRead === true) {
+                                    var referencingComponent = referencingComponentEntity.component;
+
+                                    // reload the processor if it's in the current group
+                                    if (referencingComponent.referenceType === 'PROCESSOR' && nfCanvasUtils.getGroupId() === referencingComponent.processGroupId) {
+                                        nfProcessor.reload(referencingComponent.id);
+                                    }
+                                }
+                            });
+
+                            // update the fetch parameter table if displayed
+                            if ($('#fetch-parameters-table').is(':visible')) {
+                                var parameterProviderGrid = $('#fetch-parameters-table').data('gridInstance');
+                                var parameterProviderData = parameterProviderGrid.getData();
+
+                                $.extend(currentParameterProviderEntity, {
+                                    revision: updateRequestEntity.parameterContextRevision,
+                                    component: updateRequestEntity.request.parameterProvider
+                                });
+
+                                var item = parameterProviderData.getItemById(currentParameterProviderEntity.id);
+                                if (nfCommon.isDefinedAndNotNull(item)) {
+                                    parameterProviderData.updateItem(currentParameterProviderEntity.id, currentParameterProviderEntity);
+                                }
+                            }
+
+                            // delete the update request
+                            deleteUpdateRequest(currentParameterProviderEntity.id, requestId);
+
+                            // update the button model
+                            updateToCloseButtonModel();
+
+                            // check if border is necessary
+                            if ($('#affected-referencing-components-container').is(':visible')) {
+                                updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                            }
+                        } else {
+                            // wait to get an updated status
+                            setTimeout(function () {
+                                getUpdateRequest(currentParameterProviderEntity.id, requestId).done(function (getResponse) {
+                                    pollUpdateRequest(getResponse);
+                                }).fail(handleAjaxFailure);
+                            }, 2000);
+                        }
+                    }
+                };
+
+                // get the parameter provider groups names
+                var parameterProviderGroupNames = [];
+                $.each(response.request.parameterProvider.parameterGroupConfigurations, function (_, parameterProviderGroup) {
+                    parameterProviderGroupNames.push(parameterProviderGroup.groupName);
+                });
+                $('#apply-groups-list')
+                    .removeClass('unset')
+                    .attr('title', parameterProviderGroupNames.join(', '))
+                    .text(parameterProviderGroupNames.join(', '));
+
+                // update the visibility
+                // left column
+                $('#fetch-parameters-usage-container').hide();
+                $('#apply-groups-container').show();
+
+                // middle column
+                $('#parameters-container').hide();
+                $('#fetch-parameters-update-status').show();
+
+                pollUpdateRequest(response);
+            }).fail(handleAjaxFailure);
+        }).promise();
+    };
+
+    /**
+     * Confirms a cancel dialog.
+     */
+    var confirmCancelDialog = function (dialog) {
+        nfDialog.showYesNoDialog({
+            headerText: 'Fetch Parameters',
+            dialogContent: 'Are you sure you want to cancel?',
+            noText: 'Cancel',
+            yesText: 'Yes',
+            yesHandler: function () {
+                closeModal(dialog);
+            }
+        });
+    };
+
+    /**
+     * Shows the dialog to fetch parameters.
+     *
+     * @param {object} parameterProviderEntity parameterProviderEntity
+     * @param {object} fetchParameterProviderOptions fetchParameterProviderOptions
+     */
+    var showFetchParametersDialog = function (parameterProviderEntity, fetchParameterProviderOptions) {
+        updateFetchParametersRequest(parameterProviderEntity).done(function (response) {
+            var updatedParameterProviderEntity = _.cloneDeep(response);
+            currentParameterProviderEntity = response;
+
+            groupCount = 0;
+            parameterCount = 0;
+
+            // populate the fetch parameters dialog
+            $('#fetch-parameters-id').text(updatedParameterProviderEntity.id);
+            $('#fetch-parameters-name').text(nfCommon.getComponentName(updatedParameterProviderEntity));
+
+            // set parameters contexts to be updated to none
+            $('<div class="parameter-contexts-to-create"><span class="unset">None</span></div>')
+                .appendTo($('#parameter-contexts-to-create-container'));
+
+            // list parameter contexts to update
+            var parameterContextsToUpdate = $('#parameter-contexts-to-update-container').empty();
+
+            if (!updatedParameterProviderEntity.component.referencingParameterContexts) {
+                $('<div class="parameter-contexts-to-update"><span class="unset">None</span></div>')
+                    .appendTo(parameterContextsToUpdate);
+            } else {
+                // populate contexts to be updated
+                var parameterContextNames = [];
+                $.each(updatedParameterProviderEntity.component.referencingParameterContexts, function (_, paramContext) {
+                    parameterContextNames.push(paramContext.component.name);
+                });
+                parameterContextNames.sort();
+                parameterContextsToUpdate
+                    .removeClass('unset')
+                    .attr('title', parameterContextNames.join(', '))
+                    .text(parameterContextNames.join(', '));
+            }
+
+            loadParameterGroups(updatedParameterProviderEntity);
+
+            // keep original group data
+            var initialFetchedGroups = getFetchedParameterGroups(response);
+
+            // update visibility
+            $('#fetch-parameters-permissions-parameter-contexts-message').addClass('hidden');
+            $('#fetch-parameters-permissions-affected-components-message').addClass('hidden');
+            $('#fetch-parameters-missing-context-name-message').addClass('hidden');
+
+            $('#fetch-parameters-usage-container').show();
+            $('#parameters-container').show();
+
+            // build the button model
+            var buttons = [{
+                buttonText: 'Apply',
+                color: {
+                    base: '#728E9B',
+                    hover: '#004849',
+                    text: '#ffffff'
+                },
+                disabled: function () {
+                    return disableApplyButton(updatedParameterProviderEntity, initialFetchedGroups);
+                },
+                handler: {
+                    click: function () {
+                        applyParametersHandler(updatedParameterProviderEntity).done(function () {
+                            // reload the parameter provider
+                            nfParameterProvider.reload(parameterProviderEntity.id);
+                        });
+                    }
+                }
+            }, {
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        if (!disableApplyButton(updatedParameterProviderEntity, initialFetchedGroups)) {
+                            return confirmCancelDialog('#fetch-parameters-dialog');
+                        }
+                        closeModal('#fetch-parameters-dialog');
+                    }
+                }
+            }];
+
+            // synchronize the current component canvas attributes in the status bar
+            if (fetchParameterProviderOptions.supportsStatusBar) {
+                var formattedBulletins = nfCommon.getFormattedBulletins(updatedParameterProviderEntity.bulletins);
+                var unorderedBulletins = nfCommon.formatUnorderedList(formattedBulletins);
+
+                // initialize the canvas synchronization
+                if (updatedParameterProviderEntity.bulletins.length !== 0) {
+                    $('#fetch-parameters-status-bar').statusbar(
+                        'observe',
+                        { provider: unorderedBulletins }
+                    );
+                }
+            }
+
+            $('#fetch-parameters-affected-referencing-components-container').hide();
+            $('#fetch-parameters-referencing-components-container').show();
+
+            // show the dialog
+            $('#fetch-parameters-dialog')
+                .modal('setButtonModel', buttons)
+                .modal('show');
+
+            if ($('#fetched-parameters-listing-container').is(':visible')) {
+                updateReferencingComponentsBorder($('#fetched-parameters-listing-container'));
+            }
+        });
+    };
+
+    /**
+     * Determines if the fetch parameters dialog has changes and whether the user has permissions to apply the changes.
+     *
+     * @param {object} updatedParameterProviderEntity updatedParameterProviderEntity
+     * @param {object} initialFetchedGroups initialFetchedGroups
+     */
+    var disableApplyButton = function (updatedParameterProviderEntity, initialFetchedGroups) {
+        var canReadWrite = function (component) {
+            return component.permissions.canRead && component.permissions.canWrite;
+        }
+
+        // referencing parameter contexts
+        if (updatedParameterProviderEntity.component.referencingParameterContexts) {
+            var hasReadWriteParameterContextsPermissions = updatedParameterProviderEntity.component.referencingParameterContexts.every(canReadWrite);
+
+            if (hasReadWriteParameterContextsPermissions) {
+                // user has read and write permissions on a referencing parameter context
+                $('#fetch-parameters-permissions-parameter-contexts-message').addClass('hidden');
+            } else {
+                // user does not have read and write permissions on a referencing parameter context
+                // no need to continue checking
+                $('#fetch-parameters-permissions-parameter-contexts-message').removeClass('hidden');
+                return true;
+            }
+        }
+
+        // affected referencing components
+        if (updatedParameterProviderEntity.component.affectedComponents) {
+            var hasReadWriteAffectedComponentsPermissions = updatedParameterProviderEntity.component.affectedComponents.every(canReadWrite);
+
+            if (hasReadWriteAffectedComponentsPermissions) {
+                // user has read and write permissions on an affected component
+                $('#fetch-parameters-permissions-affected-components-message').addClass('hidden');
+
+                // user has permissions to all affected component... enable Apply button
+            } else {
+                // user does not have read and write permissions on an affected component
+                // no need to continue checking
+                $('#fetch-parameters-permissions-affected-components-message').removeClass('hidden');
+                return true;
+            }
+        }
+
+        var groupsData = $('#parameter-groups-table').data('gridInstance').getData();
+        var groups = groupsData.getItems();
+
+        // new parameter contexts
+        var parameterContextNames = [];
+        $.each(groups, function (i, g) {
+            if (g.createNewParameterContext) {
+                parameterContextNames.push(g.parameterContextName);
+            }
+        })
+
+        // if any createNewParameterContext, then the name input cannot be empty
+        var isNameBlank = function (name) {
+            return nfCommon.isBlank(name);
+        }
+
+        if (!_.isEmpty(parameterContextNames)) {
+            var isAnyParameterContextNameBlank = parameterContextNames.some(isNameBlank);
+
+            if (isAnyParameterContextNameBlank) {
+                // missing a parameter context name
+                // no need to continue checking
+                $('#fetch-parameters-missing-context-name-message').removeClass('hidden');
+                return true;
+            } else {
+                $('#fetch-parameters-missing-context-name-message').addClass('hidden');
+            }
+        } else {
+            // hide the message if previously shown
+            $('#fetch-parameters-missing-context-name-message').addClass('hidden');
+        }
+
+        // parameter sensitivities
+        for (var k = 0; k < initialFetchedGroups.length; k++) {
+            var groupFromDataGrid = groups[k];
+
+            if (nfCommon.isDefinedAndNotNull(groupFromDataGrid)) {
+                if (groupFromDataGrid.isParameterContext && nfCommon.isUndefinedOrNull(groupFromDataGrid.createNewParameterContext)) {
+                    // form the initially fetched group sensitivities
+                    var groupInitiallyFetched = initialFetchedGroups[k];
+                    var initialGroupParamSensitivity = {};
+
+                    for (var param in groupInitiallyFetched.parameterSensitivities) {
+                        initialGroupParamSensitivity[param] = groupInitiallyFetched.parameterSensitivities[param] ? groupInitiallyFetched.parameterSensitivities[param] : SENSITIVE;
+                    }
+
+                    // compare
+                    if (!_.isEqual(initialGroupParamSensitivity, groupFromDataGrid.parameterSensitivities)) {
+                        // parameter sensitive has changed... do not disable the Apply button
+                        return false;
+                    }
+                }
+            }
+        }
+
+        // check for changed parameter values
+        if (updatedParameterProviderEntity.component.parameterStatus) {
+            var isChanged = function (parameterStatus) {
+                return parameterStatus.status === 'CHANGED';
+            }
+
+            var isAnyParameterValueChanged = updatedParameterProviderEntity.component.parameterStatus.some(isChanged);
+
+            if (isAnyParameterValueChanged) {
+                // a fetched parameter value has changed... do not disable the Apply button
+                return false;
+            }
+        }
+
+        return _.isEmpty(parameterContextNames);
+    };
+
+    /**
+     * Loads the specified fetched groups.
+     *
+     * @param {object} parameterProviderGroupEntity
+     * @param {boolean} if the parameters should be displayed in a read-only state regardless of permissions
+     */
+    var loadParameterGroups = function (parameterProviderGroupEntity) {
+        // providedGroups will be an array of groups
+        if (nfCommon.isDefinedAndNotNull(parameterProviderGroupEntity)) {
+            var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+            var groupsData = groupsGrid.getData();
+
+            // begin the update
+            groupsData.beginUpdate();
+
+            var parameterGroups = [];
+            $.each(parameterProviderGroupEntity.component.parameterGroupConfigurations, function (i, groupConfig) {
+                var referencingParameterContext = parameterProviderGroupEntity.component.referencingParameterContexts
+                    ? isReferencingParamContext(parameterProviderGroupEntity.component.referencingParameterContexts, groupConfig.parameterContextName)
+                    : false;
+
+                var canWriteParameterContexts;
+                if (referencingParameterContext) {
+                    canWriteParameterContexts = referencingParameterContext.permissions.canWrite;
+                } else {
+                    canWriteParameterContexts = true;
+                }
+
+                var canWriteAffectedComponents;
+                if (parameterProviderGroupEntity.component.affectedComponents) {
+                    canWriteAffectedComponents = (parameterProviderGroupEntity.component.affectedComponents).every(function (c) {
+                        return c.permissions.canRead && c.permissions.canWrite;
+                    });
+                } else {
+                    canWriteAffectedComponents = true;
+                }
+
+                var group = {
+                    id: groupCount++,
+                    hidden: false,
+                    isParameterContext: referencingParameterContext ? true : false,
+                    name: groupConfig.groupName,
+                    parameterContextName: groupConfig.parameterContextName,
+                    parameterSensitivities: groupConfig.parameterSensitivities,
+                    referencingParameterContexts: groupConfig.referencingParameterContexts ? groupConfig.referencingParameterContexts : null,
+                    enableParametersCheckboxes: canWriteParameterContexts && canWriteAffectedComponents,
+                    parameterStatus: parameterProviderGroupEntity.component.parameterStatus
+                };
+
+                parameterGroups.push({
+                    group: group
+                });
+
+                groupsData.addItem(group);
+            });
+
+            // complete the update
+            groupsData.endUpdate();
+            groupsData.reSort();
+
+            // select the first row
+            groupsGrid.setSelectedRows([0]);
+        }
+    };
+
+    /**
+     * Determines if the provided group is synced to a parameter context.
+     *
+     * @param {object} referencingParameterContexts
+     * @param {string} parameterContextName
+     * @returns {boolean}
+     */
+    var isReferencingParamContext = function (referencingParameterContexts, parameterContextName) {
+        var referencingParamContext = null;
+        $.each(referencingParameterContexts, function (i, paramContext) {
+            if (paramContext.component.name.includes(parameterContextName)) {
+                referencingParamContext = paramContext;
+            }
+        })
+
+        return referencingParamContext;
+    }
+
+    /**
+     * Loads the selectable parameters for a specified parameter group.
+     *
+     * @param groupId
+     * @param {object} parametersEntity
+     * @param {object} updatedGroup
+     */
+    var loadSelectableParameters = function (groupId, parametersEntity, updatedGroup) {
+        if (nfCommon.isDefinedAndNotNull(parametersEntity)) {
+            var selectableParametersGrid = $('#selectable-parameters-table').data('gridInstance');
+            var parametersData = selectableParametersGrid.getData();
+
+            // clear the rows
+            selectableParametersGrid.setSelectedRows([]);
+            parametersData.setItems([]);
+
+            // begin the update
+            parametersData.beginUpdate();
+
+            var isAffectedParameter = function (providerEntity, param) {
+                var isAffectedParameter = false;
+                // check for affected components
+                $.each(providerEntity.component.parameterStatus, function (i, status) {
+                    if (status.parameter.parameter.name === param) {
+                        if (status.status === 'CHANGED') {
+                            isAffectedParameter = true;
+                        }
+                    }
+                });
+                return isAffectedParameter;
+            }
+
+            var getParameterStatus = function (parameterStatus, param) {
+                return nfCommon.isDefinedAndNotNull(parameterStatus.find(function (status) { return status.parameter.parameter.name === param }))
+                    ? parameterStatus.find(function (status) { return status.parameter.parameter.name === param })
+                    : [];
+            }
+
+            var isReferencedParameter = function (paramStatus, param) {
+                var status = getParameterStatus(paramStatus, param);
+                return !_.isEmpty(status) && !_.isEmpty(status.parameter.parameter.referencingComponents);
+            }
+
+            var idx = 0;
+            var referencingParameters = 0;
+            var parameterCount = 0;
+            for (var param in parametersEntity) {
+
+                var parameter = {
+                    id: idx++,
+                    groupId: groupId,
+                    name: param,
+                    sensitivity: parametersEntity[param] ? parametersEntity[param] : SENSITIVE,
+                    isAffectedParameter: currentParameterProviderEntity.component.affectedComponents ? isAffectedParameter(currentParameterProviderEntity, param) : false,
+                    isReferencingParameter: !_.isEmpty(updatedGroup.parameterStatus) ? isReferencedParameter(updatedGroup.parameterStatus, param) : false,
+                    parameterStatus: !_.isEmpty(updatedGroup.parameterStatus) ? getParameterStatus(updatedGroup.parameterStatus, param) : []
+                }
+
+                parametersData.addItem(parameter);
+
+                parameterCount++;
+                if (parameter.isReferencingParameter === true) {
+                    referencingParameters++;
+                }
+            }
+
+            // complete the update
+            parametersData.endUpdate();
+            parametersData.reSort();
+
+            // select the the first row
+            selectableParametersGrid.setSelectedRows([0]);
+
+            // list the parameters to be created
+            loadParameterContextsToCreate();
+        }
+    };
+
+    /**
+     * Populates the affected referencing components for the specified parameter provider.
+     *
+     * @param {object} referencingComponents
+     */
+    var populateAffectedReferencingComponents = function (referencingComponents) {
+        // toggles the visibility of a container
+        var toggle = function (twist, container) {
+            if (twist.hasClass('expanded')) {
+                twist.removeClass('expanded').addClass('collapsed');
+                container.hide();
+            } else {
+                twist.removeClass('collapsed').addClass('expanded');
+                container.show();
+            }
+        };
+
+        // update visibility
+        $('#fetch-parameters-referencing-components-container').hide();
+        $('#affected-referencing-components-container').show();
+        updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+
+        $('#fetch-parameters-affected-referencing-components-container').show();
+
+        var referencingProcessors = [];
+        var referencingControllerServices = [];
+        var unauthorizedReferencingComponents = [];
+
+        var spinner = $('#fetch-parameters-affected-referencing-components-container .referencing-components-loading');
+
+        var loadingDeferred = $.Deferred(function (deferred) {
+            spinner.addClass('ajax-loading');
+            deferred.resolve();
+        });
+        loadingDeferred.then(function () {
+            resetUsage();
+        }).then(function() {
+            var parameterReferencingComponentsContainer = $('#affected-referencing-components-container').empty();
+
+            // referencing component will be undefined when a new parameter is added
+            if (nfCommon.isUndefined(referencingComponents)) {
+                // set to pending
+                $('<div class="referencing-component-container"><span class="unset">Pending Apply</span></div>').appendTo(parameterReferencingComponentsContainer);
+            } else {
+                // bin the referencing components according to their type
+                $.each(referencingComponents, function (_, referencingComponentEntity) {
+                    if (referencingComponentEntity.permissions.canRead === true && referencingComponentEntity.permissions.canWrite === true) {
+                        if (referencingComponentEntity.component.referenceType === 'PROCESSOR') {
+                            referencingProcessors.push(referencingComponentEntity);
+                        } else {
+                            referencingControllerServices.push(referencingComponentEntity);
+                        }
+                    } else {
+                        unauthorizedReferencingComponents.push(referencingComponentEntity);
+                    }
+                });
+
+                var referencingProcessGroups = {};
+
+                // bin the referencing processors according to their PG
+                $.each(referencingProcessors, function (_, referencingProcessorEntity) {
+                    if (referencingProcessGroups[referencingProcessorEntity.processGroup.id]) {
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].referencingProcessors.push(referencingProcessorEntity);
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].id = referencingProcessorEntity.processGroup.id;
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].name = referencingProcessorEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: referencingProcessorEntity.processGroup.name,
+                            id: referencingProcessorEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].referencingProcessors.push(referencingProcessorEntity);
+                    }
+                });
+
+                // bin the referencing CS according to their PG
+                $.each(referencingControllerServices, function (_, referencingControllerServiceEntity) {
+                    if (referencingProcessGroups[referencingControllerServiceEntity.processGroup.id]) {
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].referencingControllerServices.push(referencingControllerServiceEntity);
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].id = referencingControllerServiceEntity.processGroup.id;
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].name = referencingControllerServiceEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: referencingControllerServiceEntity.processGroup.name,
+                            id: referencingControllerServiceEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].referencingControllerServices.push(referencingControllerServiceEntity);
+                    }
+                });
+
+                // bin the referencing unauthorized components according to their PG
+                $.each(unauthorizedReferencingComponents, function (_, unauthorizedReferencingComponentEntity) {
+                    if (referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id]) {
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].unauthorizedReferencingComponents.push(unauthorizedReferencingComponentEntity);
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].id = unauthorizedReferencingComponentEntity.processGroup.id;
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].name = unauthorizedReferencingComponentEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: unauthorizedReferencingComponentEntity.processGroup.name,
+                            id: unauthorizedReferencingComponentEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].unauthorizedReferencingComponents.push(unauthorizedReferencingComponentEntity);
+                    }
+                });
+
+                var parameterReferencingComponentsContainer = $('#affected-referencing-components-container');
+                var groups = $('<ul class="referencing-component-listing clear"></ul>');
+
+                var referencingProcessGroupsArray = [];
+                for (var key in referencingProcessGroups) {
+                    if (referencingProcessGroups.hasOwnProperty(key)) {
+                        referencingProcessGroupsArray.push(referencingProcessGroups[key]);
+                    }
+                }
+
+                if (nfCommon.isEmpty(referencingProcessGroupsArray)) {
+                    // set to none
+                    $('<div class="referencing-component-container"><span class="unset">None</span></div>').appendTo(parameterReferencingComponentsContainer);
+                } else {
+                    //sort alphabetically
+                    var sortedReferencingProcessGroups = referencingProcessGroupsArray.sort(function (a, b) {
+                        if (a.name < b.name) {
+                            return -1;
+                        }
+                        if (a.name > b.name) {
+                            return 1;
+                        }
+                        return 0;
+                    });
+
+                    sortedReferencingProcessGroups.forEach(function (referencingProcessGroup) {
+                        // container for this pg's references
+                        var referencingPgReferencesContainer = $('<div class="referencing-component-references"></div>');
+                        parameterReferencingComponentsContainer.append(referencingPgReferencesContainer);
+
+                        // create the collapsable listing for each PG
+                        var createReferenceBlock = function (referencingProcessGroup, list) {
+                            var twist = $('<div class="expansion-button collapsed"></div>');
+                            var title = $('<span class="referencing-component-title"></span>').text(referencingProcessGroup.name);
+                            var count = $('<span class="referencing-component-count"></span>').text('(' + (referencingProcessGroup.referencingProcessors.length + referencingProcessGroup.referencingControllerServices.length + referencingProcessGroup.unauthorizedReferencingComponents.length) + ')');
+                            var referencingComponents = $('#affected-referencing-components-template').clone();
+                            referencingComponents.removeAttr('id');
+                            referencingComponents.removeClass('hidden');
+
+                            // create the reference block
+                            var groupTwist = $('<div class="referencing-component-block pointer unselectable"></div>').data('processGroupId', referencingProcessGroup.id).on('click', function () {
+                                if (twist.hasClass('collapsed')) {
+                                    groupTwist.append(referencingComponents);
+
+                                    var processorContainer = groupTwist.find('.fetch-parameters-referencing-processors');
+                                    nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-state');
+                                    nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-bulletins');
+                                    processorContainer.empty();
+
+                                    var controllerServiceContainer = groupTwist.find('.fetch-parameters-referencing-controller-services');
+                                    nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-state');
+                                    nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-bulletins');
+                                    controllerServiceContainer.empty();
+
+                                    var unauthorizedComponentsContainer = groupTwist.find('.fetch-parameters-referencing-unauthorized-components').empty();
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(processorContainer);
+                                    } else {
+                                        // sort the referencing processors
+                                        referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors.sort(nameComparator);
+
+                                        // render each and register a click handler
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors, function (_, referencingProcessorEntity) {
+                                            renderReferencingProcessor(referencingProcessorEntity, processorContainer);
+                                        });
+                                    }
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(controllerServiceContainer);
+                                    } else {
+                                        // sort the referencing controller services
+                                        referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices.sort(nameComparator);
+
+                                        // render each and register a click handler
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices, function (_, referencingControllerServiceEntity) {
+                                            renderReferencingControllerService(referencingControllerServiceEntity, controllerServiceContainer);
+                                        });
+                                    }
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(unauthorizedComponentsContainer);
+                                    } else {
+                                        // sort the unauthorized referencing components
+                                        referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents.sort(function (a, b) {
+                                            if (a.permissions.canRead === true && b.permissions.canRead === true) {
+                                                // processors before controller services
+                                                var sortVal = a.component.referenceType === b.component.referenceType ? 0 : a.component.referenceType > b.component.referenceType ? -1 : 1;
+
+                                                // if a and b are the same type, then sort by name
+                                                if (sortVal === 0) {
+                                                    sortVal = a.component.name === b.component.name ? 0 : a.component.name > b.component.name ? 1 : -1;
+                                                }
+
+                                                return sortVal;
+                                            } else {
+
+                                                // if lacking read and write perms on both, sort by id
+                                                if (a.permissions.canRead === false && b.permissions.canRead === false) {
+                                                    return a.id > b.id ? 1 : -1;
+                                                } else {
+                                                    // if only one has read perms, then let it come first
+                                                    if (a.permissions.canRead === true) {
+                                                        return -1;
+                                                    } else {
+                                                        return 1;
+                                                    }
+                                                }
+                                            }
+                                        });
+
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents, function (_, unauthorizedReferencingComponentEntity) {
+                                            if (unauthorizedReferencingComponentEntity.permissions.canRead === true) {
+                                                if (unauthorizedReferencingComponentEntity.component.referenceType === 'PROCESSOR') {
+                                                    renderReferencingProcessor(unauthorizedReferencingComponentEntity, unauthorizedComponentsContainer);
+                                                } else {
+                                                    renderReferencingControllerService(unauthorizedReferencingComponentEntity, unauthorizedComponentsContainer);
+                                                }
+                                            } else {
+                                                var referencingUnauthorizedComponentContainer = $('<li class="referencing-component-container"></li>').appendTo(unauthorizedComponentsContainer);
+                                                $('<span class="fetch-parameters-referencing-component-name link ellipsis"></span>')
+                                                    .prop('title', unauthorizedReferencingComponentEntity.id)
+                                                    .text(unauthorizedReferencingComponentEntity.id)
+                                                    .on('click', function () {
+                                                        // close the shell
+                                                        $('#shell-dialog').modal('hide');
+
+                                                        // show the component in question
+                                                        if (unauthorizedReferencingComponentEntity.referenceType === 'PROCESSOR') {
+                                                            nfCanvasUtils.showComponent(unauthorizedReferencingComponentEntity.processGroup.id, unauthorizedReferencingComponentEntity.id);
+                                                        } else if (unauthorizedReferencingComponentEntity.referenceType === 'CONTROLLER_SERVICE') {
+                                                            nfProcessGroupConfiguration.showConfiguration(unauthorizedReferencingComponentEntity.processGroup.id).done(function () {
+                                                                nfProcessGroup.enterGroup(unauthorizedReferencingComponentEntity.processGroup.id);
+                                                                nfProcessGroupConfiguration.selectControllerService(unauthorizedReferencingComponentEntity.id);
+                                                            });
+                                                        }
+                                                    })
+                                                    .appendTo(referencingUnauthorizedComponentContainer);
+                                            }
+                                        });
+                                    }
+                                } else {
+                                    groupTwist.find('.affected-referencing-components-template').remove();
+                                }
+
+                                // toggle this block
+                                toggle(twist, list);
+
+                                // update the border if necessary
+                                updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                            }).append(twist).append(title).append(count).appendTo(referencingPgReferencesContainer);
+
+                            // add the listing
+                            list.appendTo(referencingPgReferencesContainer);
+
+                            // expand the group twist
+                            groupTwist.click();
+                        };
+
+                        // create block for this process group
+                        createReferenceBlock(referencingProcessGroup, groups);
+                    });
+                }
+            }
+        })
+            .always(function () {
+                spinner.removeClass('ajax-loading');
+            });
+        return loadingDeferred.promise();
+    };
+
+    /**
+     * Loads the referencing components for this parameter provider.
+     *
+     * @param {jQuery} parameterProviderReferencingComponentsContainer
+     * @param {object} parameterProviderEntity
+     */
+    var loadParameterProviderReferencingComponents = function (parameterProviderReferencingComponentsContainer, parameterProviderEntity) {
+        parameterProviderReferencingComponentsContainer.empty();
+        var parameterProviderComponent = parameterProviderEntity;
+
+        if (nfCommon.isEmpty(parameterProviderComponent.referencingParameterContexts)) {
+            parameterProviderReferencingComponentsContainer.append('<div class="unset">No referencing components.</div>');
+            return;
+        }
+
+        // toggles the visibility of a container
+        var toggle = function (twist, container) {
+            if (twist.hasClass('expanded')) {
+                twist.removeClass('expanded').addClass('collapsed');
+                container.hide();
+            } else {
+                twist.removeClass('collapsed').addClass('expanded');
+                container.show();
+            }
+        };
+
+        var parameterContexts = $('<ul class="referencing-component-listing clear"></ul>');
+        var unauthorized = $('<ul class="referencing-component-listing clear"></ul>');
+
+        $.each(parameterProviderComponent.referencingParameterContexts, function (_, refParameterContextComponent) {
+            // check the access policy for this referencing component
+            if (refParameterContextComponent.permissions.canRead === false) {
+                var unauthorizedReferencingComponent = $('<div class="unset"></div>').text(refParameterContextComponent.id);
+                unauthorized.append(unauthorizedReferencingComponent);
+            } else {
+                var referencingComponent = refParameterContextComponent.component;
+
+                var parameterContextLink = $('<span class="referencing-component-name link"></span>')
+                    .text(referencingComponent.name)
+                    .on('click', function () {
+                        // show the component
+                        nfParameterContexts.showParameterContexts(referencingComponent.id);
+
+                        // close the dialog and shell
+                        parameterProviderReferencingComponentsContainer.closest('.dialog').modal('hide');
+                        $('#shell-close-button').click();
+                    });
+                var parameterContextItem = $('<li></li>').append(parameterContextLink);
+                parameterContexts.append(parameterContextItem);
+            }
+        });
+
+        // create the collapsable listing for each type
+        var createReferenceBlock = function (titleText, list) {
+            if (list.is(':empty')) {
+                list.remove();
+                return;
+            }
+
+            var twist = $('<div class="expansion-button expanded"></div>');
+            var title = $('<span class="referencing-component-title"></span>').text(titleText);
+            var count = $('<span class="referencing-component-count"></span>').text('(' + list.children().length + ')');
+
+            // create the reference block
+            $('<div class="referencing-component-block pointer unselectable"></div>')
+                .on('click', function () {
+                    // toggle this block
+                    toggle(twist, list);
+
+                    // update the border if necessary
+                    updateReferencingComponentsBorder(parameterProviderReferencingComponentsContainer);
+                })
+                .append(twist)
+                .append(title)
+                .append(count)
+                .appendTo(parameterProviderReferencingComponentsContainer);
+
+            // add the listing
+            list.appendTo(parameterProviderReferencingComponentsContainer);
+        };
+
+        // create blocks for each type of component
+        createReferenceBlock('Parameter Contexts', parameterContexts);
+        createReferenceBlock('Unauthorized', unauthorized);
+    };
+
+    /**
+     * Loads the groups that will be created as a parameter context.
+     *
+     */
+    var loadParameterContextsToCreate = function () {
+        var parameterContextsToCreate = $('#parameter-contexts-to-create-container').empty();
+        var parameterContextNames = [];
+
+        var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+        var groupsData = groupsGrid.getData();
+
+        $.each(groupsData.getItems(), function (_, group) {
+            if (group.createNewParameterContext) {
+                parameterContextNames.push(group.parameterContextName);
+            }
+        });
+
+        if (parameterContextNames.length === 0) {
+            $('<div class="parameter-contexts-to-create"><span class="unset">None</span></div>')
+                .appendTo(parameterContextsToCreate);
+        } else {
+            parameterContextNames.sort();
+            parameterContextsToCreate
+                .removeClass('unset')
+                .attr('title', parameterContextNames.join(', '))
+                .text(parameterContextNames.join(', '));
+        }
+    }
+
+    var getFetchedParameterGroups = function (groupEntity) {
+        var parameterGroups = [];
+        $.each(groupEntity.component.parameterGroupConfigurations, function (i, groupConfig) {
+            var referencingParameterContext = groupEntity.component.referencingParameterContexts
+                ? isReferencingParamContext(groupEntity.component.referencingParameterContexts, groupConfig.parameterContextName)
+                : false;
+
+            var canWriteParameterContexts;
+            if (referencingParameterContext) {
+                canWriteParameterContexts = referencingParameterContext.permissions.canWrite;
+            } else {
+                canWriteParameterContexts = true;
+            }
+
+            var canWriteAffectedComponents;
+            if (groupEntity.component.affectedComponents) {
+                canWriteAffectedComponents = (groupEntity.component.affectedComponents).every(function (c) {
+                    return c.permissions.canRead && c.permissions.canWrite;
+                });
+            } else {
+                canWriteAffectedComponents = true;
+            }
+
+            var index = 0;
+            var group = {
+                id: index++,
+                hidden: false,
+                isParameterContext: referencingParameterContext ? true : false,
+                name: groupConfig.groupName,
+                parameterContextName: groupConfig.parameterContextName,
+                parameterSensitivities: groupConfig.parameterSensitivities,
+                referencingParameterContexts: groupConfig.referencingParameterContexts ? groupConfig.referencingParameterContexts : null,
+                enableParametersCheckboxes: canWriteParameterContexts && canWriteAffectedComponents,
+                parameterStatus: groupEntity.component.parameterStatus
+            };
+
+            parameterGroups.push(group);
+        });
+        return parameterGroups;
+    }
+
+    /**
+     * Handles outstanding changes.
+     *
+     * @returns {deferred}
+     */
+    var handleOutstandingChanges = function () {
+        return $.Deferred(function (deferred) {
+            if ($('#fetch-parameters-update-status').is(':visible')) {
+                close();
+                deferred.resolve();
+            } else {
+                var parameterGroups = marshalParameterGroups();
+
+                // if there are no parameters there is nothing to save
+                if ($.isEmptyObject(parameterGroups)) {
+                    close();
+                    deferred.resolve();
+                } else {
+                    // see if those changes should be saved
+                    nfDialog.showYesNoDialog({
+                        headerText: 'Fetch Parameters',
+                        dialogContent: 'Save changes before leaving parameters configuration?',
+                        noHandler: function () {
+                            close();
+                            deferred.resolve();
+                        },
+                        yesHandler: function () {
+                            applyParametersHandler(currentParameterProviderEntity).done(function () {
+                                deferred.resolve();
+                            }).fail(function () {
+                                deferred.reject();
+                            });
+                        }
+                    });
+                }
+            }
+
+        }).promise();
+    };
+
+    /**
+     * Adds a border to the fetch parameters referencing components if necessary.
+     *
+     * @argument {jQuery} referenceContainer
+     */
+    var updateReferencingComponentsBorder = function (referenceContainer) {
+        // determine if it is too big
+        var tooBig = referenceContainer.get(0).scrollHeight > Math.round(referenceContainer.innerHeight()) ||
+            referenceContainer.get(0).scrollWidth > Math.round(referenceContainer.innerWidth());
+
+        // draw the border if necessary
+        if (referenceContainer.is(':visible') && tooBig) {
+            referenceContainer.css('border-width', '1px');
+        } else {
+            referenceContainer.css('border-width', '0px');
+        }
+    };
+
+    /**
+     * Determines if any of the specified referencing components are not authorized to apply parameters.
+     *
+     * @param {object} parameterProviderEntity having referencingComponents referencing components
+     * @returns {boolean}
+     */
+    var hasUnauthorizedReferencingComponent = function (parameterProviderEntity) {
+
+        if (parameterProviderEntity.permissions.canRead === false
+            || (parameterProviderEntity.permissions.canWrite === false && parameterProviderEntity.operatePermissions.canWrite === false)) {
+            return true;
+        }
+
+        var hasUnauthorized = false;
+        var referencingComponents = parameterProviderEntity.component.referencingComponents;
+        $.each(referencingComponents, function (_, referencingComponentEntity) {
+            if (hasUnauthorizedReferencingComponent(referencingComponentEntity)) {
+                hasUnauthorized = true;
+                return false;
+            }
+        });
+
+        return hasUnauthorized;
+    };
+
+    /**
+     * Used to handle closing a modal dialog
+     *
+     * * @param {string} dialog the dialog to close
+     */
+    var closeModal = function (dialog) {
+        $(dialog).modal('hide');
+    };
+
+    /**
+     * Obtains the current state of the updateRequest using the specified update request id.
+     *
+     * @param {string} parameterProviderId parameter provider id
+     * @param {string} updateRequestId update request id
+     * @returns {deferred} update request xhr
+     */
+    var getUpdateRequest = function (parameterProviderId, updateRequestId) {
+        return $.ajax({
+            type: 'GET',
+            url: config.urls.parameterProviders + '/' + encodeURIComponent(parameterProviderId) + '/apply-parameters-requests/' + encodeURIComponent(updateRequestId),
+            dataType: 'json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Deletes an updateRequest using the specified update request id.
+     *
+     * @param {string} parameterProviderId parameter provider id
+     * @param {string} updateRequestId update request id
+     * @returns {deferred} update request xhr
+     */
+    var deleteUpdateRequest = function (parameterProviderId, updateRequestId) {
+        return $.ajax({
+            type: 'DELETE',
+            url: config.urls.parameterProviders + '/' + encodeURIComponent(parameterProviderId) + '/apply-parameters-requests/' + encodeURIComponent(updateRequestId) + '?' + $.param({
+                'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
+            }),
+            dataType: 'json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Submits a parameter provider update request.
+     *
+     * @param {object} parameterProviderEntity parameter provider id
+     * @returns {deferred} update request xhr
+     */
+    var submitUpdateRequest = function (parameterProviderEntity) {
+        var requestEntity = {
+            'id': parameterProviderEntity.id,
+            'revision': nfClient.getRevision(parameterProviderEntity),
+            'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
+            'parameterGroupConfigurations': parameterProviderEntity.component.parameterGroupConfigurations
+        }
+
+        return $.ajax({
+            type: 'POST',
+            data: JSON.stringify(requestEntity),
+            url: config.urls.parameterProviders + '/' + encodeURIComponent(requestEntity.id) + '/apply-parameters-requests',
+            dataType: 'json',
+            contentType: 'application/json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Submits a parameter provider update request.
+     *
+     * @param {object} parameterProviderEntity parameter provider id
+     * @returns {deferred} update request xhr
+     */
+    var updateFetchParametersRequest = function (parameterProviderEntity) {
+        return $.ajax({
+            type: 'POST',
+            data: JSON.stringify(parameterProviderEntity),
+            url: config.urls.parameterProviders + '/' + encodeURIComponent(parameterProviderEntity.id) + '/parameters/fetch-requests'+ '?' + $.param({
+                'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
+            }),
+            dataType: 'json',
+            contentType: 'application/json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Populates the fetch parameters update steps.
+     *
+     * @param {array} updateSteps steps to update
+     * @param {boolean} cancelled whether this request has been cancelled
+     * @param {boolean} errored whether this request has errored
+     */
+    var populateFetchParametersUpdateStep = function (updateSteps, cancelled, errored) {
+        var updateStatusContainer = $('#fetch-parameters-update-steps').empty();
+
+        // go through each step
+        $.each(updateSteps, function (_, updateStep) {
+            var stepItem = $('<li></li>').text(updateStep.description).appendTo(updateStatusContainer);
+
+            $('<div class="fetch-parameters-step"></div>').addClass(function () {
+                if (nfCommon.isDefinedAndNotNull(updateStep.failureReason)) {
+                    return 'ajax-error';
+                } else {
+                    if (updateStep.complete === true) {
+                        return 'ajax-complete';
+                    } else {
+                        return cancelled === true || errored === true ? 'ajax-error' : 'ajax-loading';
+                    }
+                }
+            }).appendTo(stepItem);
+
+            $('<div class="clear"></div>').appendTo(stepItem);
+        });
+    };
+
+    /**
+     * Loads the fetched parameters for the specified parameter group.
+     *
+     * @param {object} fetchedGroup the parameter group
+     */
+    var loadFetchedParameters = function (fetchedGroup) {
+        var updatedGroup = fetchedGroup;
+        var isParameterContext = updatedGroup.isParameterContext;
+        var isCreateNewParameterContext = updatedGroup.createNewParameterContext;
+
+        $('#fetched-parameters-listing').empty();
+
+        // get an instance of the grid
+        var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+        var groupsData = groupsGrid.getData();
+
+        // show the appropriate parameters table when dialog first opens
+        if (isParameterContext) {
+            // get the active group's parameters to populate the selectable parameters container
+            loadSelectableParameters(updatedGroup.id, updatedGroup.parameterSensitivities, updatedGroup);
+
+            $('#parameters-container').show();
+            $('#selectable-parameters-container').show();
+            $('#fetched-parameters-container').hide();
+        } else {
+            $('#selectable-parameters-container').hide();
+            $('#fetched-parameters-container').show();
+        }
+
+        if (Object.keys(updatedGroup.parameterSensitivities).length !== 0) {
+            $('#create-parameter-context-checkbox-container').show();
+
+            /* Form the create parameter context checkbox */
+            var parametersCheckboxContainer = $('#create-parameter-context-checkbox-container').empty();
+            var createParamContextContainer;
+
+            /* Form the parameter context name input */
+            if (isParameterContext) {
+                // only show the name
+                createParamContextContainer = $('<div id="create-parameter-context-container" class="string-check-container setting" style="display:block;"></div>');
+
+                $('<div class="setting">' +
+                    '<div class="setting-name">Parameter Context Name</div>' +
+                    '<div class="setting-field">' +
+                    '<div id="referencing-parameter-context-name">' + nfCommon.escapeHtml(fetchedGroup.parameterContextName) + '</div>' +
+                    '</div>').appendTo(createParamContextContainer);
+            } else {
+
+                var checkboxMarkup = $('<div id="checkbox-container"></div>');
+
+                if (isCreateNewParameterContext) {
+                    // select checkbox
+                    $('<div id="create-parameter-context-field" class="nf-checkbox checkbox-checked"></div>').appendTo(checkboxMarkup);
+
+                    loadSelectableParameters(updatedGroup.id, updatedGroup.parameterSensitivities, updatedGroup);
+
+                    $('#parameters-container').show();
+                    $('#selectable-parameters-container').show();
+                    $('#fetched-parameters-container').hide();
+                } else {
+                    // deselect checkbox
+                    $('<div id="create-parameter-context-field" class="nf-checkbox checkbox-unchecked"></div>').appendTo(checkboxMarkup);
+
+                    $('#selectable-parameters-container').hide();
+                    $('#fetched-parameters-container').show();
+                }
+
+                $('<div id="create-parameter-context-label" class="nf-checkbox-label ellipsis" title="create-parameter-context" style="text-overflow: ellipsis;">' +
+                    'Create Parameter Context' +
+                    '</div></div>').appendTo(checkboxMarkup);
+
+                var settingMarkup = $('<div class="setting"></div>');
+                $(checkboxMarkup).appendTo(settingMarkup);
+                $(settingMarkup).appendTo(parametersCheckboxContainer);
+
+                // create the input container and set visibility
+                if (isCreateNewParameterContext) {
+                    createParamContextContainer = $('<div id="create-parameter-context-container" class="string-check-container setting" style="display:block;"></div>');
+                } else {
+                    createParamContextContainer = $('<div id="create-parameter-context-container" class="string-check-container setting" style="display:none;"></div>');
+                }
+
+                // create the input
+                $('<div class="setting">' +
+                    '<div class="setting-name">Parameter Context Name</div>' +
+                    '<div class="setting-field required">' +
+                    '<input id="create-parameter-context-input" type="text" name="parameter-context-name"/></div>' +
+                    '</div>').appendTo(createParamContextContainer);
+            }
+
+            createParamContextContainer.appendTo(parametersCheckboxContainer);
+
+            // populate the name input
+            var contextName = updatedGroup.parameterContextName;
+            $('#create-parameter-context-input').val(contextName);
+        } else {
+            $('#create-parameter-context-checkbox-container').hide();
+        }
+
+        /* Form the parameters listing */
+        var fetchedParametersContainer = $('#fetched-parameters-listing').empty();
+        var fetchedParameters = updatedGroup.parameterSensitivities;
+
+        // get parameter names only
+        var parametersArray = Object.keys(fetchedParameters);
+
+        // create parameter names listing
+        if (parametersArray.length > 0) {
+            $.each(parametersArray, function (i, parameter) {
+                var li = $('<li></li>').text(parameter);
+                $('<div class="clear"></div>').appendTo(li);
+                li.appendTo(fetchedParametersContainer);
+            })
+        } else {
+            // set to none
+            $('<div><span class="unset">None</span></div>').appendTo(fetchedParametersContainer);
+        }
+
+        // update the border if necessary
+        updateReferencingComponentsBorder($('#fetched-parameters-listing-container'));
+
+        /* Temporarily save any changes */
+        // begin updating the group table
+        groupsData.beginUpdate();
+
+        // save parameter context name change
+        $('#create-parameter-context-input').on('blur', function () {
+            // get the input value
+            updatedGroup.parameterContextName = $('#create-parameter-context-input').val();
+
+            // update the group item
+            groupsData.updateItem(updatedGroup.id, updatedGroup);
+
+            // update the list of parameters to be created
+            loadParameterContextsToCreate();
+
+            $('#fetch-parameters-dialog').modal('refreshButtons');
+        })
+
+        // create parameter checkbox behaviors
+        $('#create-parameter-context-field').off().on('change', function (event, args) {
+            // if checked then show the name input, hide parameters listing, show selectable parameters table
+            if (args.isChecked) {
+                updatedGroup.createNewParameterContext = true;
+
+                loadSelectableParameters(updatedGroup.id, updatedGroup.parameterSensitivities, updatedGroup);
+
+                $('#fetched-parameters-container').hide();
+                $('#create-parameter-context-container').show();
+                $('#selectable-parameters-container').show();
+                $('#fetch-parameters-dialog').modal('refreshButtons');
+            } else {
+                // if unchecked, then hide the input and only show the parameters listing
+                updatedGroup.createNewParameterContext = false;
+
+                loadSelectableParameters(updatedGroup.id, updatedGroup.parameterSensitivities, updatedGroup);
+
+                $('#create-parameter-context-container').hide();
+                $('#selectable-parameters-container').hide();
+                $('#fetched-parameters-container').show();
+                $('#fetch-parameters-dialog').modal('refreshButtons');
+            }
+
+            // update the group item
+            groupsData.updateItem(updatedGroup.id, updatedGroup);
+        });
+
+        // finish updating the group table
+        groupsData.endUpdate();
+        groupsData.reSort();
+    };
+
+    /**
+     * Reset the dialog.
+     */
+    var resetFetchParametersDialog = function () {
+        $('#fetch-parameters-affected-referencing-components-container').hide();
+        $('#fetch-parameters-referencing-components-container').show();
+        $('#fetch-parameters-permissions-parameter-contexts-message').addClass('hidden');
+        $('#fetch-parameters-permissions-affected-components-message').addClass('hidden');
+        $('#create-parameter-context-input').val('');
+
+        var headerCheckbox = $('#selectable-parameters-table .slick-column-name input');
+        headerCheckbox.removeAttr('checked');
+
+        // clear the groups table
+        var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+        var groupsData = groupsGrid.getData();
+        groupsGrid.setSelectedRows([]);
+        groupsData.setItems([]);
+
+        // clear the selectable parameters table
+        var selectableParametersGrid = $('#selectable-parameters-table').data('gridInstance');
+        var selectableParametersData = selectableParametersGrid.getData();
+        selectableParametersGrid.setSelectedRows([]);
+        selectableParametersData.setItems([]);
+
+        resetUsage();
+
+        // reset the last selected
+        lastSelectedGroupId = null;
+        lastSelectedParameterId = null;
+    };
+
+    var lastSelectedGroupId = null;
+    var lastSelectedParameterId = null;
+
+    /**
+     * Sorts the specified data using the specified sort details.
+     *
+     * @param {object} sortDetails
+     * @param {object} data
+     */
+    var sortParameters = function (sortDetails, data) {
+        // defines a function for sorting
+        var comparer = function (a, b) {
+            // direct parameters always come above inherited ones
+            if (a.isInherited === false && b.isInherited === true) {
+                return -1;
+            }
+            if (a.isInherited === true && b.isInherited === false) {
+                return 1;
+            }
+            if (sortDetails.columnId === 'name') {
+                var aString = _.get(a, '[' + sortDetails.columnId + ']', '');
+                var bString = _.get(b, '[' + sortDetails.columnId + ']', '');
+                return aString === bString ? 0 : aString > bString ? 1 : -1;
+            }
+        };
+
+        // perform the sort
+        data.sort(comparer, sortDetails.sortAsc);
+    };
+
+    /**
+     * Sorts the specified entities based on the name.
+     *
+     * @param {object} a
+     * @param {object} b
+     * @returns {number}
+     */
+    var nameComparator = function (a, b) {
+        return a.component.name.localeCompare(b.component.name);
+    };
+
+    /**
+     * Performs the filtering.
+     *
+     * @param {object} item     The item subject to filtering
+     * @param {object} args     Filter arguments
+     * @returns {Boolean}       Whether or not to include the item
+     */
+    var filter = function (item, args) {
+        return item.hidden === false;
+    };
+
+    /**
+     * Renders the bulletins as a tooltip of the bulletinIconElement, shows the icon
+     *
+     * @param bulletins            bulletins to be rendered
+     * @param bulletinIconElement  jQuery element to display as the bulletin icon and source for the tooltip
+     */
+    var renderBulletins = function (bulletins, bulletinIconElement) {
+        // format the new bulletins
+        var formattedBulletins = nfCommon.getFormattedBulletins(bulletins);
+
+        var list = nfCommon.formatUnorderedList(formattedBulletins);
+
+        // update existing tooltip or initialize a new one if appropriate
+        bulletinIconElement.addClass('has-bulletins').show().qtip($.extend({},
+            nfCanvasUtils.config.systemTooltipConfig,
+            {
+                content: list
+            }));
+    }
+
+    /**
+     * Populates the referencing components for the specified parameter context.
+     *
+     * @param {object} referencingComponents
+     */
+    var populateReferencingComponents = function (referencingComponents) {
+        // toggles the visibility of a container
+        var toggle = function (twist, container) {
+            if (twist.hasClass('expanded')) {
+                twist.removeClass('expanded').addClass('collapsed');
+                container.hide();
+            } else {
+                twist.removeClass('collapsed').addClass('expanded');
+                container.show();
+            }
+        };
+
+        var referencingProcessors = [];
+        var referencingControllerServices = [];
+        var unauthorizedReferencingComponents = [];
+
+        var spinner = $('#fetch-parameters-referencing-components-container .referencing-components-loading');
+
+        var loadingDeferred = $.Deferred(function (deferred) {
+            spinner.addClass('ajax-loading');
+            deferred.resolve();
+        });
+        loadingDeferred.then(function () {
+            resetUsage();
+        }).then(function() {
+            var fetchParameterReferencingComponentsContainer = $('#fetch-parameter-referencing-components-container').empty();
+
+            // referencing component will be undefined when a new parameter is added
+            if (_.isEmpty(referencingComponents)) {
+                // set to pending
+                $('<div class="referencing-component-container"><span class="unset">None</span></div>').appendTo(fetchParameterReferencingComponentsContainer);
+            } else {
+                // bin the referencing components according to their type
+                $.each(referencingComponents.parameter.parameter.referencingComponents, function (_, referencingComponentEntity) {
+                    if (referencingComponentEntity.permissions.canRead === true && referencingComponentEntity.permissions.canWrite === true) {
+                        if (referencingComponentEntity.component.referenceType === 'PROCESSOR') {
+                            referencingProcessors.push(referencingComponentEntity);
+                        } else {
+                            referencingControllerServices.push(referencingComponentEntity);
+                        }
+                    } else {
+                        unauthorizedReferencingComponents.push(referencingComponentEntity);
+                    }
+                });
+
+                var referencingProcessGroups = {};
+
+                // bin the referencing processors according to their PG
+                $.each(referencingProcessors, function (_, referencingProcessorEntity) {
+                    if (referencingProcessGroups[referencingProcessorEntity.processGroup.id]) {
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].referencingProcessors.push(referencingProcessorEntity);
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].id = referencingProcessorEntity.processGroup.id;
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].name = referencingProcessorEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: referencingProcessorEntity.processGroup.name,
+                            id: referencingProcessorEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[referencingProcessorEntity.processGroup.id].referencingProcessors.push(referencingProcessorEntity);
+                    }
+                });
+
+                // bin the referencing CS according to their PG
+                $.each(referencingControllerServices, function (_, referencingControllerServiceEntity) {
+                    if (referencingProcessGroups[referencingControllerServiceEntity.processGroup.id]) {
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].referencingControllerServices.push(referencingControllerServiceEntity);
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].id = referencingControllerServiceEntity.processGroup.id;
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].name = referencingControllerServiceEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: referencingControllerServiceEntity.processGroup.name,
+                            id: referencingControllerServiceEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[referencingControllerServiceEntity.processGroup.id].referencingControllerServices.push(referencingControllerServiceEntity);
+                    }
+                });
+
+                // bin the referencing unauthorized components according to their PG
+                $.each(unauthorizedReferencingComponents, function (_, unauthorizedReferencingComponentEntity) {
+                    if (referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id]) {
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].unauthorizedReferencingComponents.push(unauthorizedReferencingComponentEntity);
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].id = unauthorizedReferencingComponentEntity.processGroup.id;
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].name = unauthorizedReferencingComponentEntity.processGroup.name;
+                    } else {
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id] = {
+                            referencingProcessors: [],
+                            referencingControllerServices: [],
+                            unauthorizedReferencingComponents: [],
+                            name: unauthorizedReferencingComponentEntity.processGroup.name,
+                            id: unauthorizedReferencingComponentEntity.processGroup.id
+                        };
+
+                        referencingProcessGroups[unauthorizedReferencingComponentEntity.processGroup.id].unauthorizedReferencingComponents.push(unauthorizedReferencingComponentEntity);
+                    }
+                });
+
+                var fetchParameterReferencingComponentsContainer = $('#fetch-parameter-referencing-components-container');
+                var groups = $('<ul class="referencing-component-listing clear"></ul>');
+
+                var referencingProcessGroupsArray = [];
+                for (var key in referencingProcessGroups) {
+                    if (referencingProcessGroups.hasOwnProperty(key)) {
+                        referencingProcessGroupsArray.push(referencingProcessGroups[key]);
+                    }
+                }
+
+                if (nfCommon.isEmpty(referencingProcessGroupsArray)) {
+                    // set to none
+                    $('<div class="referencing-component-container"><span class="unset">None</span></div>').appendTo(fetchParameterReferencingComponentsContainer);
+                } else {
+                    //sort alphabetically
+                    var sortedReferencingProcessGroups = referencingProcessGroupsArray.sort(function (a, b) {
+                        if (a.name < b.name) {
+                            return -1;
+                        }
+                        if (a.name > b.name) {
+                            return 1;
+                        }
+                        return 0;
+                    });
+
+                    sortedReferencingProcessGroups.forEach(function (referencingProcessGroup) {
+                        // container for this pg's references
+                        var referencingPgReferencesContainer = $('<div class="referencing-component-references"></div>');
+                        fetchParameterReferencingComponentsContainer.append(referencingPgReferencesContainer);
+
+                        // create the collapsable listing for each PG
+                        var createReferenceBlock = function (referencingProcessGroup, list) {
+                            var twist = $('<div class="expansion-button collapsed"></div>');
+                            var title = $('<span class="referencing-component-title"></span>').text(referencingProcessGroup.name);
+                            var count = $('<span class="referencing-component-count"></span>').text('(' + (referencingProcessGroup.referencingProcessors.length + referencingProcessGroup.referencingControllerServices.length + referencingProcessGroup.unauthorizedReferencingComponents.length) + ')');
+                            var referencingComponents = $('#fetch-parameters-referencing-components-template').clone();
+                            referencingComponents.removeAttr('id');
+                            referencingComponents.removeClass('hidden');
+
+                            // create the reference block
+                            var groupTwist = $('<div class="referencing-component-block pointer unselectable"></div>').data('processGroupId', referencingProcessGroup.id).on('click', function () {
+                                if (twist.hasClass('collapsed')) {
+                                    groupTwist.append(referencingComponents);
+
+                                    var processorContainer = groupTwist.find('.fetch-parameters-referencing-processors');
+                                    nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-state');
+                                    nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-bulletins');
+                                    processorContainer.empty();
+
+                                    var controllerServiceContainer = groupTwist.find('.fetch-parameters-referencing-controller-services');
+                                    nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-state');
+                                    nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-bulletins');
+                                    controllerServiceContainer.empty();
+
+                                    var unauthorizedComponentsContainer = groupTwist.find('.fetch-parameters-referencing-unauthorized-components').empty();
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(processorContainer);
+                                    } else {
+                                        // sort the referencing processors
+                                        referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors.sort(nameComparator);
+
+                                        // render each and register a click handler
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].referencingProcessors, function (_, referencingProcessorEntity) {
+                                            renderReferencingProcessor(referencingProcessorEntity, processorContainer);
+                                        });
+                                    }
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(controllerServiceContainer);
+                                    } else {
+                                        // sort the referencing controller services
+                                        referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices.sort(nameComparator);
+
+                                        // render each and register a click handler
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].referencingControllerServices, function (_, referencingControllerServiceEntity) {
+                                            renderReferencingControllerService(referencingControllerServiceEntity, controllerServiceContainer);
+                                        });
+                                    }
+
+                                    if (referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents.length === 0) {
+                                        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(unauthorizedComponentsContainer);
+                                    } else {
+                                        // sort the unauthorized referencing components
+                                        referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents.sort(function (a, b) {
+                                            if (a.permissions.canRead === true && b.permissions.canRead === true) {
+                                                // processors before controller services
+                                                var sortVal = a.component.referenceType === b.component.referenceType ? 0 : a.component.referenceType > b.component.referenceType ? -1 : 1;
+
+                                                // if a and b are the same type, then sort by name
+                                                if (sortVal === 0) {
+                                                    sortVal = a.component.name === b.component.name ? 0 : a.component.name > b.component.name ? 1 : -1;
+                                                }
+
+                                                return sortVal;
+                                            } else {
+
+                                                // if lacking read and write perms on both, sort by id
+                                                if (a.permissions.canRead === false && b.permissions.canRead === false) {
+                                                    return a.id > b.id ? 1 : -1;
+                                                } else {
+                                                    // if only one has read perms, then let it come first
+                                                    if (a.permissions.canRead === true) {
+                                                        return -1;
+                                                    } else {
+                                                        return 1;
+                                                    }
+                                                }
+                                            }
+                                        });
+
+                                        $.each(referencingProcessGroups[$(this).data('processGroupId')].unauthorizedReferencingComponents, function (_, unauthorizedReferencingComponentEntity) {
+                                            if (unauthorizedReferencingComponentEntity.permissions.canRead === true) {
+                                                if (unauthorizedReferencingComponentEntity.component.referenceType === 'PROCESSOR') {
+                                                    renderReferencingProcessor(unauthorizedReferencingComponentEntity, unauthorizedComponentsContainer);
+                                                } else {
+                                                    renderReferencingControllerService(unauthorizedReferencingComponentEntity, unauthorizedComponentsContainer);
+                                                }
+                                            } else {
+                                                var referencingUnauthorizedComponentContainer = $('<li class="referencing-component-container"></li>').appendTo(unauthorizedComponentsContainer);
+                                                $('<span class="fetch-parameter-referencing-component-name link ellipsis"></span>')
+                                                    .prop('title', unauthorizedReferencingComponentEntity.id)
+                                                    .text(unauthorizedReferencingComponentEntity.id)
+                                                    .on('click', function () {
+                                                        // check if there are outstanding changes
+                                                        handleOutstandingChanges().done(function () {
+                                                            // close the shell
+                                                            $('#shell-dialog').modal('hide');
+
+                                                            // show the component in question
+                                                            if (unauthorizedReferencingComponentEntity.referenceType === 'PROCESSOR') {
+                                                                nfCanvasUtils.showComponent(unauthorizedReferencingComponentEntity.processGroup.id, unauthorizedReferencingComponentEntity.id);
+                                                            } else if (unauthorizedReferencingComponentEntity.referenceType === 'CONTROLLER_SERVICE') {
+                                                                nfProcessGroupConfiguration.showConfiguration(unauthorizedReferencingComponentEntity.processGroup.id).done(function () {
+                                                                    nfProcessGroup.enterGroup(unauthorizedReferencingComponentEntity.processGroup.id);
+                                                                    nfProcessGroupConfiguration.selectControllerService(unauthorizedReferencingComponentEntity.id);
+                                                                });
+                                                            }
+                                                        });
+                                                    })
+                                                    .appendTo(referencingUnauthorizedComponentContainer);
+                                            }
+                                        });
+                                    }
+                                } else {
+                                    groupTwist.find('.fetch-parameters-referencing-components-template').remove();
+                                }
+
+                                // toggle this block
+                                toggle(twist, list);
+
+                                // update the border if necessary
+                                updateReferencingComponentsBorder($('#fetch-parameter-referencing-components-container'));
+                            }).append(twist).append(title).append(count).appendTo(referencingPgReferencesContainer);
+
+                            // add the listing
+                            list.appendTo(referencingPgReferencesContainer);
+
+                            // expand the group twist
+                            groupTwist.click();
+                        };
+
+                        // create block for this process group
+                        createReferenceBlock(referencingProcessGroup, groups);
+                    });
+                }
+            }
+        })
+            .always(function () {
+                spinner.removeClass('ajax-loading');
+            });
+        return loadingDeferred.promise();
+    };
+
+    /**
+     * Renders the specified referencing component.
+     *
+     * @param {object} referencingProcessorEntity
+     * @param {jQuery} container
+     */
+    var renderReferencingProcessor = function (referencingProcessorEntity, container) {
+        var referencingProcessorContainer = $('<li class="referencing-component-container"></li>').appendTo(container);
+        var referencingProcessor = referencingProcessorEntity.component;
+
+        // processor state
+        $('<div class="referencing-component-state"></div>').addClass(function () {
+            if (nfCommon.isDefinedAndNotNull(referencingProcessor.state)) {
+                var icon = $(this);
+
+                var state = referencingProcessor.state.toLowerCase();
+                if (state === 'stopped' && !nfCommon.isEmpty(referencingProcessor.validationErrors)) {
+                    state = 'invalid';
+
+                    // build the validation error listing
+                    var list = nfCommon.formatUnorderedList(referencingProcessor.validationErrors);
+
+                    // add tooltip for the warnings
+                    icon.qtip($.extend({},
+                        nfCanvasUtils.config.systemTooltipConfig,
+                        {
+                            content: list
+                        }));
+                }
+
+                return state;
+            } else {
+                return '';
+            }
+        }).appendTo(referencingProcessorContainer);
+
+
+        // processor name
+        $('<span class="parameter-provider-referencing-component-name link ellipsis"></span>')
+            .prop('title', referencingProcessor.name)
+            .text(referencingProcessor.name)
+            .on('click', function () {
+                // close the shell
+                $('#shell-dialog').modal('hide');
+
+                // show the component in question
+                nfCanvasUtils.showComponent(referencingProcessorEntity.processGroup.id, referencingProcessor.id);
+                $('#fetch-parameters-dialog').modal('hide');
+            }).appendTo(referencingProcessorContainer);
+
+        // bulletin
+        var bulletinIcon = $('<div class="referencing-component-bulletins"></div>').addClass(referencingProcessor.id + '-referencing-bulletins');
+        bulletinIcon.appendTo(referencingProcessorContainer);
+        if (!nfCommon.isEmpty(referencingProcessorEntity.bulletins)) {
+            renderBulletins(referencingProcessorEntity.bulletins, bulletinIcon);
+        }
+
+        // processor active threads
+        $('<span class="referencing-component-active-thread-count"></span>').text(function () {
+            if (nfCommon.isDefinedAndNotNull(referencingProcessor.activeThreadCount) && referencingProcessor.activeThreadCount > 0) {
+                return '(' + referencingProcessor.activeThreadCount + ')';
+            } else {
+                return '';
+            }
+        }).appendTo(referencingProcessorContainer);
+    };
+
+    /**
+     * Renders the specified affect controller service.
+     *
+     * @param {object} referencingControllerServiceEntity
+     * @param {jQuery} container
+     */
+    var renderReferencingControllerService = function (referencingControllerServiceEntity, container) {
+        var referencingControllerServiceContainer = $('<li class="referencing-component-container"></li>').appendTo(container);
+        var referencingControllerService = referencingControllerServiceEntity.component;
+
+        // controller service state
+        $('<div class="referencing-component-state"></div>').addClass(function () {
+            if (nfCommon.isDefinedAndNotNull(referencingControllerService.state)) {
+                var icon = $(this);
+
+                var state = referencingControllerService.state === 'ENABLED' ? 'enabled' : 'disabled';
+                if (state === 'disabled' && !nfCommon.isEmpty(referencingControllerService.validationErrors)) {
+                    state = 'invalid';
+
+                    // build the error listing
+                    var list = nfCommon.formatUnorderedList(referencingControllerService.validationErrors);
+
+                    // add tooltip for the warnings
+                    icon.qtip($.extend({},
+                        nfCanvasUtils.config.systemTooltipConfig,
+                        {
+                            content: list
+                        }));
+                }
+                return state;
+            } else {
+                return '';
+            }
+        }).appendTo(referencingControllerServiceContainer);
+
+        // bulletin
+        var bulletinIcon = $('<div class="referencing-component-bulletins"></div>')
+            .addClass(referencingControllerService.id + '-referencing-bulletins')
+            .appendTo(referencingControllerServiceContainer);
+        if (!nfCommon.isEmpty(referencingControllerServiceEntity.bulletins)) {
+            renderBulletins(referencingControllerServiceEntity.bulletins, bulletinIcon);
+        }
+
+        // controller service name
+        $('<span class="parameter-provider-referencing-component-name link ellipsis"></span>')
+            .prop('title', referencingControllerService.name)
+            .text(referencingControllerService.name)
+            .on('click', function () {
+                // close the shell
+                $('#shell-dialog').modal('hide');
+
+                nfProcessGroup.enterGroup(referencingControllerService.processGroupId);
+
+                // show the component in question
+                nfProcessGroupConfiguration.showConfiguration(referencingControllerService.processGroupId)
+                    .done(function () {
+                        nfProcessGroupConfiguration.selectControllerService(referencingControllerService.id);
+                        $('#fetch-parameters-dialog').modal('hide');
+                    });
+            }).appendTo(referencingControllerServiceContainer);
+    };
+
+    /**
+     * Initializes the selectable parameters table
+     */
+    var initSelectableParametersTable = function () {
+        var selectableParametersTable = $('#selectable-parameters-table');
+
+        var nameFormatter = function (row, cell, value, columnDef, dataContext) {
+            var valueWidthOffset = 10;
+            var cellContent = $('<div></div>');
+
+            // format the contents
+            var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
+            if (dataContext.type === 'required') {
+                formattedValue.addClass('required');
+            }
+
+            if (dataContext.isAffectedParameter || dataContext.parameterStatus.status === 'CHANGED') {
+                valueWidthOffset += 30;
+                $('<div class="fa fa-asterisk" alt="Info" style="float: right;"></div>').appendTo(cellContent);
+            }
+
+            if (dataContext.isReferencingParameter) {
+                valueWidthOffset += 30;
+                $('<div class="fa fa-hashtag" alt="Info" style="float: right;"></div>').appendTo(cellContent);
+            }
+
+            // adjust the width accordingly
+            formattedValue.width(columnDef.width - valueWidthOffset).ellipsis();
+
+            // return the cell content
+            return cellContent.html();
+        };
+
+        var checkboxSelectionFormatter = function (row, cell, value, columnDef, dataContext) {
+            if (dataContext) {
+                if (_.isEmpty(dataContext.parameterStatus) || _.isEmpty(dataContext.parameterStatus.parameter.parameter.referencingComponents)) {
+                    if (dataContext.sensitivity === SENSITIVE) {
+                        return "<input type='checkbox' checked='checked' class='checked-input-enabled'><label for='selector'></label>";
+                    } else {
+                        return "<input type='checkbox' class='unchecked-input-enabled'><label for='selector'></label>";
+                    }
+                }
+
+                if (!_.isEmpty(dataContext.parameterStatus.parameter.parameter.referencingComponents)) {
+                    // disable checkboxes
+                    if (dataContext.sensitivity === SENSITIVE) {
+                        return "<input type='checkbox' checked='checked' class='disabled checked-input-disabled' disabled><label for='selector'></label>";
+                    } else {
+                        return "<input type='checkbox' class='disabled unchecked-input-disabled' disabled><label for='selector'></label>";
+                    }
+                }
+
+                $('#fetch-parameters-dialog').modal('refreshButtons');
+            }
+            return null;
+        };
+
+        var parametersColumn = [];
+
+        // define the column models for the table
+        var nameColumnDefinition = {
+            id: 'name',
+            name: 'Parameter Name',
+            field: 'name',
+            formatter: nameFormatter,
+            sortable: true,
+            resizable: false,
+            rerenderOnResize: true,
+            width: 280,
+            minWidth: 280,
+            editor: Slick.Editors.Text
+        };
+
+        var checkboxColumnDefinition = {
+            id: 'selector',
+            field: 'sel',
+            width: 30,
+            resizable: false,
+            sortable: false,
+            cssClass: 'slick-cell-checkboxsel',
+            hideSelectAllCheckbox: false,
+            formatter: checkboxSelectionFormatter
+        }
+
+        parametersColumn.push(checkboxColumnDefinition);
+        parametersColumn.push(nameColumnDefinition);
+
+        // initialize the dataview
+        var selectableParametersData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+
+        // initialize the sort
+        sortParameters({
+            columnId: 'name',
+            sortAsc: true
+        }, selectableParametersData);
+
+
+        // initialize the grid
+        var selectableParametersGrid = new Slick.Grid(selectableParametersTable, selectableParametersData, parametersColumn, selectableParametersGridOptions);
+        selectableParametersGrid.setSelectionModel(new Slick.RowSelectionModel());
+        selectableParametersGrid.registerPlugin(new Slick.AutoTooltips());
+
+        selectableParametersGrid.setSortColumn('name', true);
+        selectableParametersGrid.onSort.subscribe(function (e, args) {
+            sortParameters({
+                columnId: args.sortCol.id,
+                sortAsc: args.sortAsc
+            }, selectableParametersData);
+        });
+
+        selectableParametersGrid.onClick.subscribe(function (e, args) {
+            var target = $(e.target);
+            // get the node at this row
+            var item = selectableParametersData.getItem(args.row);
+            var saveToGroup = false;
+
+            if (selectableParametersGrid.getColumns()[args.cell].id === 'selector') {
+                if (target.hasClass('unchecked-input-enabled')) {
+                    saveToGroup = true;
+                    // check the box
+                    item.sensitivity = SENSITIVE;
+                    selectableParametersData.updateItem(item.id, item)
+                } else if (target.hasClass('checked-input-enabled')) {
+                    saveToGroup = true;
+                    // uncheck the box
+                    item.sensitivity = NON_SENSITIVE;
+                    selectableParametersData.updateItem(item.id, item)
+                }
+
+                // save to its group
+                if (saveToGroup) {
+                    var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+                    var groupsData = groupsGrid.getData();
+                    var currentGroup = groupsData.getItem([item.groupId]);
+
+                    currentGroup.parameterSensitivities[item.name] = item.sensitivity;
+                    groupsData.updateItem(item.groupId, currentGroup);
+
+                    $('#fetch-parameters-dialog').modal('refreshButtons');
+                }
+            }
+        })
+
+        selectableParametersGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+            if ($.isArray(args.rows) && args.rows.length === 1) {
+                // show the referencing components for the selected parameter
+                if (selectableParametersGrid.getDataLength() > 0) {
+                    var parameterIndex = args.rows[0];
+                    var parameter = selectableParametersGrid.getDataItem(parameterIndex);
+
+                    // only populate referencing components if this parameter is different than the last selected
+                    if (lastSelectedParameterId === null || lastSelectedParameterId !== parameter.id) {
+
+                        populateReferencingComponents(parameter.parameterStatus)
+                            .then(function () {
+                                updateReferencingComponentsBorder($('#fetch-parameter-referencing-components-container'));
+
+                                // update the last selected id
+                                lastSelectedParameterId = parameter.id;
+                            });
+                    }
+                }
+            }
+
+            $('#fetch-parameters-dialog').modal('refreshButtons');
+        })
+
+        // wire up the dataview to the grid
+        selectableParametersData.onRowCountChanged.subscribe(function (e, args) {
+            var selectableParameters = args.dataView.getItems();
+
+            if (!_.isEmpty(selectableParameters)) {
+                var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+                var groupsData = groupsGrid.getData();
+                var currentGroupId = selectableParameters[0].groupId;
+                var currentGroup = groupsData.getItem([currentGroupId]);
+
+                if (!currentGroup.isParameterContext) {
+                    // update parameter sensitivities
+                    var paramSensitivities = {};
+                    $.each(selectableParameters, function (i, param) {
+                        paramSensitivities[param.name] = param.sensitivity;
+                    })
+
+                    // save to its group
+                    currentGroup.parameterSensitivities = paramSensitivities;
+                    groupsData.updateItems(currentGroupId, currentGroup);
+                }
+            }
+
+            selectableParametersGrid.updateRowCount();
+            selectableParametersGrid.render();
+        });
+        selectableParametersData.onRowsChanged.subscribe(function (e, args) {
+            selectableParametersGrid.invalidateRows(args.rows);
+            selectableParametersGrid.render();
+        });
+        selectableParametersData.syncGridSelection(selectableParametersGrid, true);
+
+        // hold onto an instance of the grid and create an affected component tooltip
+        selectableParametersTable.data('gridInstance', selectableParametersGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var asteriskIconElement =  $(this).find('div.fa-asterisk');
+            var asteriskTooltipContent = nfCommon.escapeHtml('Value has changed.');
+
+            // initialize tooltip
+            asteriskIconElement.qtip($.extend({},
+                nfCommon.config.tooltipConfig,
+                {
+                    content: asteriskTooltipContent
+                }));
+
+            var hashtagIconElement =  $(this).find('div.fa-hashtag');
+            var hashtagTooltipContent = nfCommon.escapeHtml('This parameter is currently referenced by a property. The sensitivity cannot be changed.');
+
+            // initialize tooltip
+            hashtagIconElement.qtip($.extend({},
+                nfCommon.config.tooltipConfig,
+                {
+                    content: hashtagTooltipContent
+                }));
+        });
+
+        /**
+         * Select or deselect parameters that are not disabled.
+         *
+         * @param selectionType   The type of selection.
+         */
+        var selectOrDeselectAll = function (selectionType) {
+            var sensitivity = selectionType === 'select' ? SENSITIVE : NON_SENSITIVE;
+
+            // get all selectable parameters
+            var selectableParams = [];
+            $.each(selectableParametersData.getItems(), function (i, param) {
+                if (!param.isReferencingParameter) {
+                    selectableParams.push(param);
+                }
+            });
+
+            if (!_.isEmpty(selectableParams)) {
+                var groupsGrid = $('#parameter-groups-table').data('gridInstance');
+                var groupsData = groupsGrid.getData();
+                var currentGroupId = groupsGrid.getSelectedRows();
+                var currentGroup = groupsData.getItem(currentGroupId);
+
+                // begin updating
+                groupsData.beginUpdate();
+
+                // set sensitivities
+                $.each(selectableParams, function (i, param) {
+                    var updateParamItem = $.extend(param, { sensitivity: sensitivity });
+                    selectableParametersData.updateItem(param.id, updateParamItem);
+
+                    // save to its group
+                    currentGroup.parameterSensitivities[param.name] = sensitivity;
+                })
+
+                // complete the update
+                groupsData.endUpdate();
+
+                $('#fetch-parameters-dialog').modal('refreshButtons');
+            }
+        }
+
+        $('#select-all-fetched-parameters').on('click', function() {
+            selectOrDeselectAll('select');
+        });
+
+        $('#deselect-all-fetched-parameters').on('click', function() {
+            selectOrDeselectAll('deselect');
+        });
+
+        // end
+    }
+
+    /**
+     * Initializes the parameter groups table
+     */
+    var initParameterGroupsTable = function () {
+        var parameterGroupsTable = $('#parameter-groups-table');
+
+        var nameFormatter = function (row, cell, value, columnDef, dataContext) {
+            var nameWidthOffset = 30;
+            var cellContent = $('<div></div>');
+
+            // format the contents
+            var formattedValue = $('<span/>').addClass('table-cell').text(value).appendTo(cellContent);
+            if (dataContext.type === 'required') {
+                formattedValue.addClass('required');
+            }
+
+            // add icon if group is or will be created as a parameter context
+            if (dataContext.isParameterContext || dataContext.createNewParameterContext) {
+                $('<div class="fa fa-star" alt="Info" style="float: right;"></div>').appendTo(cellContent);
+            }
+
+            // adjust the width accordingly
+            formattedValue.width(columnDef.width - nameWidthOffset).ellipsis();
+
+            // return the cell content
+            return cellContent.html();
+        };
+
+        // define the column model for the table
+        var groupsColumn = [
+            {
+                id: 'name',
+                name: 'Parameter Group Name',
+                field: 'name',
+                formatter: nameFormatter,
+                sortable: true,
+                resizable: true,
+                rerenderOnResize: true,
+                width: 327,
+                minWidth: 327,
+                maxWidth: 327
+            }
+        ];
+
+        // initialize the dataview
+        var groupsData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        groupsData.setFilterArgs({
+            searchString: '',
+            property: 'hidden'
+        });
+        groupsData.setFilter(filter);
+
+        // initialize the sort
+        sortParameters({
+            columnId: 'name',
+            sortAsc: true
+        }, groupsData);
+
+        // initialize the grid
+        var groupsGrid = new Slick.Grid(parameterGroupsTable, groupsData, groupsColumn, parameterGroupsGridOptions);
+        groupsGrid.setSelectionModel(new Slick.RowSelectionModel());
+        groupsGrid.registerPlugin(new Slick.AutoTooltips());
+        groupsGrid.setSortColumn('name', true);
+        groupsGrid.onSort.subscribe(function (e, args) {
+            sortParameters({
+                columnId: args.sortCol.id,
+                sortAsc: args.sortAsc
+            }, groupsData);
+        });
+        groupsGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+            // clean up tooltips
+            nfCommon.cleanUpTooltips($('#selectable-parameters-table'), 'div.fa-asterisk');
+            nfCommon.cleanUpTooltips($('#selectable-parameters-table'), 'div.fa-hashtag');
+
+            if ($.isArray(args.rows) && args.rows.length === 1) {
+                if (groupsGrid.getDataLength() > 0) {
+                    var groupIndex = args.rows[0];
+                    var group = groupsGrid.getDataItem(groupIndex);
+
+                    // only populate fetch parameters column if this group is different than the last selected
+                    if (lastSelectedGroupId === null || lastSelectedGroupId !== group.id) {
+                        loadFetchedParameters(group);
+
+                        // update the last selected id
+                        lastSelectedGroupId = group.id;
+
+                        // populate parameter referencing components
+                        var selectableParametersGrid = $('#selectable-parameters-table').data('gridInstance');
+                        var selectedRow = selectableParametersGrid.getSelectedRows()[0];
+                        var parameter = selectableParametersGrid.getData().getItem(selectedRow);
+
+                        if (parameter && group.isParameterContext) {
+                            populateReferencingComponents(parameter.parameterStatus).then(function () {
+                                updateReferencingComponentsBorder($('#fetch-parameter-referencing-components-container'));
+                            });
+                        } else {
+                            // show 'None' for referencing components
+                            populateReferencingComponents({}).then(function () {
+                                updateReferencingComponentsBorder($('#fetch-parameter-referencing-components-container'));
+                            });
+                        }
+                    }
+                }
+            }
+        });
+        groupsGrid.onBeforeCellEditorDestroy.subscribe(function (e, args) {
+            setTimeout(function () {
+                groupsGrid.resizeCanvas();
+            }, 50);
+        });
+
+        // wire up the dataview to the grid
+        groupsData.onRowCountChanged.subscribe(function (e, args) {
+            groupsGrid.updateRowCount();
+            groupsGrid.render();
+        });
+        groupsData.onRowsChanged.subscribe(function (e, args) {
+            groupsGrid.invalidateRows(args.rows);
+            groupsGrid.render();
+        });
+        groupsData.syncGridSelection(groupsGrid, true);
+
+        // hold onto an instance of the grid and create parameter context tooltip
+        parameterGroupsTable.data('gridInstance', groupsGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var starIconElement =  $(this).find('div.fa-star');
+            var tooltipContent = nfCommon.escapeHtml('Synced to a parameter context.');
+
+            // initialize tooltip
+            starIconElement.qtip($.extend({},
+                nfCommon.config.tooltipConfig,
+                {
+                    content: tooltipContent
+                }));
+        });
+    };
+    // end initParameterGroupTable
+
+    var resetUsage = function () {
+        // empty the containers
+        var fetchParameterRefComponents = $('#fetch-parameter-referencing-components-container .referencing-component-references');
+        fetchParameterRefComponents.empty();
+        var fetchParameterRefComponentsContainer = $('#fetch-parameter-referencing-components-container');
+
+        var processorContainer = $('.fetch-parameters-referencing-processors');
+        nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-state');
+        nfCommon.cleanUpTooltips(processorContainer, 'div.referencing-component-bulletins');
+        processorContainer.empty();
+
+        var controllerServiceContainer = $('.fetch-parameters-referencing-controller-services');
+        nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-state');
+        nfCommon.cleanUpTooltips(controllerServiceContainer, 'div.referencing-component-bulletins');
+        controllerServiceContainer.empty();
+
+        var unauthorizedComponentsContainer = $('.fetch-parameters-referencing-unauthorized-components').empty();
+
+        $('#affected-referencing-components-container').empty();
+
+        // reset the last selected
+        lastSelectedGroupId = null;
+        lastSelectedParameterId = null;
+
+        // indicate no referencing components
+        $('<div class="referencing-component-container"><span class="unset">None</span></div>').appendTo(fetchParameterRefComponentsContainer);
+        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(processorContainer);
+        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(controllerServiceContainer);
+        $('<li class="referencing-component-container"><span class="unset">None</span></li>').appendTo(unauthorizedComponentsContainer);
+    };
+
+    var currentParameterProviderEntity = null;
+
+    var nfParameterProvider = {
+        /**
+         * Initializes the parameter provider configuration dialog.
+         *
+         * @param options   Option settings for the parameter provider.
+         */
+        init: function (options) {
+            nfSettings = options.nfSettings;
+            fetchParameterProviderOptions = options.statusBarOptions;
+
+
+            // initialize the configuration dialog tabs
+            $('#parameter-provider-configuration-tabs').tabbs({
+                tabStyle: 'tab',
+                selectedTabStyle: 'selected-tab',
+                scrollableTabContentStyle: 'scrollable',
+                tabs: [{
+                    name: 'Settings',
+                    tabContentId: 'parameter-provider-standard-settings-tab-content'
+                }, {
+                    name: 'Properties',
+                    tabContentId: 'parameter-provider-properties-tab-content'
+                }, {
+                    name: 'Comments',
+                    tabContentId: 'parameter-provider-comments-tab-content'
+                }],
+                select: function () {
+                    // remove all property detail dialogs
+                    nfUniversalCapture.removeAllPropertyDetailDialogs();
+
+                    // update the property table size in case this is the first time its rendered
+                    if ($(this).text() === 'Properties') {
+                        $('#parameter-provider-properties').propertytable('resetTableSize');
+                    }
+
+                    // close all fields currently being edited
+                    $('#parameter-provider-properties').propertytable('saveRow');
+                }
+            });
+
+            // initialize the parameter provider configuration dialog
+            $('#parameter-provider-configuration').data('mode', config.edit).modal({
+                scrollableContentStyle: 'scrollable',
+                headerText: 'Configure Parameter Provider',
+                handler: {
+                    close: function () {
+                        // cancel any active edits
+                        $('#parameter-provider-properties').propertytable('cancelEdit');
+
+                        // clear the tables
+                        $('#parameter-provider-properties').propertytable('clear');
+
+                        // clear the comments
+                        nfCommon.clearField('read-only-parameter-provider-comments');
+
+                        // removed the cached parameter provider details
+                        $('#parameter-provider-configuration').removeData('parameterProviderDetails');
+
+                        // clean up an shown verification errors
+                        $('#parameter-provider-properties-verification-results').hide();
+                        $('#parameter-provider-properties-verification-results-listing').css('border-width', '0').empty();
+                        $('#parameter-provider-properties').css('bottom', '0');
+
+                        // clean up an shown verification errors
+                        $('#parameter-provider-properties-verification-results').hide();
+                        $('#parameter-provider-properties-verification-results-listing').css('border-width', '0').empty();
+                        $('#parameter-provider-properties').css('bottom', '0');
+
+                        // clear most recently submitted referenced attributes
+                        referencedAttributes = null;
+                    },
+                    open: function () {
+                        nfCommon.toggleScrollable($('#' + this.find('.tab-container').attr('id') + '-content').get(0));
+                    }
+                }
+            });
+
+            // if the status bar is supported, initialize it.
+            if (fetchParameterProviderOptions) {
+                $('#fetch-parameters-status-bar').statusbar('provider');
+            }
+
+            // initialize the property table
+            $('#parameter-provider-properties').propertytable({
+                readOnly: false,
+                supportsGoTo: true,
+                dialogContainer: '#new-parameter-provider-property-container',
+                descriptorDeferred: getParameterProviderPropertyDescriptor,
+                controllerServiceCreatedDeferred: function (response) {
+                    return nfControllerServices.loadControllerServices(controllerServicesUri, $('#controller-services-table'));
+                },
+                goToServiceDeferred: goToServiceFromProperty
+            });
+
+            // initialize the fetch parameters dialog
+            $('#fetch-parameters-dialog').modal({
+                headerText: 'Fetch Parameters',
+                scrollableContentStyle: 'scrollable',
+                handler: {
+                    close: function () {
+                        // reset visibility
+                        $('#fetch-parameters-update-status-container').hide();
+                        $('#apply-groups-container').hide();
+
+                        // clear the dialog
+                        $('#fetch-parameters-id').text('');
+                        $('#fetch-parameters-name').text('');
+                        $('#fetched-parameters-listing').empty();
+                        $('#fetch-parameters-update-steps').empty();
+                        $('#parameter-contexts-to-create-container').empty();
+
+                        // clean up tooltips
+                        nfCommon.cleanUpTooltips($('#parameter-groups-table'), 'div.fa-star');
+
+                        // reset progress
+                        $('div.parameter-contexts-to-update').removeClass('ajax-loading ajax-complete ajax-error');
+
+                        //stop any synchronization
+                        if (fetchParameterProviderOptions) {
+                            $('#fetch-parameters-status-bar').statusbar('disconnect');
+                        }
+
+                        // reset dialog
+                        resetFetchParametersDialog();
+                    }
+                }
+            });
+
+            initParameterGroupsTable();
+            initSelectableParametersTable();
+
+            $(window).on('resize', function (e) {
+                if ($('#affected-referencing-components-container').is(':visible')) {
+                    updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                }
+
+                if ($('#fetch-parameter-referencing-components-container').is(':visible')) {
+                    updateReferencingComponentsBorder($('#fetch-parameter-referencing-components-container'));
+                }
+            });
+        },
+
+        /**
+         * Shows the configuration dialog for the specified parameter provider.
+         *
+         * @argument {parameterProvider} parameterProviderEntity the parameter provider
+         */
+        showConfiguration: function (parameterProviderEntity) {
+            var parameterProviderDialog = $('#parameter-provider-configuration');
+
+            parameterProviderDialog.find('.dialog-header .dialog-header-text').text('Configure Parameter Provider');
+            if (parameterProviderDialog.data('mode') === config.readOnly) {
+                // update the visibility
+                $('#parameter-provider-configuration .parameter-provider-read-only').hide();
+                $('#parameter-provider-configuration .parameter-provider-editable').show();
+
+                // initialize the property table
+                $('#parameter-provider-properties').propertytable('destroy').propertytable({
+                    readOnly: false,
+                    supportsGoTo: true,
+                    dialogContainer: '#new-parameter-provider-property-container',
+                    descriptorDeferred: getParameterProviderPropertyDescriptor,
+                    controllerServiceCreatedDeferred: function (response) {
+                        return nfControllerServices.loadControllerServices(controllerServicesUri, $('#controller-services-table'));
+                    },
+                    goToServiceDeferred: goToServiceFromProperty
+                });
+
+                // update the mode
+                parameterProviderDialog.data('mode', config.edit);
+            }
+
+            // reload the provider in case the property descriptors have changed
+            var reloadProvider = $.ajax({
+                type: 'GET',
+                url: parameterProviderEntity.uri,
+                dataType: 'json'
+            });
+
+            // get the parameter provider history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/flow/history/components/' + encodeURIComponent(parameterProviderEntity.id),
+                dataType: 'json'
+            });
+
+            // once everything is loaded, show the dialog
+            $.when(reloadProvider, loadHistory).done(function (providerResponse, historyResponse) {
+                // get the updated parameter provider
+                parameterProviderEntity = providerResponse[0];
+                currentParameterProviderEntity = parameterProviderEntity;
+                var parameterProvider = parameterProviderEntity.component;
+
+                // get the parameter provider history
+                var parameterProviderHistory = historyResponse[0].componentHistory;
+
+                // record the parameter provider details
+                $('#parameter-provider-configuration').data('parameterProviderDetails', parameterProviderEntity);
+
+                // populate the parameter provider settings
+                nfCommon.populateField('parameter-provider-id', parameterProvider['id']);
+
+                nfCommon.populateField('parameter-provider-type', nfCommon.formatType(parameterProvider));
+                $('#parameter-provider-configuration').modal('setSubtitle', nfCommon.formatType(parameterProvider));
+
+                nfCommon.populateField('parameter-provider-bundle', nfCommon.formatBundle(parameterProvider['bundle']));
+                $('#parameter-provider-name').val(parameterProvider['name']);
+                $('#parameter-provider-comments').val(parameterProvider['comments']);
+
+                // load referencing components for the parameter provider
+                loadParameterProviderReferencingComponents($('#parameter-provider-referencing-components'), parameterProvider);
+
+                var buttons = [{
+                    buttonText: 'Apply',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            // close all fields currently being edited
+                            $('#parameter-provider-properties').propertytable('saveRow');
+
+                            // save the parameter provider
+                            saveParameterProvider(parameterProviderEntity).done(function (response) {
+                                // reload the parameter provider
+                                nfControllerService.reloadReferencedServices(getControllerServicesTable(), response.component);
+
+                                // close the details panel
+                                closeModal('#parameter-provider-configuration');
+                            });
+                        }
+                    }
+                },
+                    {
+                        buttonText: 'Cancel',
+                        color: {
+                            base: '#E3E8EB',
+                            hover: '#C7D2D7',
+                            text: '#004849'
+                        },
+                        handler: {
+                            click: function () {
+                                closeModal('#parameter-provider-configuration');
+                            }
+                        }
+                    }];
+
+                // determine if we should show the advanced button
+                if (nfCommon.isDefinedAndNotNull(parameterProvider.customUiUrl) && parameterProvider.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        clazz: 'fa fa-cog button-icon',
+                        color: {
+                            base: '#E3E8EB',
+                            hover: '#C7D2D7',
+                            text: '#004849'
+                        },
+                        handler: {
+                            click: function () {
+                                var openCustomUi = function () {
+                                    // reset state and close the dialog manually to avoid hiding the faded background
+                                    closeModal('#parameter-provider-configuration');
+
+                                    // close the settings dialog since the custom ui is also opened in the shell
+                                    $('#shell-close-button').click();
+
+                                    // show the custom ui
+                                    nfCustomUi.showCustomUi(parameterProviderEntity, parameterProvider.customUiUrl, true).done(function () {
+                                        // once the custom ui is closed, reload the parameter provider
+                                        nfParameterProvider.reload(parameterProviderEntity.id).done(function (response) {
+                                            nfControllerService.reloadReferencedServices(getControllerServicesTable(), response.parameterProvider);
+                                        });
+
+                                        // show the settings
+                                        nfSettings.showSettings();
+                                    });
+                                };
+
+                                // close all fields currently being edited
+                                $('#parameter-provider-properties').propertytable('saveRow');
+
+                                // determine if changes have been made
+                                if (isSaveRequired()) {
+                                    // see if those changes should be saved
+                                    nfDialog.showYesNoDialog({
+                                        headerText: 'Save',
+                                        dialogContent: 'Save changes before opening the advanced configuration?',
+                                        noHandler: openCustomUi,
+                                        yesHandler: function () {
+                                            saveParameterProvider(parameterProviderEntity).done(function () {
+                                                // open the custom ui
+                                                openCustomUi();
+                                            });
+                                        }
+                                    });
+                                } else {
+                                    // if there were no changes, simply open the custom ui
+                                    openCustomUi();
+                                }
+                            }
+                        }
+                    });
+                }
+
+                // set the button model
+                $('#parameter-provider-configuration').modal('setButtonModel', buttons);
+
+                // load the property table
+                $('#parameter-provider-properties')
+                    .propertytable('setGroupId', null)
+                    .propertytable('loadProperties', parameterProvider.properties, parameterProvider.descriptors, parameterProviderHistory.propertyHistory)
+                    .propertytable('setPropertyVerificationCallback', function (proposedProperties) {
+                        nfVerify.verify(parameterProvider['id'], parameterProviderEntity['uri'], proposedProperties, referencedAttributes, handleVerificationResults, $('#parameter-provider-properties-verification-results-listing'));
+                    });
+
+                // show the details
+                $('#parameter-provider-configuration').modal('show');
+
+                $('#parameter-provider-properties').propertytable('resetTableSize');
+            }).fail(nfErrorHandler.handleAjaxError);
+        },
+
+        /**
+         * Shows the parameter provider details in a read only dialog.
+         *
+         * @param {object} parameterProviderEntity parameter provider id
+         */
+        showDetails: function (parameterProviderEntity) {
+            var parameterProviderDialog = $('#parameter-provider-configuration');
+
+            parameterProviderDialog.find('.dialog-header .dialog-header-text').text('Parameter Provider Details');
+            if (parameterProviderDialog.data('mode') === config.edit) {
+                // update the visibility
+                $('#parameter-provider-configuration .parameter-provider-read-only').show();
+                $('#parameter-provider-configuration .parameter-provider-editable').hide();
+
+                // initialize the property table
+                $('#parameter-provider-properties').propertytable('destroy').propertytable({
+                    supportsGoTo: true,
+                    readOnly: true
+                });
+
+                // update the mode
+                parameterProviderDialog.data('mode', config.readOnly);
+            }
+
+            // reload the provider in case the property descriptors have changed
+            var reloadProvider = $.ajax({
+                type: 'GET',
+                url: parameterProviderEntity.uri,
+                dataType: 'json'
+            });
+
+            // get the parameter provider history
+            var loadHistory = $.ajax({
+                type: 'GET',
+                url: '../nifi-api/flow/history/components/' + encodeURIComponent(parameterProviderEntity.id),
+                dataType: 'json'
+            });
+
+            // once everything is loaded, show the dialog
+            $.when(reloadProvider, loadHistory).done(function (providerResponse, historyResponse) {
+                // get the updated parameter provider
+                parameterProviderEntity = providerResponse[0];
+                var parameterProvider = parameterProviderEntity.component;
+
+                // get the parameter provider history
+                var parameterProviderHistory = historyResponse[0].componentHistory;
+
+                // populate the parameter provider settings
+                nfCommon.populateField('parameter-provider-id', parameterProvider['id']);
+                nfCommon.populateField('parameter-provider-type', nfCommon.substringAfterLast(parameterProvider['type'], '.'));
+                nfCommon.populateField('parameter-provider-bundle', nfCommon.formatBundle(parameterProvider['bundle']));
+                nfCommon.populateField('read-only-parameter-provider-name', parameterProvider['name']);
+                nfCommon.populateField('read-only-parameter-provider-comments', parameterProvider['comments']);
+
+                // load referencing components for the parameter provider
+                loadParameterProviderReferencingComponents($('#parameter-provider-referencing-components'), parameterProvider);
+
+                var buttons = [{
+                    buttonText: 'Ok',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            // hide the dialog
+                            closeModal('#parameter-provider-configuration');
+                        }
+                    }
+                }];
+
+                // determine if we should show the advanced button
+                if (nfCommon.isDefinedAndNotNull(nfCustomUi) && nfCommon.isDefinedAndNotNull(parameterProvider.customUiUrl) && parameterProvider.customUiUrl !== '') {
+                    buttons.push({
+                        buttonText: 'Advanced',
+                        clazz: 'fa fa-cog button-icon',
+                        color: {
+                            base: '#E3E8EB',
+                            hover: '#C7D2D7',
+                            text: '#004849'
+                        },
+                        handler: {
+                            click: function () {
+                                // reset state and close the dialog manually to avoid hiding the faded background
+                                closeModal('#parameter-provider-configuration');
+
+                                // close the settings dialog since the custom ui is also opened in the shell
+                                $('#shell-close-button').click();
+
+                                // show the custom ui
+                                nfCustomUi.showCustomUi(parameterProviderEntity, parameterProvider.customUiUrl, false).done(function () {
+                                    nfSettings.showSettings();
+                                });
+                            }
+                        }
+                    });
+                }
+
+                // show the dialog
+                parameterProviderDialog.modal('setButtonModel', buttons).modal('show');
+
+                // load the property table
+                $('#parameter-provider-properties')
+                    .propertytable('setGroupId', null)
+                    .propertytable('loadProperties', parameterProvider.properties, parameterProvider.descriptors, parameterProviderHistory.propertyHistory);
+
+                // show the details
+                parameterProviderDialog.modal('show');
+
+                $('#parameter-provider-properties').propertytable('resetTableSize');
+            });
+        },
+
+        /**
+         * Show the fetch parameters dialog.
+         *
+         * @param {object} parameterProviderEntity parameter provider id
+         */
+        showFetchDialog: function (parameterProviderEntity) {
+            showFetchParametersDialog(parameterProviderEntity, fetchParameterProviderOptions);
+        },
+
+        /**
+         * Reloads the specified parameter provider.
+         *
+         * @param {string} id
+         */
+        reload: function (id) {
+            var parameterProviderGrid = $('#parameter-providers-table').data('gridInstance');
+            var parameterProviderData = parameterProviderGrid.getData();
+            var parameterProviderEntity = parameterProviderData.getItemById(id);
+
+            return $.ajax({
+                type: 'GET',
+                url: parameterProviderEntity.uri,
+                dataType: 'json'
+            }).done(function (response) {
+                renderParameterProvider(response);
+            }).fail(nfErrorHandler.handleAjaxError);
+        },
+
+        /**
+         * Prompts the user before attempting to delete the specified parameter provider.
+         *
+         * @param {object} parameterProviderEntity parameter provider id
+         */
+        promptToDeleteParameterProvider: function (parameterProviderEntity) {
+            // prompt for deletion
+            nfDialog.showYesNoDialog({
+                headerText: 'Delete Parameter Provider',
+                dialogContent: 'Delete parameter provider \'' + nfCommon.escapeHtml(parameterProviderEntity.component.name) + '\'?',
+                yesHandler: function () {
+                    nfParameterProvider.remove(parameterProviderEntity);
+                }
+            });
+        },
+
+        /**
+         * Deletes the specified parameter provider.
+         *
+         * @param {object} parameterProviderEntity parameter provider id
+         */
+        remove: function (parameterProviderEntity) {
+            var revision = nfClient.getRevision(parameterProviderEntity);
+            $.ajax({
+                type: 'DELETE',
+                url: parameterProviderEntity.uri + '?' + $.param({
+                    'version': revision.version,
+                    'clientId': revision.clientId,
+                    'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged()
+                }),
+                dataType: 'json'
+            }).done(function (response) {
+                // remove the provider
+                var parameterProviderGrid = $('#parameter-providers-table').data('gridInstance');
+                var parameterProviderData = parameterProviderGrid.getData();
+                parameterProviderData.deleteItem(parameterProviderEntity.id);
+            }).fail(nfErrorHandler.handleAjaxError);
+        },
+
+        /**
+         * Shows the specified parameter provider.
+         */
+        showParameterProvider: function (parameterProviderId) {
+            // show the settings dialog
+            nfSettings.showSettings().done(function () {
+                var parameterProviderGrid = $('#parameter-providers-table').data('gridInstance');
+                var parameterProviderData = parameterProviderGrid.getData();
+
+                // select the desired provider
+                var row = parameterProviderData.getRowById(parameterProviderId);
+                parameterProviderGrid.setSelectedRows([row]);
+                parameterProviderGrid.scrollRowIntoView(row);
+
+                $('#settings-tabs').find('li:eq(4)').click();
+
+                // adjust the table size
+                nfSettings.resetTableSize();
+            });
+        }
+    };
+
+    return nfParameterProvider;
+}));
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
index a5874d2c07..01e699c311 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-policy-management.js
@@ -1556,6 +1556,60 @@
             return loadPolicy().always(showPolicy);
         },
 
+        /**
+         * Shows the paramter provider policy.
+         *
+         * @param d
+         */
+        showParameterProviderPolicy: function (d) {
+            initializingComponentPolicy = true;
+
+            // reset the policy message
+            resetPolicyMessage();
+
+            // update the policy controls visibility
+            $('#component-policy-controls').show();
+            $('#global-policy-controls').hide();
+
+            // update the visibility
+            if (d.permissions.canRead === true) {
+                $('#policy-selected-parameter-provider-container div.policy-selected-component-name').text(d.component.name);
+            } else {
+                $('#policy-selected-parameter-provider-container div.policy-selected-component-name').text(d.id);
+            }
+            $('#policy-selected-parameter-provider-container').show();
+
+            // populate the initial resource
+            $('#selected-policy-component-id').text(d.id);
+            $('#selected-policy-component-type').text('parameter-providers');
+            $('#component-policy-target')
+                .combo('setOptionEnabled', {
+                    value: 'operate-component'
+                }, false)
+                .combo('setOptionEnabled', {
+                    value: 'write-receive-data'
+                }, false)
+                .combo('setOptionEnabled', {
+                    value: 'write-send-data'
+                }, false)
+                .combo('setOptionEnabled', {
+                    value: 'read-data'
+                }, false)
+                .combo('setOptionEnabled', {
+                    value: 'read-provenance'
+                }, false)
+                .combo('setOptionEnabled', {
+                    value: 'write-data'
+                }, false)
+                .combo('setSelectedOption', {
+                    value: 'read-component'
+                });
+
+            initializingComponentPolicy = false;
+
+            return loadPolicy().always(showPolicy);
+        },
+
         /**
          * Shows the template policy.
          *
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
index b802c3b4b0..027f4ff27d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-processor-configuration.js
@@ -659,7 +659,7 @@
 
             //if the status bar is supported, initialize it.
             if(config.supportsStatusBar){
-                $('#processor-configuration-status-bar').statusbar();
+                $('#processor-configuration-status-bar').statusbar('processor');
             }
 
             // initialize the bulletin combo
@@ -784,6 +784,7 @@
                 $.when.apply(window, requests).done(function (processorResult, historyResult) {
                     // get the updated processor'
                     var processorResponse = processorResult[0];
+                    var bulletins = processorResponse.bulletins;
                     processor = processorResponse.component;
 
                     // get the processor history
@@ -1047,10 +1048,20 @@
                     //Synchronize the current component canvas attributes in the status bar
                     if(config.supportsStatusBar){
 
+                        var formattedBulletins = nfCommon.getFormattedBulletins(bulletins);
+                        var unorderedBulletins = nfCommon.formatUnorderedList(formattedBulletins);
+
                         //initialize the canvas synchronization
-                        $("#processor-configuration-status-bar").statusbar('observe',processor.id, function(){
-                            $('#processor-configuration').modal('refreshButtons');
-                        });
+                        $("#processor-configuration-status-bar").statusbar(
+                            'observe',
+                            {
+                                processor: processor.id,
+                                bulletins: unorderedBulletins
+                            },
+                            function () {
+                                $('#processor-configuration').modal('refreshButtons');
+                            }
+                        );
 
                         //if there are active threads, add the terminate button to the status bar
                         if(nfCommon.isDefinedAndNotNull(config.nfActions) &&
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
index a602335a57..0c5ef6acbc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
@@ -34,9 +34,10 @@
                 'nf.Shell',
                 'nf.ComponentState',
                 'nf.ComponentVersion',
-                'nf.PolicyManagement'],
-            function ($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement) {
-                return (nf.Settings = factory($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement));
+                'nf.PolicyManagement',
+                'nf.ParameterProvider'],
+            function ($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement, nfParameterProvider) {
+                return (nf.Settings = factory($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement, nfParameterProvider));
             });
     } else if (typeof exports === 'object' && typeof module === 'object') {
         module.exports = (nf.Settings =
@@ -55,7 +56,8 @@
                 require('nf.Shell'),
                 require('nf.ComponentState'),
                 require('nf.ComponentVersion'),
-                require('nf.PolicyManagement')));
+                require('nf.PolicyManagement'),
+                require('nf.ParameterProvider')));
     } else {
         nf.Settings = factory(root.$,
             root.Slick,
@@ -72,9 +74,10 @@
             root.nf.Shell,
             root.nf.ComponentState,
             root.nf.ComponentVersion,
-            root.nf.PolicyManagement);
+            root.nf.PolicyManagement,
+            root.nf.ParameterProvider);
     }
-}(this, function ($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement) {
+}(this, function ($, Slick, d3, nfClient, nfDialog, nfStorage, nfCommon, nfCanvasUtils, nfControllerServices, nfErrorHandler, nfFilteredDialogCommon, nfReportingTask, nfShell, nfComponentState, nfComponentVersion, nfPolicyManagement, nfParameterProvider) {
     'use strict';
 
 
@@ -85,7 +88,10 @@
             reportingTaskTypes: '../nifi-api/flow/reporting-task-types',
             createReportingTask: '../nifi-api/controller/reporting-tasks',
             reportingTasks: '../nifi-api/flow/reporting-tasks',
-            registries: '../nifi-api/controller/registry-clients'
+            registries: '../nifi-api/controller/registry-clients',
+            createParameterProvider: '../nifi-api/controller/parameter-providers',
+            parameterProviderTypes: '../nifi-api/flow/parameter-provider-types',
+            parameterProviders: '../nifi-api/flow/parameter-providers'
         }
     };
 
@@ -345,6 +351,15 @@
         return $('#reporting-task-type-filter').val();
     };
 
+    /**
+     * Get the text out of the filter field. If the filter field doesn't
+     * have any text it will contain the text 'filter list' so this method
+     * accounts for that.
+     */
+    var getParameterProviderTypeFilterText = function () {
+        return $('#parameter-provider-type-filter').val();
+    };
+
     /**
      * Filters the reporting task type table.
      */
@@ -372,6 +387,33 @@
         }
     };
 
+    /**
+     * Filters the parameter provider type table.
+     */
+    var applyParameterProviderTypeFilter = function () {
+        // get the dataview
+        var parameterProviderTypesGrid = $('#parameter-provider-types-table').data('gridInstance');
+
+        // ensure the grid has been initialized
+        if (nfCommon.isDefinedAndNotNull(parameterProviderTypesGrid)) {
+            var parameterProviderTypesData = parameterProviderTypesGrid.getData();
+
+            // update the search criteria
+            parameterProviderTypesData.setFilterArgs({
+                searchString: getParameterProviderTypeFilterText()
+            });
+            parameterProviderTypesData.refresh();
+
+            // update the buttons to possibly trigger the disabled state
+            $('#new-parameter-provider-dialog').modal('refreshButtons');
+
+            // update the selection if possible
+            if (parameterProviderTypesData.getLength() > 0) {
+                nfFilteredDialogCommon.choseFirstRow(parameterProviderTypesGrid);
+            }
+        }
+    };
+
     /**
      * Hides the selected reporting task.
      */
@@ -384,6 +426,18 @@
         $('#reporting-task-description-container').hide();
     };
 
+    /**
+     * Hides the selected parameter provider.
+     */
+    var clearSelectedParameterProvider = function () {
+        $('#parameter-provider-type-description').attr('title', '').text('');
+        $('#parameter-provider-type-name').attr('title', '').text('');
+        $('#parameter-provider-type-bundle').attr('title', '').text('');
+        $('#selected-parameter-provider-name').text('');
+        $('#selected-parameter-provider-type').text('').removeData('bundle');
+        $('#parameter-provider-description-container').hide();
+    };
+
     /**
      * Clears the selected reporting task type.
      */
@@ -396,6 +450,18 @@
         reportingTaskTypesGrid.resetActiveCell();
     };
 
+    /**
+     * Clears the selected parameter provider type.
+     */
+    var clearParameterProviderSelection = function () {
+        // clear the selected row
+        clearSelectedParameterProvider();
+
+        // clear the active cell then it can be reselected when its included
+        var parameterProviderTypesGrid = $('#parameter-provider-types-table').data('gridInstance');
+        parameterProviderTypesGrid.resetActiveCell();
+    };
+
     /**
      * Performs the filtering.
      *
@@ -437,6 +503,47 @@
         return matches;
     };
 
+    /**
+     * Performs the filtering.
+     *
+     * @param {object} item     The item subject to filtering
+     * @param {object} args     Filter arguments
+     * @returns {Boolean}       Whether or not to include the item
+     */
+    var filterParameterProviderTypes = function (item, args) {
+        // determine if the item matches the filter
+        var matchesFilter = matchesRegex(item, args);
+
+        // determine if the row matches the selected tags
+        var matchesTags = true;
+        if (matchesFilter) {
+            var tagFilters = $('#parameter-provider-tag-cloud').tagcloud('getSelectedTags');
+            var hasSelectedTags = tagFilters.length > 0;
+            if (hasSelectedTags) {
+                matchesTags = matchesSelectedTags(tagFilters, item['tags']);
+            }
+        }
+
+        // determine if the row matches the selected source group
+        var matchesGroup = true;
+        if (matchesFilter && matchesTags) {
+            var bundleGroup = $('#parameter-provider-bundle-group-combo').combo('getSelectedOption');
+            if (nfCommon.isDefinedAndNotNull(bundleGroup) && bundleGroup.value !== '') {
+                matchesGroup = (item.bundle.group === bundleGroup.value);
+            }
+        }
+
+        // determine if this row should be visible
+        var matches = matchesFilter && matchesTags && matchesGroup;
+
+        // if this row is currently selected and its being filtered
+        if (matches === false && $('#selected-parameter-provider-type').text() === item['type']) {
+            clearParameterProviderSelection();
+        }
+
+        return matches;
+    };
+
     /**
      * Adds the currently selected reporting task.
      */
@@ -455,6 +562,24 @@
         }
     };
 
+    /**
+     * Adds the currently selected parameter provider.
+     */
+    var addSelectedParameterProvider = function () {
+        var selectedParameterProviderType = $('#selected-parameter-provider-type').text();
+        var selectedParameterProviderBundle = $('#selected-selected-parameter-type').data('bundle');
+
+        // ensure something was selected
+        if (selectedParameterProviderType === '') {
+            nfDialog.showOkDialog({
+                headerText: 'Settings',
+                dialogContent: 'The type of parameter provider to create must be selected.'
+            });
+        } else {
+            addParameterProvider(selectedParameterProviderType, selectedParameterProviderBundle);
+        }
+    };
+
     /**
      * Adds a new reporting task of the specified type.
      *
@@ -558,6 +683,59 @@
         return addRegistry;
     };
 
+    /**
+     * Adds a new parameter provider of the specified type.
+     *
+     * @param {string} parameterProviderType
+     * @param {object} parameterProviderBundle
+     */
+    var addParameterProvider = function (parameterProviderType, parameterProviderBundle) {
+        // build the parameter provider entity
+        var parameterProviderEntity = {
+            'revision': nfClient.getRevision({
+                'revision': {
+                    'version': 0
+                }
+            }),
+            'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
+            'component': {
+                'type': parameterProviderType,
+                'bundle': parameterProviderBundle
+            }
+        };
+
+        // add the new parameter provider
+        var addProvider = $.ajax({
+            type: 'POST',
+            url: config.urls.createParameterProvider,
+            data: JSON.stringify(parameterProviderEntity),
+            dataType: 'json',
+            contentType: 'application/json'
+        }).done(function (parameterProviderEntity) {
+            // add the item
+            var parameterProviderGrid = $('#parameter-providers-table').data('gridInstance');
+            var parameterProviderData = parameterProviderGrid.getData();
+            parameterProviderData.addItem($.extend({
+                type: 'ParameterProvider',
+                bulletins: []
+            }, parameterProviderEntity));
+
+            // resort
+            parameterProviderData.reSort();
+            parameterProviderGrid.invalidate();
+
+            // select the new parameter provider
+            var row = parameterProviderData.getRowById(parameterProviderEntity.id);
+            nfFilteredDialogCommon.choseRow(parameterProviderGrid, row);
+            parameterProviderGrid.scrollRowIntoView(row);
+        }).fail(nfErrorHandler.handleAjaxError);
+
+        // hide the dialog
+        $('#new-parameter-provider-dialog').modal('hide');
+
+        return addProvider;
+    };
+
     /**
      * Updates the registry with the specified id.
      *
@@ -878,16 +1056,418 @@
             });
 
             // initialize the bundle group combo
-            $('#reporting-task-bundle-group-combo').combo({
+            $('#reporting-task-bundle-group-combo').combo({
+                options: options,
+                select: applyReportingTaskTypeFilter
+            });
+        }).fail(nfErrorHandler.handleAjaxError);
+
+        var navigationKeys = [$.ui.keyCode.UP, $.ui.keyCode.PAGE_UP, $.ui.keyCode.DOWN, $.ui.keyCode.PAGE_DOWN];
+
+        // define the function for filtering the list
+        $('#reporting-task-type-filter').off('keyup').on('keyup', function (e) {
+            var code = e.keyCode ? e.keyCode : e.which;
+
+            // ignore navigation keys
+            if ($.inArray(code, navigationKeys) !== -1) {
+                return;
+            }
+
+            if (code === $.ui.keyCode.ENTER) {
+                var selected = reportingTaskTypesGrid.getSelectedRows();
+
+                if (selected.length > 0) {
+                    // grid configured with multi-select = false
+                    var item = reportingTaskTypesGrid.getDataItem(selected[0]);
+                    if (isSelectable(item)) {
+                        addSelectedReportingTask();
+                    }
+                }
+            } else {
+                applyReportingTaskTypeFilter();
+            }
+        });
+
+        // setup row navigation
+        nfFilteredDialogCommon.addKeydownListener('#reporting-task-type-filter', reportingTaskTypesGrid, reportingTaskTypesGrid.getData());
+
+        // initialize the reporting task dialog
+        $('#new-reporting-task-dialog').modal({
+            scrollableContentStyle: 'scrollable',
+            headerText: 'Add Reporting Task',
+            buttons: [{
+                buttonText: 'Add',
+                color: {
+                    base: '#728E9B',
+                    hover: '#004849',
+                    text: '#ffffff'
+                },
+                disabled: function () {
+                    var selected = reportingTaskTypesGrid.getSelectedRows();
+
+                    if (selected.length > 0) {
+                        // grid configured with multi-select = false
+                        var item = reportingTaskTypesGrid.getDataItem(selected[0]);
+                        return isSelectable(item) === false;
+                    } else {
+                        return reportingTaskTypesGrid.getData().getLength() === 0;
+                    }
+                },
+                handler: {
+                    click: function () {
+                        addSelectedReportingTask();
+                    }
+                }
+            },
+                {
+                    buttonText: 'Cancel',
+                    color: {
+                        base: '#E3E8EB',
+                        hover: '#C7D2D7',
+                        text: '#004849'
+                    },
+                    handler: {
+                        click: function () {
+                            $(this).modal('hide');
+                        }
+                    }
+                }],
+            handler: {
+                close: function () {
+                    // clear the selected row
+                    clearSelectedReportingTask();
+
+                    // clear any filter strings
+                    $('#reporting-task-type-filter').val('');
+
+                    // clear the tagcloud
+                    $('#reporting-task-tag-cloud').tagcloud('clearSelectedTags');
+
+                    // reset the group combo
+                    $('#reporting-task-bundle-group-combo').combo('setSelectedOption', {
+                        value: ''
+                    });
+
+                    // reset the filter
+                    applyReportingTaskTypeFilter();
+
+                    // unselect any current selection
+                    var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
+                    reportingTaskTypesGrid.setSelectedRows([]);
+                    reportingTaskTypesGrid.resetActiveCell();
+                },
+                resize: function () {
+                    $('#reporting-task-type-description')
+                        .width($('#reporting-task-description-container').innerWidth() - 1)
+                        .text($('#reporting-task-type-description').attr('title'))
+                        .ellipsis();
+                }
+            }
+        });
+
+        // initialize the registry configuration dialog
+        $('#registry-configuration-dialog').modal({
+            scrollableContentStyle: 'scrollable',
+            handler: {
+                close: function () {
+                    $('#registry-id').text('');
+                    $('#registry-name').val('');
+                    $('#registry-location').val('');
+                    $('#registry-description').val('');
+                }
+            }
+        });
+    };
+
+    /**
+     * Initializes the new parameter provider dialog.
+     */
+    var initNewParameterProviderDialog = function () {
+        // initialize the parameter provider type table
+        var parameterProviderColumns = [
+            {
+                id: 'type',
+                name: 'Type',
+                field: 'label',
+                formatter: nfCommon.typeFormatter,
+                sortable: true,
+                resizable: true
+            },
+            {
+                id: 'version',
+                name: 'Version',
+                field: 'version',
+                formatter: nfCommon.typeVersionFormatter,
+                sortable: true,
+                resizable: true
+            },
+            {
+                id: 'tags',
+                name: 'Tags',
+                field: 'tags',
+                sortable: true,
+                resizable: true,
+                formatter: nfCommon.genericValueFormatter
+            }
+        ];
+
+        // initialize the dataview
+        var parameterProviderTypesData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        parameterProviderTypesData.setItems([]);
+        parameterProviderTypesData.setFilterArgs({
+            searchString: getParameterProviderTypeFilterText()
+        });
+        parameterProviderTypesData.setFilter(filterParameterProviderTypes);
+
+        // initialize the sort
+        nfCommon.sortType({
+            columnId: 'type',
+            sortAsc: true
+        }, parameterProviderTypesData);
+
+        // initialize the grid
+        var parameterProviderTypesGrid = new Slick.Grid('#parameter-provider-types-table', parameterProviderTypesData, parameterProviderColumns, gridOptions);
+        parameterProviderTypesGrid.setSelectionModel(new Slick.RowSelectionModel());
+        parameterProviderTypesGrid.registerPlugin(new Slick.AutoTooltips());
+        parameterProviderTypesGrid.setSortColumn('type', true);
+        parameterProviderTypesGrid.onSort.subscribe(function (e, args) {
+            nfCommon.sortType({
+                columnId: args.sortCol.field,
+                sortAsc: args.sortAsc
+            }, parameterProviderTypesData);
+        });
+        parameterProviderTypesGrid.onSelectedRowsChanged.subscribe(function (e, args) {
+            if ($.isArray(args.rows) && args.rows.length === 1) {
+                var parameterProviderTypeIndex = args.rows[0];
+                var parameterProviderType = parameterProviderTypesGrid.getDataItem(parameterProviderTypeIndex);
+
+                // set the parameter provider type description
+                if (nfCommon.isDefinedAndNotNull(parameterProviderType)) {
+                    // show the selected parameter provider
+                    $('#parameter-provider-description-container').show();
+
+                    if (nfCommon.isBlank(parameterProviderType.description)) {
+                        $('#parameter-provider-type-description')
+                            .attr('title', '')
+                            .html('<span class="unset">No description specified</span>');
+                    } else {
+                        $('#parameter-provider-type-description')
+                            .width($('#parameter-provider-description-container').innerWidth() - 1)
+                            .html(parameterProviderType.description)
+                            .ellipsis();
+                    }
+
+                    var bundle = nfCommon.formatBundle(parameterProviderType.bundle);
+                    var type = nfCommon.formatType(parameterProviderType);
+
+                    // populate the dom
+                    $('#parameter-provider-type-name').text(type).attr('title', type);
+                    $('#parameter-provider-type-bundle').text(bundle).attr('title', bundle);
+                    $('#selected-parameter-provider-name').text(parameterProviderType.label);
+                    $('#selected-parameter-provider-type').text(parameterProviderType.type).data('bundle', parameterProviderType.bundle);
+
+                    // refresh the buttons based on the current selection
+                    $('#new-parameter-provider-dialog').modal('refreshButtons');
+                }
+            }
+        });
+        parameterProviderTypesGrid.onDblClick.subscribe(function (e, args) {
+            var parameterProviderType = parameterProviderTypesGrid.getDataItem(args.row);
+
+            if (isSelectable(parameterProviderType)) {
+                addParameterProvider(parameterProviderType.type, parameterProviderType.bundle);
+            }
+        });
+        parameterProviderTypesGrid.onViewportChanged.subscribe(function (e, args) {
+            nfCommon.cleanUpTooltips($('#parameter-provider-types-table'), 'div.view-usage-restriction');
+        });
+
+        // wire up the dataview to the grid
+        parameterProviderTypesData.onRowCountChanged.subscribe(function (e, args) {
+            parameterProviderTypesGrid.updateRowCount();
+            parameterProviderTypesGrid.render();
+
+            // update the total number of displayed processors
+            $('#displayed-parameter-provider-types').text(args.current);
+        });
+        parameterProviderTypesData.onRowsChanged.subscribe(function (e, args) {
+            parameterProviderTypesGrid.invalidateRows(args.rows);
+            parameterProviderTypesGrid.render();
+        });
+        parameterProviderTypesData.syncGridSelection(parameterProviderTypesGrid, true);
+
+        // hold onto an instance of the grid
+        $('#parameter-provider-types-table').data('gridInstance', parameterProviderTypesGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var usageRestriction = $(this).find('div.view-usage-restriction');
+            if (usageRestriction.length && !usageRestriction.data('qtip')) {
+                var rowId = $(this).find('span.row-id').text();
+
+                // get the status item
+                var item = parameterProviderTypesData.getItemById(rowId);
+
+                // show the tooltip
+                if (item.restricted === true) {
+                    var restrictionTip = $('<div></div>');
+
+                    if (nfCommon.isBlank(item.usageRestriction)) {
+                        restrictionTip.append($('<p style="margin-bottom: 3px;"></p>').text('Requires the following permissions:'));
+                    } else {
+                        restrictionTip.append($('<p style="margin-bottom: 3px;"></p>').text(item.usageRestriction + ' Requires the following permissions:'));
+                    }
+
+                    var restrictions = [];
+                    if (nfCommon.isDefinedAndNotNull(item.explicitRestrictions)) {
+                        $.each(item.explicitRestrictions, function (_, explicitRestriction) {
+                            var requiredPermission = explicitRestriction.requiredPermission;
+                            restrictions.push("'" + requiredPermission.label + "' - " + nfCommon.escapeHtml(explicitRestriction.explanation));
+                        });
+                    } else {
+                        restrictions.push('Access to restricted components regardless of restrictions.');
+                    }
+                    restrictionTip.append(nfCommon.formatUnorderedList(restrictions));
+
+                    usageRestriction.qtip($.extend({}, nfCommon.config.tooltipConfig, {
+                        content: restrictionTip,
+                        position: {
+                            container: $('#summary'),
+                            at: 'bottom right',
+                            my: 'top left',
+                            adjust: {
+                                x: 4,
+                                y: 4
+                            }
+                        }
+                    }));
+                }
+            }
+        });
+
+        var generalRestriction = nfCommon.getPolicyTypeListing('restricted-components');
+
+        // load the available parameter providers
+        $.ajax({
+            type: 'GET',
+            url: config.urls.parameterProviderTypes,
+            dataType: 'json'
+        }).done(function (response) {
+            var id = 0;
+            var tags = [];
+            var groups = d3.set();
+            var restrictedUsage = d3.map();
+            var requiredPermissions = d3.map();
+
+            // begin the update
+            parameterProviderTypesData.beginUpdate();
+
+            // go through each parameter provider type
+            $.each(response.parameterProviderTypes, function (i, documentedType) {
+                if (documentedType.restricted === true) {
+                    if (nfCommon.isDefinedAndNotNull(documentedType.explicitRestrictions)) {
+                        $.each(documentedType.explicitRestrictions, function (_, explicitRestriction) {
+                            var requiredPermission = explicitRestriction.requiredPermission;
+
+                            // update required permissions
+                            if (!requiredPermissions.has(requiredPermission.id)) {
+                                requiredPermissions.set(requiredPermission.id, requiredPermission.label);
+                            }
+
+                            // update component restrictions
+                            if (!restrictedUsage.has(requiredPermission.id)) {
+                                restrictedUsage.set(requiredPermission.id, []);
+                            }
+
+                            restrictedUsage.get(requiredPermission.id).push({
+                                type: nfCommon.formatType(documentedType),
+                                bundle: nfCommon.formatBundle(documentedType.bundle),
+                                explanation: nfCommon.escapeHtml(explicitRestriction.explanation)
+                            })
+                        });
+                    } else {
+                        // update required permissions
+                        if (!requiredPermissions.has(generalRestriction.value)) {
+                            requiredPermissions.set(generalRestriction.value, generalRestriction.text);
+                        }
+
+                        // update component restrictions
+                        if (!restrictedUsage.has(generalRestriction.value)) {
+                            restrictedUsage.set(generalRestriction.value, []);
+                        }
+
+                        restrictedUsage.get(generalRestriction.value).push({
+                            type: nfCommon.formatType(documentedType),
+                            bundle: nfCommon.formatBundle(documentedType.bundle),
+                            explanation: nfCommon.escapeHtml(documentedType.usageRestriction)
+                        });
+                    }
+                }
+
+                // record the group
+                groups.add(documentedType.bundle.group);
+
+                // add the documented type
+                parameterProviderTypesData.addItem({
+                    id: id++ + '',
+                    label: nfCommon.substringAfterLast(documentedType.type, '.'),
+                    type: documentedType.type,
+                    bundle: documentedType.bundle,
+                    description: nfCommon.escapeHtml(documentedType.description),
+                    restricted:  documentedType.restricted,
+                    usageRestriction: nfCommon.escapeHtml(documentedType.usageRestriction),
+                    explicitRestrictions: documentedType.explicitRestrictions,
+                    tags: documentedType.tags.join(', ')
+                });
+
+                // count the frequency of each tag for this type
+                $.each(documentedType.tags, function (i, tag) {
+                    tags.push(tag.toLowerCase());
+                });
+            });
+
+            // end the update
+            parameterProviderTypesData.endUpdate();
+
+            // resort
+            parameterProviderTypesData.reSort();
+            parameterProviderTypesGrid.invalidate();
+
+            // set the component restrictions and the corresponding required permissions
+            nfCanvasUtils.addComponentRestrictions(restrictedUsage, requiredPermissions);
+
+            // set the total number of processors
+            $('#total-parameter-provider-types, #displayed-parameter-provider-types').text(response.parameterProviderTypes.length);
+
+            // create the tag cloud
+            $('#parameter-provider-tag-cloud').tagcloud({
+                tags: tags,
+                select: applyParameterProviderTypeFilter,
+                remove: applyParameterProviderTypeFilter
+            });
+
+            // build the combo options
+            var options = [{
+                text: 'all groups',
+                value: ''
+            }];
+            groups.each(function (group) {
+                options.push({
+                    text: group,
+                    value: group
+                });
+            });
+
+            // initialize the bundle group combo
+            $('#parameter-provider-bundle-group-combo').combo({
                 options: options,
-                select: applyReportingTaskTypeFilter
+                select: applyParameterProviderTypeFilter
             });
         }).fail(nfErrorHandler.handleAjaxError);
 
         var navigationKeys = [$.ui.keyCode.UP, $.ui.keyCode.PAGE_UP, $.ui.keyCode.DOWN, $.ui.keyCode.PAGE_DOWN];
 
         // define the function for filtering the list
-        $('#reporting-task-type-filter').off('keyup').on('keyup', function (e) {
+        $('#parameter-provider-type-filter').off('keyup').on('keyup', function (e) {
             var code = e.keyCode ? e.keyCode : e.which;
 
             // ignore navigation keys
@@ -896,27 +1476,27 @@
             }
 
             if (code === $.ui.keyCode.ENTER) {
-                var selected = reportingTaskTypesGrid.getSelectedRows();
+                var selected = parameterProviderTypesGrid.getSelectedRows();
 
                 if (selected.length > 0) {
                     // grid configured with multi-select = false
-                    var item = reportingTaskTypesGrid.getDataItem(selected[0]);
+                    var item = parameterProviderTypesGrid.getDataItem(selected[0]);
                     if (isSelectable(item)) {
-                        addSelectedReportingTask();
+                        addSelectedParameterProvider();
                     }
                 }
             } else {
-                applyReportingTaskTypeFilter();
+                applyParameterProviderTypeFilter();
             }
         });
 
         // setup row navigation
-        nfFilteredDialogCommon.addKeydownListener('#reporting-task-type-filter', reportingTaskTypesGrid, reportingTaskTypesGrid.getData());
+        nfFilteredDialogCommon.addKeydownListener('#parameter-provider-type-filter', parameterProviderTypesGrid, parameterProviderTypesGrid.getData());
 
-        // initialize the reporting task dialog
-        $('#new-reporting-task-dialog').modal({
+        // initialize the parameter provider dialog
+        $('#new-parameter-provider-dialog').modal({
             scrollableContentStyle: 'scrollable',
-            headerText: 'Add Reporting Task',
+            headerText: 'Add Parameter Provider',
             buttons: [{
                 buttonText: 'Add',
                 color: {
@@ -925,19 +1505,19 @@
                     text: '#ffffff'
                 },
                 disabled: function () {
-                    var selected = reportingTaskTypesGrid.getSelectedRows();
+                    var selected = parameterProviderTypesGrid.getSelectedRows();
 
                     if (selected.length > 0) {
                         // grid configured with multi-select = false
-                        var item = reportingTaskTypesGrid.getDataItem(selected[0]);
+                        var item = parameterProviderTypesGrid.getDataItem(selected[0]);
                         return isSelectable(item) === false;
                     } else {
-                        return reportingTaskTypesGrid.getData().getLength() === 0;
+                        return parameterProviderTypesGrid.getData().getLength() === 0;
                     }
                 },
                 handler: {
                     click: function () {
-                        addSelectedReportingTask();
+                        addSelectedParameterProvider();
                     }
                 }
             },
@@ -957,48 +1537,35 @@
             handler: {
                 close: function () {
                     // clear the selected row
-                    clearSelectedReportingTask();
+                    clearSelectedParameterProvider();
 
                     // clear any filter strings
-                    $('#reporting-task-type-filter').val('');
+                    $('#parameter-provider-type-filter').val('');
 
                     // clear the tagcloud
-                    $('#reporting-task-tag-cloud').tagcloud('clearSelectedTags');
+                    $('#parameter-provider-tag-cloud').tagcloud('clearSelectedTags');
 
                     // reset the group combo
-                    $('#reporting-task-bundle-group-combo').combo('setSelectedOption', {
+                    $('#parameter-provider-bundle-group-combo').combo('setSelectedOption', {
                         value: ''
                     });
 
                     // reset the filter
-                    applyReportingTaskTypeFilter();
+                    applyParameterProviderTypeFilter();
 
                     // unselect any current selection
-                    var reportingTaskTypesGrid = $('#reporting-task-types-table').data('gridInstance');
-                    reportingTaskTypesGrid.setSelectedRows([]);
-                    reportingTaskTypesGrid.resetActiveCell();
+                    var parameterProviderTypesGrid = $('#parameter-provider-types-table').data('gridInstance');
+                    parameterProviderTypesGrid.setSelectedRows([]);
+                    parameterProviderTypesGrid.resetActiveCell();
                 },
                 resize: function () {
-                    $('#reporting-task-type-description')
-                        .width($('#reporting-task-description-container').innerWidth() - 1)
-                        .text($('#reporting-task-type-description').attr('title'))
+                    $('#parameter-provider-type-description')
+                        .width($('#parameter-provider-description-container').innerWidth() - 1)
+                        .text($('#parameter-provider-type-description').attr('title'))
                         .ellipsis();
                 }
             }
         });
-
-        // initialize the registry configuration dialog
-        $('#registry-configuration-dialog').modal({
-            scrollableContentStyle: 'scrollable',
-            handler: {
-                close: function () {
-                    $('#registry-id').text('');
-                    $('#registry-name').val('');
-                    $('#registry-location').val('');
-                    $('#registry-description').val('');
-                }
-            }
-        });
     };
 
     /**
@@ -1458,6 +2025,263 @@
         $('#registries-table').data('gridInstance', registriesGrid);
     };
 
+    /**
+     * Initializes the parameter providers tab.
+     */
+    var initParameterProvidersTable = function () {
+        // initialize the new parameter provider dialog
+        initNewParameterProviderDialog();
+
+        var moreParameterProvidersDetails = function (row, cell, value, columnDef, dataContext) {
+            if (!dataContext.permissions.canRead) {
+                return '';
+            }
+
+            var markup = '<div title="View Details" class="pointer view-parameter-provider fa fa-info-circle"></div>';
+
+            // always include a button to view the usage
+            markup += '<div title="Usage" class="pointer parameter-provider-usage fa fa-book"></div>';
+
+            var hasErrors = !nfCommon.isEmpty(dataContext.component.validationErrors);
+            var hasBulletins = !nfCommon.isEmpty(dataContext.bulletins);
+
+            if (hasErrors) {
+                markup += '<div class="pointer has-errors fa fa-warning" ></div>';
+            }
+
+            if (hasBulletins) {
+                markup += '<div class="has-bulletins fa fa-sticky-note-o"></div>';
+            }
+
+            if (hasErrors || hasBulletins) {
+                markup += '<span class="hidden row-id">' + nfCommon.escapeHtml(dataContext.component.id) + '</span>';
+            }
+
+            return markup;
+        };
+
+        var parameterProvidersActionFormatter = function (row, cell, value, columnDef, dataContext) {
+            var markup = '';
+
+            var canWrite = dataContext.permissions.canWrite;
+            var canRead = dataContext.permissions.canRead;
+
+            var hasErrors = canRead ? !nfCommon.isEmpty(dataContext.component.validationErrors) : null;
+
+            var hasReadParameterContextsPermissions = true;
+
+            if (canRead && dataContext.component.referencingParameterContexts) {
+                (dataContext.component.referencingParameterContexts).every(function (refParamContext) {
+                    return hasReadParameterContextsPermissions = refParamContext.permissions.canRead;
+                });
+            }
+
+            if (canRead && canWrite) {
+                markup += '<div title="Edit" class="pointer edit-parameter-provider fa fa-pencil"></div>';
+
+                if (hasReadParameterContextsPermissions && !hasErrors) {
+                    markup += '<div title="Fetch Parameters" class="pointer fetch-parameter-provider fa fa-arrow-circle-down"></div>';
+                }
+            }
+
+            if (canRead && canWrite && nfCommon.canModifyController()) {
+                markup += '<div title="Remove" class="pointer delete-parameter-provider fa fa-trash"></div>';
+            }
+
+            // allow policy configuration conditionally
+            if (nfCanvasUtils.isManagedAuthorizer() && nfCommon.canAccessTenants()) {
+                markup += '<div title="Access Policies" class="pointer edit-access-policies fa fa-key"></div>';
+            }
+
+            return markup;
+        };
+
+        // define the column model for the parameter providers table
+        var parameterProvidersColumnModel = [
+            {
+                id: 'moreDetails',
+                name: '&nbsp;',
+                resizable: false,
+                formatter: moreParameterProvidersDetails,
+                sortable: true,
+                width: 90,
+                maxWidth: 90,
+                toolTip: 'Sorts based on presence of bulletins'
+            },
+            {
+                id: 'name',
+                name: 'Name',
+                sortable: true,
+                resizable: true,
+                formatter: nameFormatter
+            },
+            {
+                id: 'type',
+                name: 'Type',
+                formatter: nfCommon.instanceTypeFormatter,
+                sortable: true,
+                resizable: true
+            },
+            {
+                id: 'bundle',
+                name: 'Bundle',
+                formatter: nfCommon.instanceBundleFormatter,
+                sortable: true,
+                resizable: true
+            }
+        ];
+
+        // action column should always be last
+        parameterProvidersColumnModel.push({
+            id: 'actions',
+            name: '&nbsp;',
+            resizable: false,
+            formatter: parameterProvidersActionFormatter,
+            sortable: false,
+            width: 115,
+            maxWidth: 115
+        });
+
+        // initialize the dataview
+        var parameterProvidersData = new Slick.Data.DataView({
+            inlineFilters: false
+        });
+        parameterProvidersData.setItems([]);
+
+        // initialize the sort
+        sort({
+            columnId: 'name',
+            sortAsc: true
+        }, parameterProvidersData);
+
+        // initialize the grid
+        var parameterProvidersGrid = new Slick.Grid('#parameter-providers-table', parameterProvidersData, parameterProvidersColumnModel, gridOptions);
+        parameterProvidersGrid.setSelectionModel(new Slick.RowSelectionModel());
+        parameterProvidersGrid.registerPlugin(new Slick.AutoTooltips());
+        parameterProvidersGrid.setSortColumn('name', true);
+        parameterProvidersGrid.onSort.subscribe(function (e, args) {
+            sort({
+                columnId: args.sortCol.id,
+                sortAsc: args.sortAsc
+            }, parameterProvidersData);
+        });
+
+        // configure a click listener
+        parameterProvidersGrid.onClick.subscribe(function (e, args) {
+            var target = $(e.target);
+
+            // get the service at this row
+            var parameterProviderEntity = parameterProvidersData.getItem(args.row);
+
+            // determine the desired action
+            if (parameterProvidersGrid.getColumns()[args.cell].id === 'actions') {
+                if (target.hasClass('edit-parameter-provider')) {
+                    nfParameterProvider.showConfiguration(parameterProviderEntity);
+                } else if (target.hasClass('fetch-parameter-provider')) {
+                    nfParameterProvider.showFetchDialog(parameterProviderEntity);
+                } else if (target.hasClass('delete-parameter-provider')) {
+                    nfParameterProvider.promptToDeleteParameterProvider(parameterProviderEntity);
+                } else if (target.hasClass('edit-access-policies')) {
+                    // show the policies for this service
+                    nfPolicyManagement.showParameterProviderPolicy(parameterProviderEntity);
+
+                    // close the settings dialog
+                    $('#shell-close-button').click();
+                }
+            } else if (parameterProvidersGrid.getColumns()[args.cell].id === 'moreDetails') {
+                if (target.hasClass('view-parameter-provider')) {
+                    nfParameterProvider.showDetails(parameterProviderEntity);
+                } else if (target.hasClass('parameter-provider-usage')) {
+                    // close the settings dialog
+                    $('#shell-close-button').click();
+
+                    // open the documentation for this parameter provider
+                    nfShell.showPage('../nifi-docs/documentation?' + $.param({
+                        select: parameterProviderEntity.component.type,
+                        group: parameterProviderEntity.component.bundle.group,
+                        artifact: parameterProviderEntity.component.bundle.artifact,
+                        version: parameterProviderEntity.component.bundle.version
+                    })).done(function () {
+                        nfSettings.showSettings();
+                    });
+                }
+            }
+        });
+
+        // wire up the dataview to the grid
+        parameterProvidersData.onRowCountChanged.subscribe(function (e, args) {
+            parameterProvidersGrid.updateRowCount();
+            parameterProvidersGrid.render();
+        });
+        parameterProvidersData.onRowsChanged.subscribe(function (e, args) {
+            parameterProvidersGrid.invalidateRows(args.rows);
+            parameterProvidersGrid.render();
+        });
+        parameterProvidersData.syncGridSelection(parameterProvidersGrid, true);
+
+        // hold onto an instance of the grid
+        $('#parameter-providers-table').data('gridInstance', parameterProvidersGrid).on('mouseenter', 'div.slick-cell', function (e) {
+            var errorIcon = $(this).find('div.has-errors');
+            if (errorIcon.length && !errorIcon.data('qtip')) {
+                var providerId = $(this).find('span.row-id').text();
+
+                // get the provider item
+                var providerEntity = parameterProvidersData.getItemById(providerId);
+
+                // format the errors
+                var tooltip = nfCommon.formatUnorderedList(providerEntity.component.validationErrors);
+
+                // show the tooltip
+                if (nfCommon.isDefinedAndNotNull(tooltip)) {
+                    errorIcon.qtip($.extend({},
+                        nfCommon.config.tooltipConfig,
+                        {
+                            content: tooltip,
+                            position: {
+                                target: 'mouse',
+                                viewport: $('#shell-container'),
+                                adjust: {
+                                    x: 8,
+                                    y: 8,
+                                    method: 'flipinvert flipinvert'
+                                }
+                            }
+                        }));
+                }
+            }
+
+            var bulletinIcon = $(this).find('div.has-bulletins');
+            if (bulletinIcon.length && !bulletinIcon.data('qtip')) {
+                var providerId = $(this).find('span.row-id').text();
+
+                // get the provider item
+                var parameterProviderEntity = parameterProvidersData.getItemById(providerId);
+
+                // format the tooltip
+                var bulletins = nfCommon.getFormattedBulletins(parameterProviderEntity.bulletins);
+                var tooltip = nfCommon.formatUnorderedList(bulletins);
+
+                // show the tooltip
+                if (nfCommon.isDefinedAndNotNull(tooltip)) {
+                    bulletinIcon.qtip($.extend({},
+                        nfCommon.config.tooltipConfig,
+                        {
+                            content: tooltip,
+                            position: {
+                                target: 'mouse',
+                                viewport: $('#shell-container'),
+                                adjust: {
+                                    x: 8,
+                                    y: 8,
+                                    method: 'flipinvert flipinvert'
+                                }
+                            }
+                        }));
+                }
+            }
+        });
+    };
+
     /**
      * Edits the specified registry entity.
      *
@@ -1608,8 +2432,11 @@
         // load the registries
         var registries = loadRegistries();
 
+        // load the parameter providers
+        var parameterProviders = loadParameterProviders();
+
         // return a deferred for all parts of the settings
-        return $.when(settings, controllerServicesXhr, reportingTasks).done(function (settingsResult, controllerServicesResult) {
+        return $.when(settings, controllerServicesXhr, reportingTasks, parameterProviders).done(function (settingsResult, controllerServicesResult) {
             var controllerServicesResponse = controllerServicesResult[0];
 
             // update the current time
@@ -1674,6 +2501,37 @@
         });
     };
 
+    /**
+     * Loads the parameter providers.
+     */
+    var loadParameterProviders = function () {
+        return $.ajax({
+            type: 'GET',
+            url: config.urls.parameterProviders,
+            dataType: 'json'
+        }).done(function (response) {
+            var providers = [];
+            $.each(response.parameterProviders, function (_, provider) {
+                providers.push($.extend({
+                    type: 'ParameterProvider',
+                    bulletins: []
+                }, provider));
+            });
+
+            var parameterProvidersElement = $('#parameter-providers-table');
+            nfCommon.cleanUpTooltips(parameterProvidersElement, 'div.has-errors');
+            nfCommon.cleanUpTooltips(parameterProvidersElement, 'div.has-bulletins');
+
+            var parameterProvidersGrid = parameterProvidersElement.data('gridInstance');
+            var parameterProvidersData = parameterProvidersGrid.getData();
+
+            // update the parameter providers
+            parameterProvidersData.setItems(providers);
+            parameterProvidersData.reSort();
+            parameterProvidersGrid.invalidate();
+        });
+    };
+
     /**
      * Shows the process group configuration.
      */
@@ -1712,7 +2570,7 @@
                     name: 'General',
                     tabContentId: 'general-settings-tab-content'
                 }, {
-                    name: 'Reporting Task Controller Services',
+                    name: 'Management Controller Services',
                     tabContentId: 'controller-services-tab-content'
                 }, {
                     name: 'Reporting Tasks',
@@ -1720,6 +2578,9 @@
                 }, {
                     name: 'Registry Clients',
                     tabContentId: 'registries-tab-content'
+                }, {
+                    name: 'Parameter Providers',
+                    tabContentId: 'parameter-providers-tab-content'
                 }],
                 select: function () {
                     var tab = $(this).text();
@@ -1740,15 +2601,18 @@
 
                             // update the tooltip on the button
                             $('#new-service-or-task').attr('title', function () {
-                                if (tab === 'Reporting Task Controller Services') {
+                                if (tab === 'Management Controller Services') {
                                     $('#settings-save').hide();
-                                    return 'Create a new reporting task controller service';
+                                    return 'Create a new controller level controller service';
                                 } else if (tab === 'Reporting Tasks') {
                                     $('#settings-save').hide();
                                     return 'Create a new reporting task';
                                 } else if (tab === 'Registry Clients') {
                                     $('#settings-save').hide();
                                     return 'Register a new registry client';
+                                } else if (tab === 'Parameter Providers') {
+                                    $('#settings-save').hide();
+                                    return 'Add a new parameter provider';
                                 }
                             });
                         } else {
@@ -1756,9 +2620,9 @@
                             $('div.controller-settings-table').css('top', '0');
                         }
 
-                        if (tab === 'Reporting Task Controller Services') {
+                        if (tab === 'Management Controller Services') {
                             $('#controller-cs-availability').show();
-                        } else if (tab === 'Reporting Tasks' || tab === 'Registry Clients') {
+                        } else if (tab === 'Reporting Tasks' || tab === 'Registry Clients' || tab === 'Parameter Providers') {
                             $('#controller-cs-availability').hide();
                         }
 
@@ -1776,7 +2640,7 @@
             // create a new controller service or reporting task
             $('#new-service-or-task').on('click', function () {
                 var selectedTab = $('#settings-tabs li.selected-tab').text();
-                if (selectedTab === 'Reporting Task Controller Services') {
+                if (selectedTab === 'Management Controller Services') {
                     var controllerServicesUri = config.urls.api + '/controller/controller-services';
                     nfControllerServices.promptNewControllerService(controllerServicesUri, getControllerServicesTable());
                 } else if (selectedTab === 'Reporting Tasks') {
@@ -1826,6 +2690,24 @@
 
                     // set the initial focus
                     $('#registry-name').focus();
+                } else if (selectedTab === 'Parameter Providers') {
+                    $('#new-parameter-provider-dialog').modal('show');
+
+                    var parameterProviderTypesGrid = $('#parameter-provider-types-table').data('gridInstance');
+                    if (nfCommon.isDefinedAndNotNull(parameterProviderTypesGrid)) {
+                        var parameterProviderTypesData = parameterProviderTypesGrid.getData();
+
+                        // reset the canvas size after the dialog is shown
+                        parameterProviderTypesGrid.resizeCanvas();
+
+                        // select the first row if possible
+                        if (parameterProviderTypesData.getLength() > 0) {
+                            nfFilteredDialogCommon.choseFirstRow(parameterProviderTypesGrid);
+                        }
+                    }
+
+                    // set the initial focus
+                    $('#parameter-provider-type-filter').focus();
                 }
             });
 
@@ -1834,6 +2716,7 @@
             nfControllerServices.init(getControllerServicesTable(), nfSettings.showSettings);
             initReportingTasks();
             initRegistriesTable();
+            initParameterProvidersTable();
         },
 
         /**
@@ -1846,6 +2729,10 @@
             if (nfCommon.isDefinedAndNotNull(reportingTasksGrid)) {
                 reportingTasksGrid.resizeCanvas();
             }
+            var parameterProvidersGrid = $('#parameter-providers-table').data('gridInstance');
+            if (nfCommon.isDefinedAndNotNull(parameterProvidersGrid)) {
+                parameterProvidersGrid.resizeCanvas();
+            }
         },
 
         /**
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
index fc2bcaaab5..49a3b1a7bb 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-processor-details.js
@@ -183,7 +183,7 @@
 
             //apply the status bar if indicated
             if(config.supportsStatusBar){
-                $("#processor-details-status-bar").statusbar();
+                $("#processor-details-status-bar").statusbar('processor');
             }
 
             // initialize the properties
@@ -372,9 +372,17 @@
 
                 //Populate the status bar if the feature is enabled
                 if (config.supportsStatusBar && nfCommon.isDefinedAndNotNull(config.nfCanvasUtils)){
+                    var formattedBulletins = nfCommon.getFormattedBulletins(processorResponse.bulletins);
+                    var unorderedBulletins = nfCommon.formatUnorderedList(formattedBulletins);
 
                     //initialize the canvas synchronization
-                    $("#processor-details-status-bar").statusbar('observe',processor.id);
+                    $("#processor-details-status-bar").statusbar(
+                        'observe',
+                        {
+                            processor: processor.id,
+                            bulletins: unorderedBulletins
+                        }
+                    );
 
                     //Fetch the component as a selection from the canvas
                     selection = config.nfCanvasUtils.getSelectionById(processor.id);