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 2015/03/20 15:51:34 UTC

[2/2] incubator-nifi git commit: NIFI-250: - Updating the way in which UI extensions interact with the underlying NiFi instance.

NIFI-250:
- Updating the way in which UI extensions interact with the underlying NiFi instance.

Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/24d787e5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/24d787e5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/24d787e5

Branch: refs/heads/NIFI-250
Commit: 24d787e5d40949659427e72d126dd2a225b6ff86
Parents: ffa919a
Author: Matt Gilman <ma...@gmail.com>
Authored: Fri Mar 20 10:51:15 2015 -0400
Committer: Matt Gilman <ma...@gmail.com>
Committed: Fri Mar 20 10:51:15 2015 -0400

----------------------------------------------------------------------
 .../apache/nifi/web/ComponentConfiguration.java | 122 ------
 .../org/apache/nifi/web/ComponentDetails.java   | 122 ++++++
 .../apache/nifi/web/ConfigurationAction.java    |  51 +++
 .../apache/nifi/web/ConfigurationContext.java   |  96 +++++
 .../nifi/web/ConfigurationRequestContext.java   |  29 ++
 .../org/apache/nifi/web/RequestContext.java     |  58 +++
 .../nifi/web/UiExtensionRequestContext.java     |  95 -----
 .../org/apache/nifi/web/UiExtensionType.java    |  26 ++
 .../nifi/web/api/dto/ControllerServiceDTO.java  |  15 +
 .../nifi/web/api/dto/ProcessorConfigDTO.java    |   3 +-
 .../nifi/web/api/dto/ReportingTaskDTO.java      |  15 +
 .../org/apache/nifi/web/server/JettyServer.java | 147 ++++++-
 .../extension/ConfigureComponentController.java |   9 +-
 .../apache/nifi/ui/extension/UiExtension.java   |  10 +-
 .../nifi/ui/extension/UiExtensionMapping.java   |   7 +-
 .../nifi/ui/extension/UiExtensionType.java      |  26 --
 .../controller/UiExtensionControllerFacade.java | 101 -----
 .../UiExtensionControllerRequest.java           |  64 ---
 .../nifi/web/StandardConfigurationContext.java  | 417 +++++++++++++++++++
 .../nifi/web/api/ControllerServiceResource.java |  19 +
 .../apache/nifi/web/api/ProcessorResource.java  |  15 +
 .../nifi/web/api/ReportingTaskResource.java     |  21 +-
 .../StandardUiExtensionControllerFacade.java    | 377 -----------------
 .../src/main/resources/nifi-web-api-context.xml |   2 +-
 .../nifi/integration/util/NiFiTestServer.java   |   5 +
 .../nifi-framework/nifi-web/pom.xml             |   1 -
 26 files changed, 1041 insertions(+), 812 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java
deleted file mode 100644
index bb47760..0000000
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentConfiguration.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- *
- */
-public class ComponentConfiguration {
-
-    private final String id;
-    private final String name;
-    private final String type;
-    private final String state;
-    private final String annotationData;
-    private final Map<String, String> properties;
-    private final Collection<String> validationErrors;
-
-    private ComponentConfiguration(final Builder builder) {
-        this.id = builder.id;
-        this.name = builder.name;
-        this.type = builder.type;
-        this.state = builder.state;
-        this.annotationData = builder.annotationData;
-        this.properties = builder.properties;
-        this.validationErrors = builder.validationErrors;
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getType() {
-        return type;
-    }
-    
-    public String getState() {
-        return state;
-    }
-
-    public String getAnnotationData() {
-        return annotationData;
-    }
-
-    public Map<String, String> getProperties() {
-        return properties;
-    }
-
-    public Collection<String> getValidationErrors() {
-        return validationErrors;
-    }
-
-    public static final class Builder {
-
-        private String id;
-        private String name;
-        private String type;
-        private String state;
-        private String annotationData;
-        private Map<String, String> properties;
-        private Collection<String> validationErrors;
-
-        public Builder id(final String id) {
-            this.id = id;
-            return this;
-        }
-
-        public Builder name(final String name) {
-            this.name = name;
-            return this;
-        }
-        
-        public Builder type(final String type) {
-            this.type = type;
-            return this;
-        }
-
-        public Builder state(final String state) {
-            this.state = state;
-            return this;
-        }
-
-        public Builder annotationData(final String annotationData) {
-            this.annotationData = annotationData;
-            return this;
-        }
-
-        public Builder properties(final Map<String, String> properties) {
-            this.properties = properties;
-            return this;
-        }
-
-        public Builder validateErrors(final Collection<String> validationErrors) {
-            this.validationErrors = validationErrors;
-            return this;
-        }
-
-        public ComponentConfiguration build() {
-            return new ComponentConfiguration(this);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
new file mode 100644
index 0000000..af5f5bd
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ComponentDetails.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ *
+ */
+public class ComponentDetails {
+
+    private final String id;
+    private final String name;
+    private final String type;
+    private final String state;
+    private final String annotationData;
+    private final Map<String, String> properties;
+    private final Collection<String> validationErrors;
+
+    private ComponentDetails(final Builder builder) {
+        this.id = builder.id;
+        this.name = builder.name;
+        this.type = builder.type;
+        this.state = builder.state;
+        this.annotationData = builder.annotationData;
+        this.properties = builder.properties;
+        this.validationErrors = builder.validationErrors;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getType() {
+        return type;
+    }
+    
+    public String getState() {
+        return state;
+    }
+
+    public String getAnnotationData() {
+        return annotationData;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public Collection<String> getValidationErrors() {
+        return validationErrors;
+    }
+
+    public static final class Builder {
+
+        private String id;
+        private String name;
+        private String type;
+        private String state;
+        private String annotationData;
+        private Map<String, String> properties;
+        private Collection<String> validationErrors;
+
+        public Builder id(final String id) {
+            this.id = id;
+            return this;
+        }
+
+        public Builder name(final String name) {
+            this.name = name;
+            return this;
+        }
+        
+        public Builder type(final String type) {
+            this.type = type;
+            return this;
+        }
+
+        public Builder state(final String state) {
+            this.state = state;
+            return this;
+        }
+
+        public Builder annotationData(final String annotationData) {
+            this.annotationData = annotationData;
+            return this;
+        }
+
+        public Builder properties(final Map<String, String> properties) {
+            this.properties = properties;
+            return this;
+        }
+
+        public Builder validateErrors(final Collection<String> validationErrors) {
+            this.validationErrors = validationErrors;
+            return this;
+        }
+
+        public ComponentDetails build() {
+            return new ComponentDetails(this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
index d46d489..ca3af44 100644
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationAction.java
@@ -21,17 +21,50 @@ package org.apache.nifi.web;
  */
 public class ConfigurationAction {
 
+    private final String id;
+    private final String name;
+    private final String type;
     private final String field;
     private final String previousValue;
     private final String value;
 
     private ConfigurationAction(final Builder builder) {
+        this.id = builder.id;
+        this.name = builder.name;
+        this.type = builder.type;
         this.field = builder.field;
         this.previousValue = builder.previousValue;
         this.value = builder.value;
     }
 
     /**
+     * The id of the component being modified.
+     * 
+     * @return 
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * The name of the component being modified.
+     * 
+     * @return 
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * The type of the component being modified.
+     * 
+     * @return 
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
      * Gets the name of the field, property, etc that has been modified.
      *
      * @return
@@ -60,10 +93,28 @@ public class ConfigurationAction {
 
     public static class Builder {
 
+        private String id;
+        private String name;
+        private String type;
         private String field;
         private String previousValue;
         private String value;
 
+        public Builder id(final String id) {
+            this.id = id;
+            return this;
+        }
+        
+        public Builder name(final String name) {
+            this.name = name;
+            return this;
+        }
+        
+        public Builder type(final String type) {
+            this.type = type;
+            return this;
+        }
+        
         public Builder field(final String field) {
             this.field = field;
             return this;

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationContext.java
new file mode 100644
index 0000000..422b262
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationContext.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+import java.util.Collection;
+
+import org.apache.nifi.controller.ControllerService;
+
+/**
+ * NiFi web context providing limited access to dataflow configuration for
+ * component custom UIs.
+ */
+public interface ConfigurationContext {
+    
+    /**
+     * Gets the ControllerService for the specified identifier. If a
+     * corresponding service cannot be found, null is returned. If this NiFi is
+     * clustered, the only services available will be those those
+     * availability is NCM only.
+     *
+     * @param serviceIdentifier
+     * @return
+     */
+    ControllerService getControllerService(String serviceIdentifier);
+
+    /**
+     * Provides a mechanism for custom UIs to save actions to appear in NiFi
+     * configuration history. Note all fields within each Action must be
+     * populated. Null values will result in a failure to insert the audit
+     * record. Since the saving to these actions is separate from the actual
+     * configuration change, a failure to insert here will just generate a
+     * warning log message. The recording of these actions typically happens
+     * after a configuration change is applied. Since those changes have already
+     * been applied to the flow, we cannot revert them because of a failure to
+     * insert an audit record.
+     *
+     * @param requestContext
+     * @param actions
+     */
+    void saveActions(RequestContext requestContext, Collection<ConfigurationAction> actions);
+
+    /**
+     * Gets the current user dn. Returns null if no user is found.
+     *
+     * @return
+     */
+    String getCurrentUserDn();
+
+    /**
+     * Gets the current user name. Returns null if no user is found.
+     *
+     * @return
+     */
+    String getCurrentUserName();
+
+    /**
+     * Sets the annotation data for the underlying component.
+     * 
+     * @param configurationContext
+     * @param annotationData
+     * @return the configuration for the underlying component
+     * @throws ResourceNotFoundException if the underlying component does not exit
+     * @throws InvalidRevisionException if a revision other than the current
+     * revision is given
+     * @throws ClusterRequestException if the annotation data was unable to be
+     * set for the underlying component. This exception will only be thrown when operating
+     * in a cluster.
+     */
+    ComponentDetails setAnnotationData(ConfigurationRequestContext configurationContext, String annotationData) throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException;
+    
+    /**
+     * Gets the details for the underlying component (including configuration, validation errors, and annotation data).
+     * 
+     * @param requestContext
+     * @return the configuration for the underlying component
+     * @throws ResourceNotFoundException if the underlying component does not exit
+     * @throws ClusterRequestException if the underlying component was unable to be
+     * retrieved from the cluster. This exception will only be thrown when
+     * operating in a cluster.
+     */
+    ComponentDetails getComponentDetails(RequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationRequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationRequestContext.java
new file mode 100644
index 0000000..701e25e
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/ConfigurationRequestContext.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+/**
+ * Context around about a configuration request.
+ */
+public interface ConfigurationRequestContext extends RequestContext {
+
+    /**
+     * @return the revision
+     */
+    Revision getRevision();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/RequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/RequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/RequestContext.java
new file mode 100644
index 0000000..1bfe16a
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/RequestContext.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+/**
+ * Contextual details required to make a request from a UI extension.
+ */
+public interface RequestContext {
+
+    /**
+     * Returns the type of UI extension is making the request.
+     * 
+     * @return 
+     */
+    UiExtensionType getExtensionType();
+    
+    /**
+     * The request protocol scheme (http or https). When scheme is https, the
+     * X509Certificate can be used for subsequent remote requests.
+     *
+     * @return the protocol scheme
+     */
+    String getScheme();
+
+    /**
+     * The id of the component.
+     * 
+     * @return the ID
+     */
+    String getId();
+
+    /**
+     * Returns the proxied entities chain. The format of the chain is as
+     * follows:
+     *
+     * <code>
+     * &lt;CN=original-proxied-entity&gt;&lt;CN=first-proxy&gt;&lt;CN=second-proxy&gt;...
+     * </code>
+     *
+     * @return the proxied entities chain or null if no chain
+     */
+    String getProxiedEntitiesChain();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java
deleted file mode 100644
index 24c4226..0000000
--- a/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionRequestContext.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web;
-
-import java.util.Collection;
-
-import org.apache.nifi.controller.ControllerService;
-
-/**
- * NiFi web context providing limited access to dataflow configuration for
- * processor custom UIs.
- */
-public interface UiExtensionRequestContext {
-    
-    public static final String ATTRIBUTE_KEY = "ui-extension-request-context";
-
-    /**
-     * Gets the ControllerService for the specified identifier. If a
-     * corresponding service cannot be found, null is returned. If this NiFi is
-     * clustered, the only services available will be those those
-     * availability is NCM only.
-     *
-     * @param serviceIdentifier
-     * @return
-     */
-    ControllerService getControllerService(String serviceIdentifier);
-
-    /**
-     * Provides a mechanism for custom UIs to save actions to appear in NiFi
-     * configuration history. Note all fields within each Action must be
-     * populated. Null values will result in a failure to insert the audit
-     * record. Since the saving to these actions is separate from the actual
-     * configuration change, a failure to insert here will just generate a
-     * warning log message. The recording of these actions typically happens
-     * after a configuration change is applied. Since those changes have already
-     * been applied to the flow, we cannot revert them because of a failure to
-     * insert an audit record.
-     *
-     * @param actions
-     */
-    void saveActions(Collection<ConfigurationAction> actions);
-
-    /**
-     * Gets the current user dn. Returns null if no user is found.
-     *
-     * @return
-     */
-    String getCurrentUserDn();
-
-    /**
-     * Gets the current user name. Returns null if no user is found.
-     *
-     * @return
-     */
-    String getCurrentUserName();
-
-    /**
-     * Sets the annotation data for the underlying component.
-     * 
-     * @param annotationData
-     * @return the configuration for the underlying component
-     * @throws ResourceNotFoundException if the underlying component does not exit
-     * @throws InvalidRevisionException if a revision other than the current
-     * revision is given
-     * @throws ClusterRequestException if the annotation data was unable to be
-     * set for the underlying component. This exception will only be thrown when operating
-     * in a cluster.
-     */
-    ComponentConfiguration setAnnotationData(String annotationData) throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException;
-    
-    /**
-     * Gets the configuration for the underlying component (including annotation data).
-     * 
-     * @return the configuration for the underlying component
-     * @throws ResourceNotFoundException if the underlying component does not exit
-     * @throws ClusterRequestException if the underlying component was unable to be
-     * retrieved from the cluster. This exception will only be thrown when
-     * operating in a cluster.
-     */
-    ComponentConfiguration getComponentDetails() throws ResourceNotFoundException, ClusterRequestException;
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
new file mode 100644
index 0000000..e25d8ca
--- /dev/null
+++ b/nifi/nifi-api/src/main/java/org/apache/nifi/web/UiExtensionType.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+/**
+ * Types of UI extensions.
+ */
+public enum UiExtensionType {
+    ProcessorConfiguration,
+    ControllerServiceConfiguration,
+    ReportingTaskConfiguration
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
index 76f1f62..75d18a2 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ControllerServiceDTO.java
@@ -36,6 +36,7 @@ public class ControllerServiceDTO extends NiFiComponentDTO {
     private Map<String, String> properties;
     private Map<String, PropertyDescriptorDTO> descriptors;
  
+    private String customUiUrl;
     private String annotationData;
     
     private Set<ControllerServiceReferencingComponentDTO> referencingComponents;
@@ -133,6 +134,20 @@ public class ControllerServiceDTO extends NiFiComponentDTO {
     }
 
     /**
+     * Returns the URL for this controller services custom configuration UI
+     * if applicable. Null otherwise.
+     *
+     * @return
+     */
+    public String getCustomUiUrl() {
+        return customUiUrl;
+    }
+
+    public void setCustomUiUrl(String customUiUrl) {
+        this.customUiUrl = customUiUrl;
+    }
+
+    /**
      * The annotation data for this controller service.
      * 
      * @return 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
index d1a44b8..63ed005 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ProcessorConfigDTO.java
@@ -207,7 +207,8 @@ public class ProcessorConfigDTO {
     }
 
     /**
-     * Whether of not this processor has a custom UI.
+     * Returns the URL for this processors custom configuration UI
+     * if applicable. Null otherwise.
      *
      * @return
      */

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
index b9e7ff1..7cb24ca 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ReportingTaskDTO.java
@@ -39,6 +39,7 @@ public class ReportingTaskDTO extends NiFiComponentDTO {
 	private Map<String, String> properties;
     private Map<String, PropertyDescriptorDTO> descriptors;
  
+    private String customUiUrl;
     private String annotationData;
     
     private Collection<String> validationErrors;
@@ -157,6 +158,20 @@ public class ReportingTaskDTO extends NiFiComponentDTO {
 		this.descriptors = descriptors;
 	}
 
+    /**
+     * Returns the URL for this reporting task custom configuration UI
+     * if applicable. Null otherwise.
+     *
+     * @return
+     */
+    public String getCustomUiUrl() {
+        return customUiUrl;
+    }
+
+    public void setCustomUiUrl(String customUiUrl) {
+        this.customUiUrl = customUiUrl;
+    }
+
 	/**
 	 * The currently configured annotation data for the reporting task
 	 * @return

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 95052a7..60345ae 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -52,6 +52,10 @@ import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.NiFiWebContext;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.ConfigurationContext;
+import org.apache.nifi.web.UiExtensionType;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.HttpConfiguration;
@@ -99,9 +103,14 @@ public class JettyServer implements NiFiServer {
     private ExtensionMapping extensionMapping;
     private WebAppContext webApiContext;
     private WebAppContext webDocsContext;
-    private Collection<WebAppContext> customUiWebContexts;
     private final NiFiProperties props;
 
+    private UiExtensionMapping uiExtensions;
+    private Collection<WebAppContext> uiExtensionWebContexts;
+    
+    @Deprecated
+    private Collection<WebAppContext> customUiWebContexts;
+    
     /**
      * Creates and configures a new Jetty instance.
      *
@@ -192,19 +201,32 @@ public class JettyServer implements NiFiServer {
 
         // handlers for each war and init params for the web api
         final HandlerCollection handlers = new HandlerCollection();
-        final Map<String, String> initParams = new HashMap<>();
         final ClassLoader frameworkClassLoader = getClass().getClassLoader();
         final ClassLoader jettyClassLoader = frameworkClassLoader.getParent();
 
+        @Deprecated
+        final Map<String, String> initParams = new HashMap<>();
+        
         // deploy the other wars
         if (CollectionUtils.isNotEmpty(otherWars)) {
+            // hold onto to the web contexts for all ui extensions
             customUiWebContexts = new ArrayList<>();
+            uiExtensionWebContexts = new ArrayList<>();
+            
+            // ui extension organized by component type
+            final Map<String, List<UiExtension>> uiExtensionsByType = new HashMap<>();
             for (File war : otherWars) {
                 // see if this war is a custom processor ui
+                @Deprecated
                 List<String> customUiProcessorTypes = getCustomUiProcessorTypes(war);
 
+                // identify all known extension types in the war
+                final Map<UiExtensionType, List<String>> uiExtensionInWar = new HashMap<>();
+                identifyUiExtensions(uiExtensionInWar, war);
+                
                 // only include wars that are for custom processor ui's
-                if (CollectionUtils.isNotEmpty(customUiProcessorTypes)) {
+                if (CollectionUtils.isNotEmpty(customUiProcessorTypes) || !uiExtensionInWar.isEmpty()) {
+                    // get the context path
                     String warName = StringUtils.substringBeforeLast(war.getName(), ".");
                     String warContextPath = String.format("/%s", warName);
 
@@ -220,18 +242,53 @@ public class JettyServer implements NiFiServer {
                     WebAppContext customUiContext = loadWar(war, warContextPath, narClassLoaderForWar);
 
                     // hold on to a reference to all custom ui web app contexts
-                    customUiWebContexts.add(customUiContext);
-
-                    // include custom ui web context in the handlers
-                    handlers.addHandler(customUiContext);
+                    if (CollectionUtils.isNotEmpty(customUiProcessorTypes)) {
+                        customUiWebContexts.add(customUiContext);
+                    } else {
+                        uiExtensionWebContexts.add(customUiContext);
+                    }
 
+                    // create the ui extensions
+                    for (final Map.Entry<UiExtensionType, List<String>> entry : uiExtensionInWar.entrySet()) {
+                        final UiExtensionType extensionType = entry.getKey();
+                        final List<String> componentTypes = entry.getValue();
+
+                        // consider each type identified
+                        for (final String componentType : componentTypes) {
+                            logger.info(String.format("Loading UI extension [%s, %s] for %s", extensionType, warContextPath, componentTypes));
+                            
+                            // record the extension definition
+                            final UiExtension uiExtension = new UiExtension(extensionType, warContextPath);
+                            
+                            // create if this is the first extension for this component type
+                            List<UiExtension> uiExtensionsForType = uiExtensionsByType.get(componentType);
+                            if (uiExtensionsForType == null) {
+                                uiExtensionsForType = new ArrayList<>();
+                                uiExtensionsByType.put(componentType, uiExtensionsForType);
+                            }
+                            
+                            // record this extension
+                            uiExtensionsForType.add(uiExtension);
+                        }
+                    }
+                    
                     // add the initialization paramters
+                    // @Deprecated
                     for (String customUiProcessorType : customUiProcessorTypes) {
                         // map the processor type to the custom ui path
                         initParams.put(customUiProcessorType, warContextPath);
                     }
+                    
+                    // include custom ui web context in the handlers
+                    handlers.addHandler(customUiContext);
                 }
+                
             }
+            
+            // record all ui extensions
+            uiExtensions = new UiExtensionMapping(uiExtensionsByType);
+        } else {
+            uiExtensions = new UiExtensionMapping(Collections.EMPTY_MAP);
         }
 
         // load the web ui app
@@ -291,12 +348,57 @@ public class JettyServer implements NiFiServer {
         return wars;
     }
 
+    private void readUiExtensions(final Map<UiExtensionType, List<String>> uiExtensions, final UiExtensionType uiExtensionType, final JarFile jarFile, final JarEntry jarEntry) throws IOException {
+        if (jarEntry == null) {
+            return;
+        }
+        
+        // get an input stream for the nifi-processor configuration file
+        BufferedReader in = new BufferedReader(new InputStreamReader(jarFile.getInputStream(jarEntry)));
+
+        // read in each configured type
+        String componentType;
+        while ((componentType = in.readLine()) != null) {
+            // ensure the line isn't blank
+            if (StringUtils.isNotBlank(componentType)) {
+                List<String> extensions = uiExtensions.get(uiExtensionType);
+                
+                // if there are currently no extensions for this type create it
+                if (extensions == null) {
+                    extensions = new ArrayList<>();
+                    uiExtensions.put(uiExtensionType, extensions);
+                }
+                
+                // add the specified type
+                extensions.add(componentType);
+            }
+        }
+    }
+    
+    /**
+     * Identifies all known UI extensions and stores them in the specified map.
+     * 
+     * @param uiExtensions
+     * @param warFile 
+     */
+    private void identifyUiExtensions(final Map<UiExtensionType, List<String>> uiExtensions, final File warFile) {
+        try (final JarFile jarFile = new JarFile(warFile)) {
+            // locate the ui extensions
+            readUiExtensions(uiExtensions, UiExtensionType.ProcessorConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-processor-configuration"));
+            readUiExtensions(uiExtensions, UiExtensionType.ControllerServiceConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-controller-service-configuration"));
+            readUiExtensions(uiExtensions, UiExtensionType.ReportingTaskConfiguration, jarFile, jarFile.getJarEntry("META-INF/nifi-reporting-task-configuration"));
+        } catch (IOException ioe) {
+            logger.warn(String.format("Unable to inspect %s for a UI extensions.", warFile));
+        }
+    }
+    
     /**
      * Loads the processor types that the specified war file is a custom UI for.
      *
      * @param warFile
      * @return
      */
+    @Deprecated
     private List<String> getCustomUiProcessorTypes(final File warFile) {
         List<String> processorTypes = new ArrayList<>();
         JarFile jarFile = null;
@@ -534,18 +636,25 @@ public class JettyServer implements NiFiServer {
                     }
                 }
             }
+            
+            // ensure web api is loaded and inject the ui extensions
+            if (webApiContext != null) {
+                final ServletContext webApiServletContext = webApiContext.getServletHandler().getServletContext();
+                webApiServletContext.setAttribute("nifi-ui-extensions", uiExtensions);
+            }
 
             // ensure the appropriate wars deployed successfully before injecting the NiFi context and security filters - 
             // this must be done after starting the server (and ensuring there were no start up failures)
+            // @Deprecated
             if (webApiContext != null && CollectionUtils.isNotEmpty(customUiWebContexts)) {
                 final ServletContext webApiServletContext = webApiContext.getServletHandler().getServletContext();
                 final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(webApiServletContext);
-                final NiFiWebContext NiFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
+                final NiFiWebContext niFiWebContext = webApplicationContext.getBean("nifiWebContext", NiFiWebContext.class);
 
                 for (final WebAppContext customUiContext : customUiWebContexts) {
                     // set the NiFi context in each custom ui servlet context
                     final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
-                    customUiServletContext.setAttribute("nifi-web-context", NiFiWebContext);
+                    customUiServletContext.setAttribute("nifi-web-context", niFiWebContext);
 
                     // add the security filter to any custom ui wars
                     final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
@@ -554,6 +663,26 @@ public class JettyServer implements NiFiServer {
                     }
                 }
             }
+            
+            // ensure the appropriate wars deployed successfully before injecting the NiFi context and security filters - 
+            // this must be done after starting the server (and ensuring there were no start up failures)
+            if (webApiContext != null && CollectionUtils.isNotEmpty(uiExtensionWebContexts)) {
+                final ServletContext webApiServletContext = webApiContext.getServletHandler().getServletContext();
+                final WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(webApiServletContext);
+                final ConfigurationContext configurationContext = webApplicationContext.getBean("nifiWebConfigurationContext", ConfigurationContext.class);
+                
+                for (final WebAppContext customUiContext : uiExtensionWebContexts) {
+                    // set the NiFi context in each custom ui servlet context
+                    final ServletContext customUiServletContext = customUiContext.getServletHandler().getServletContext();
+                    customUiServletContext.setAttribute("nifi-web-configuration-context", configurationContext);
+                    
+                    // add the security filter to any ui extensions wars
+                    final FilterHolder securityFilter = webApiContext.getServletHandler().getFilter("springSecurityFilterChain");
+                    if (securityFilter != null) {
+                        customUiContext.addFilter(securityFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+                    }
+                }
+            }
 
             // ensure the web document war was loaded and provide the extension mapping
             if (webDocsContext != null) {

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java
index 8fa43db..c876686 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension-controller/src/main/java/org/apache/nifi/ui/extension/ConfigureComponentController.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.ui.extension;
 
+import org.apache.nifi.web.UiExtensionType;
 import org.apache.nifi.web.controller.UiExtensionControllerRequest;
 import org.apache.nifi.web.controller.UiExtensionControllerFacade;
 import java.io.IOException;
@@ -36,7 +37,7 @@ import org.apache.nifi.action.component.details.ExtensionDetails;
 import org.apache.nifi.action.details.ConfigureDetails;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.web.ClusterRequestException;
-import org.apache.nifi.web.ComponentConfiguration;
+import org.apache.nifi.web.ComponentDetails;
 import org.apache.nifi.web.ConfigurationAction;
 import org.apache.nifi.web.InvalidRevisionException;
 import org.apache.nifi.web.ResourceNotFoundException;
@@ -83,7 +84,7 @@ public class ConfigureComponentController extends HttpServlet {
         final UiExtensionControllerRequest config = getRequestContextConig(extensionType, request);
         
         // get the initial component details
-        final ComponentConfiguration details = controllerFacade.getComponentDetails(config);
+        final ComponentDetails details = controllerFacade.getComponentDetails(config);
         
         // lookup a viewer for the content
         final String uiExtensionUri = servletContext.getInitParameter(type);
@@ -153,12 +154,12 @@ public class ConfigureComponentController extends HttpServlet {
             }
 
             @Override
-            public ComponentConfiguration setAnnotationData(String annotationData) throws ClusterRequestException, InvalidRevisionException, ResourceNotFoundException{
+            public ComponentDetails setAnnotationData(String annotationData) throws ClusterRequestException, InvalidRevisionException, ResourceNotFoundException{
                 return controllerFacade.setAnnotationData(config, annotationData);
             }
 
             @Override
-            public ComponentConfiguration getComponentDetails() throws ClusterRequestException, ResourceNotFoundException {
+            public ComponentDetails getComponentDetails() throws ClusterRequestException, ResourceNotFoundException {
                 return controllerFacade.getComponentDetails(config);
             }
         });

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
index 9040896..61efed5 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtension.java
@@ -16,6 +16,8 @@
  */
 package org.apache.nifi.ui.extension;
 
+import org.apache.nifi.web.UiExtensionType;
+
 /**
  * Information about a UI extension required to be invoked.
  */
@@ -23,12 +25,10 @@ public class UiExtension {
 
     private final UiExtensionType extensionType;
     private final String contextPath;
-    private final ClassLoader classloader;
 
-    public UiExtension(final UiExtensionType extensionType, final String contextPath, final ClassLoader classloader) {
+    public UiExtension(final UiExtensionType extensionType, final String contextPath) {
         this.extensionType = extensionType;
         this.contextPath = contextPath;
-        this.classloader = classloader;
     }
 
     public UiExtensionType getExtensionType() {
@@ -39,8 +39,4 @@ public class UiExtension {
         return contextPath;
     }
 
-    public ClassLoader getClassloader() {
-        return classloader;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
index 6431686..e62ef6e 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionMapping.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.ui.extension;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -23,9 +24,9 @@ import java.util.Map;
  */
 public class UiExtensionMapping {
 
-    private final Map<String, UiExtension> uiExtensions;
+    private final Map<String, List<UiExtension>> uiExtensions;
 
-    public UiExtensionMapping(Map<String, UiExtension> uiExtensions) {
+    public UiExtensionMapping(Map<String, List<UiExtension>> uiExtensions) {
         this.uiExtensions = uiExtensions;
     }
 
@@ -33,7 +34,7 @@ public class UiExtensionMapping {
         return uiExtensions.containsKey(type);
     }
     
-    public UiExtension getUiExtension(final String type) {
+    public List<UiExtension> getUiExtension(final String type) {
         return uiExtensions.get(type);
     }
     

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java
deleted file mode 100644
index 1beeea6..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/ui/extension/UiExtensionType.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.ui.extension;
-
-/**
- * Types of UI extensions.
- */
-public enum UiExtensionType {
-    ProcessorConfiguration,
-    ControllerServiceConfiguration,
-    ReportingTaskConfiguration
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java
deleted file mode 100644
index cf53b7b..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerFacade.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.controller;
-
-import java.util.Collection;
-import org.apache.nifi.action.Action;
-
-import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.web.ClusterRequestException;
-import org.apache.nifi.web.ComponentConfiguration;
-import org.apache.nifi.web.InvalidRevisionException;
-import org.apache.nifi.web.ResourceNotFoundException;
-
-/**
- * Provides limited access to the controller for UI extensions.
- */
-public interface UiExtensionControllerFacade {
-
-    /**
-     * Gets the ControllerService for the specified identifier. If a
-     * corresponding service cannot be found, null is returned. If this NiFi is
-     * clustered, the only services available will be those those
-     * availability is NCM only.
-     *
-     * @param serviceIdentifier
-     * @return
-     */
-    ControllerService getControllerService(String serviceIdentifier);
-
-    /**
-     * Provides a mechanism for custom UIs to save actions to appear in NiFi
-     * configuration history. Note all fields within each Action must be
-     * populated. Null values will result in a failure to insert the audit
-     * record. Since the saving to these actions is separate from the actual
-     * configuration change, a failure to insert here will just generate a
-     * warning log message. The recording of these actions typically happens
-     * after a configuration change is applied. Since those changes have already
-     * been applied to the flow, we cannot revert them because of a failure to
-     * insert an audit record.
-     *
-     * @param actions
-     */
-    void saveActions(Collection<Action> actions);
-
-    /**
-     * Gets the current user dn. Returns null if no user is found.
-     *
-     * @return
-     */
-    String getCurrentUserDn();
-
-    /**
-     * Gets the current user name. Returns null if no user is found.
-     *
-     * @return
-     */
-    String getCurrentUserName();
-
-    /**
-     * Sets the annotation data for the component in the specified request.
-     * 
-     * @param configurationRequest
-     * @param annotationData 
-     * @return the configuration for the component specified request
-     * @throws ResourceNotFoundException if the specified component does not exit
-     * @throws InvalidRevisionException if a revision other than the current
-     * revision is given
-     * @throws ClusterRequestException if the annotation data was unable to be
-     * set for the component. This exception will only be thrown when operating
-     * in a cluster.
-     */
-    ComponentConfiguration setAnnotationData(UiExtensionControllerRequest configurationRequest, String annotationData)
-            throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException;
-    
-    /**
-     * Gets the configuration for the underlying component.
-     * 
-     * @param configurationRequest
-     * @return the configuration for the component specified in request
-     * @throws ResourceNotFoundException if the processor does not exit
-     * @throws ClusterRequestException if the component was unable to be
-     * retrieved from the cluster. This exception will only be thrown when
-     * operating in a cluster.
-     */
-    ComponentConfiguration getComponentDetails(UiExtensionControllerRequest configurationRequest)
-            throws ResourceNotFoundException, ClusterRequestException;
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java
deleted file mode 100644
index f3d591f..0000000
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-ui-extension/src/main/java/org/apache/nifi/web/controller/UiExtensionControllerRequest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.web.controller;
-
-import org.apache.nifi.ui.extension.UiExtensionType;
-import org.apache.nifi.web.Revision;
-
-/**
- * A UI extensions request object containing required items to querying the controller.
- */
-public interface UiExtensionControllerRequest {
-
-    /**
-     * The type of UI extension supports.
-     * 
-     * @return 
-     */
-    UiExtensionType getExtensionType();
-    
-    /**
-     * The request protocol scheme (http or https). When scheme is https, the
-     * X509Certificate can be used for subsequent remote requests.
-     *
-     * @return the protocol scheme
-     */
-    String getScheme();
-
-    /**
-     * @return the component ID
-     */
-    String getId();
-
-    /**
-     * @return the revision
-     */
-    Revision getRevision();
-
-    /**
-     * Returns the proxied entities chain. The format of the chain is as
-     * follows:
-     *
-     * <code>
-     * &lt;CN=original-proxied-entity&gt;&lt;CN=first-proxy&gt;&lt;CN=second-proxy&gt;...
-     * </code>
-     *
-     * @return the proxied entities chain or null if no chain
-     */
-    String getProxiedEntitiesChain();
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardConfigurationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardConfigurationContext.java
new file mode 100644
index 0000000..576ab30
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardConfigurationContext.java
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.web;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.Component;
+import org.apache.nifi.action.Operation;
+import org.apache.nifi.action.component.details.ExtensionDetails;
+import org.apache.nifi.action.details.ConfigureDetails;
+import org.apache.nifi.admin.service.AuditService;
+import org.apache.nifi.cluster.manager.NodeResponse;
+import org.apache.nifi.cluster.manager.impl.WebClusterManager;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.web.security.user.NiFiUserDetails;
+import org.apache.nifi.web.security.user.NiFiUserUtils;
+import org.apache.nifi.user.NiFiUser;
+import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
+import org.apache.nifi.web.api.dto.ProcessorDTO;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+import org.apache.nifi.web.api.entity.ProcessorEntity;
+import org.apache.nifi.web.util.WebUtils;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.web.util.ClientResponseUtils;
+
+/**
+ * Implements the NiFiWebContext interface to support a context in both
+ * standalone and clustered environments.
+ */
+public class StandardConfigurationContext implements ConfigurationContext {
+
+    private static final Logger logger = LoggerFactory.getLogger(StandardConfigurationContext.class);
+    public static final String CLIENT_ID_PARAM = "clientId";
+    public static final String REVISION_PARAM = "revision";
+    public static final String VERBOSE_PARAM = "verbose";
+
+    private NiFiProperties properties;
+    private NiFiServiceFacade serviceFacade;
+    private WebClusterManager clusterManager;
+    private ControllerServiceLookup controllerServiceLookup;
+    private AuditService auditService;
+
+    @Override
+    public ControllerService getControllerService(String serviceIdentifier) {
+        return controllerServiceLookup.getControllerService(serviceIdentifier);
+    }
+
+    @Override
+    @PreAuthorize("hasAnyRole('ROLE_DFM')")
+    public void saveActions(final RequestContext requestContext, final Collection<ConfigurationAction> configurationActions) {
+        Objects.requireNonNull(configurationActions, "Actions cannot be null.");
+
+        Component componentType = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentType = Component.Processor;
+                break;
+            case ControllerServiceConfiguration:
+                componentType = Component.ControllerService;
+                break;
+            case ReportingTaskConfiguration:
+                componentType = Component.ReportingTask;
+                break;
+        }
+
+        if (componentType == null) {
+            throw new IllegalArgumentException("UI extension must support Processor, ControllerService, or ReportingTask configuration");
+        }
+        
+        // - when running standalone or cluster ncm - actions from custom UIs are stored locally
+        // - clustered nodes do not serve custom UIs directly to users so they should never be invoking this method
+        final Date now = new Date();
+        final Collection<Action> actions = new HashSet<>(configurationActions.size());
+        for (final ConfigurationAction configurationAction : configurationActions) {
+            final ExtensionDetails extensionDetails = new ExtensionDetails();
+            extensionDetails.setType(configurationAction.getType());
+
+            final ConfigureDetails configureDetails = new ConfigureDetails();
+            configureDetails.setName(configurationAction.getName());
+            configureDetails.setPreviousValue(configurationAction.getPreviousValue());
+            configureDetails.setValue(configurationAction.getValue());
+
+            final Action action = new Action();
+            action.setTimestamp(now);
+            action.setSourceId(configurationAction.getId());
+            action.setSourceName(configurationAction.getName());
+            action.setSourceType(componentType);
+            action.setOperation(Operation.Configure);
+            action.setUserDn(getCurrentUserDn());
+            action.setUserName(getCurrentUserName());
+            action.setComponentDetails(extensionDetails);
+            action.setActionDetails(configureDetails);
+            actions.add(action);
+        }
+
+        if (!actions.isEmpty()) {
+            try {
+                // record the operations
+                auditService.addActions(actions);
+            } catch (Throwable t) {
+                logger.warn("Unable to record actions: " + t.getMessage());
+                if (logger.isDebugEnabled()) {
+                    logger.warn(StringUtils.EMPTY, t);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getCurrentUserDn() {
+        String userDn = NiFiUser.ANONYMOUS_USER_DN;
+
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user != null) {
+            userDn = user.getDn();
+        }
+
+        return userDn;
+    }
+
+    @Override
+    public String getCurrentUserName() {
+        String userName = NiFiUser.ANONYMOUS_USER_DN;
+
+        final NiFiUser user = NiFiUserUtils.getNiFiUser();
+        if (user != null) {
+            userName = user.getUserName();
+        }
+
+        return userName;
+    }
+
+    @Override
+    public ComponentDetails getComponentDetails(final RequestContext requestContext) throws ResourceNotFoundException, ClusterRequestException {
+        final String id = requestContext.getId();
+
+        if (StringUtils.isBlank(id)) {
+            throw new ResourceNotFoundException(String.format("Context config did not have a component ID."));
+        }
+
+        // ensure the path could be 
+        if (requestContext.getExtensionType() == null) {
+            throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '")));
+        }
+        
+        // get the component facade for interacting directly with that type of object
+        ComponentFacade componentFacade = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentFacade = new ProcessorFacade();
+                break;
+            case ControllerServiceConfiguration: 
+                break;
+            case ReportingTaskConfiguration:
+                break;
+        }
+        
+        if (componentFacade == null) {
+            throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '")));
+        }
+        
+        return componentFacade.getComponentDetails(requestContext);
+    }
+
+    @Override
+    @PreAuthorize("hasAnyRole('ROLE_DFM')")
+    public ComponentDetails setAnnotationData(final ConfigurationRequestContext requestContext, final String annotationData)
+            throws ResourceNotFoundException, InvalidRevisionException, ClusterRequestException {
+
+        final String id = requestContext.getId();
+
+        if (StringUtils.isBlank(id)) {
+            throw new ResourceNotFoundException(String.format("Context config did not have a component ID."));
+        }
+
+        // get the component facade for interacting directly with that type of object
+        ComponentFacade componentFacade = null;
+        switch (requestContext.getExtensionType()) {
+            case ProcessorConfiguration:
+                componentFacade = new ProcessorFacade();
+                break;
+            case ControllerServiceConfiguration: 
+                break;
+            case ReportingTaskConfiguration:
+                break;
+        }
+        
+        if (componentFacade == null) {
+            throw new ResourceNotFoundException(String.format("The type must be one of ['%s']", StringUtils.join(UiExtensionType.values(), "', '")));
+        }
+        
+        return componentFacade.setAnnotationData(requestContext, annotationData);
+    }
+
+    private interface ComponentFacade {
+        ComponentDetails getComponentDetails(RequestContext requestContext);
+        ComponentDetails setAnnotationData(ConfigurationRequestContext requestContext, String annotationData);
+    }
+    
+    private class ProcessorFacade implements ComponentFacade {
+        @Override
+        public ComponentDetails getComponentDetails(final RequestContext requestContext) {
+            final String id = requestContext.getId();
+            
+            final ProcessorDTO processor;
+            if (properties.isClusterManager()) {
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // set the request parameters
+                MultivaluedMap<String, String> parameters = new MultivaluedMapImpl();
+                parameters.add(VERBOSE_PARAM, "true");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.GET, requestUrl, parameters, getHeaders(requestContext));
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+
+                // return processor
+                final ProcessorEntity entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+                processor = entity.getProcessor();
+            } else {
+                processor = serviceFacade.getProcessor(id);
+            }
+
+            // return the processor info
+            return getComponentConfiguration(processor);
+        }
+
+        @Override
+        public ComponentDetails setAnnotationData(final ConfigurationRequestContext requestContext, final String annotationData) {
+            final Revision revision = requestContext.getRevision();
+            final String id = requestContext.getId();
+            
+            final ProcessorDTO processor;
+            if (properties.isClusterManager()) {
+                // create the request URL
+                URI requestUrl;
+                try {
+                    String path = "/nifi-api/cluster/processors/" + URLEncoder.encode(id, "UTF-8");
+                    requestUrl = new URI(requestContext.getScheme(), null, "localhost", 0, path, null, null);
+                } catch (final URISyntaxException | UnsupportedEncodingException use) {
+                    throw new ClusterRequestException(use);
+                }
+
+                // create the revision
+                RevisionDTO revisionDto = new RevisionDTO();
+                revisionDto.setClientId(revision.getClientId());
+                revisionDto.setVersion(revision.getVersion());
+
+                // create the processor entity
+                ProcessorEntity processorEntity = new ProcessorEntity();
+                processorEntity.setRevision(revisionDto);
+
+                // create the processor dto
+                ProcessorDTO processorDto = new ProcessorDTO();
+                processorEntity.setProcessor(processorDto);
+                processorDto.setId(id);
+
+                // create the processor configuration with the given annotation data
+                ProcessorConfigDTO configDto = new ProcessorConfigDTO();
+                processorDto.setConfig(configDto);
+                configDto.setAnnotationData(annotationData);
+
+                // set the content type to json
+                final Map<String, String> headers = getHeaders(requestContext);
+                headers.put("Content-Type", "application/json");
+
+                // replicate request
+                NodeResponse nodeResponse = clusterManager.applyRequest(HttpMethod.PUT, requestUrl, processorEntity, headers);
+
+                // check for issues replicating request
+                checkResponse(nodeResponse, id);
+                
+                // return processor
+                final ProcessorEntity entity = nodeResponse.getClientResponse().getEntity(ProcessorEntity.class);
+                processor = entity.getProcessor();
+            } else {
+                final ConfigurationSnapshot<ProcessorDTO> response = serviceFacade.setProcessorAnnotationData(revision, id, annotationData);
+                processor = response.getConfiguration();
+            }
+            
+            // return the processor info
+            return getComponentConfiguration(processor);
+        }
+        
+        private ComponentDetails getComponentConfiguration(final ProcessorDTO processor) {
+            final ProcessorConfigDTO processorConfig = processor.getConfig();
+            return new ComponentDetails.Builder()
+                    .id(processor.getId())
+                    .name(processor.getName())
+                    .state(processor.getState())
+                    .annotationData(processorConfig.getAnnotationData())
+                    .properties(processorConfig.getProperties())
+                    .validateErrors(processor.getValidationErrors()).build();
+        }
+    }
+    
+    /**
+     * Gets the headers for the request to replicate to each node while
+     * clustered.
+     *
+     * @param config
+     * @return
+     */
+    private Map<String, String> getHeaders(final RequestContext config) {
+        final Map<String, String> headers = new HashMap<>();
+        headers.put("Accept", "application/json,application/xml");
+        if (StringUtils.isNotBlank(config.getProxiedEntitiesChain())) {
+            headers.put("X-ProxiedEntitiesChain", config.getProxiedEntitiesChain());
+        }
+
+        // add the user's authorities (if any) to the headers
+        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication != null) {
+            final Object userDetailsObj = authentication.getPrincipal();
+            if (userDetailsObj instanceof NiFiUserDetails) {
+                // serialize user details object
+                final String hexEncodedUserDetails = WebUtils.serializeObjectToHex((Serializable) userDetailsObj);
+
+                // put serialized user details in header
+                headers.put("X-ProxiedEntityUserDetails", hexEncodedUserDetails);
+            }
+        }
+        return headers;
+    }
+    
+    /**
+     * Checks the specified response and drains the stream appropriately.
+     * 
+     * @param nodeResponse
+     * @param revision
+     * @param id 
+     */
+    private void checkResponse(final NodeResponse nodeResponse, final String id) {
+        if (nodeResponse.hasThrowable()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ClusterRequestException(nodeResponse.getThrowable());
+        } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.CONFLICT.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new InvalidRevisionException(String.format("Conflict: the flow may have been updated by another user."));
+        } else if (nodeResponse.getClientResponse().getStatus() == Response.Status.NOT_FOUND.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ResourceNotFoundException("Unable to find component with id: " + id);
+        } else if (nodeResponse.getClientResponse().getStatus() != Response.Status.OK.getStatusCode()) {
+            ClientResponseUtils.drainClientResponse(nodeResponse.getClientResponse());
+            throw new ClusterRequestException("Method resulted in an unsuccessful HTTP response code: " + nodeResponse.getClientResponse().getStatus());
+        }
+    }
+
+    public void setClusterManager(WebClusterManager clusterManager) {
+        this.clusterManager = clusterManager;
+    }
+
+    public void setProperties(NiFiProperties properties) {
+        this.properties = properties;
+    }
+
+    public void setServiceFacade(NiFiServiceFacade serviceFacade) {
+        this.serviceFacade = serviceFacade;
+    }
+
+    public void setAuditService(AuditService auditService) {
+        this.auditService = auditService;
+    }
+
+    public void setControllerServiceLookup(ControllerServiceLookup controllerServiceLookup) {
+        this.controllerServiceLookup = controllerServiceLookup;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
index a77e376..8ca9a35 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -53,6 +54,9 @@ import org.apache.nifi.web.api.request.LongParameter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.service.ControllerServiceState;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.UiExtensionType;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceReferencingComponentDTO;
 import org.apache.nifi.web.api.entity.ControllerServiceEntity;
@@ -75,6 +79,9 @@ public class ControllerServiceResource extends ApplicationResource {
     private WebClusterManager clusterManager;
     private NiFiProperties properties;
 
+    @Context
+    private ServletContext servletContext;
+    
     /**
      * Populates the uri for the specified controller service.
      * 
@@ -95,6 +102,18 @@ public class ControllerServiceResource extends ApplicationResource {
         // populate the controller service href
         controllerService.setUri(generateResourceUri("controller", "controller-services", availability, controllerService.getId()));
         controllerService.setAvailability(availability);
+        
+        // see if this processor has any ui extensions
+        final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
+        if (uiExtensionMapping.hasUiExtension(controllerService.getType())) {
+            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(controllerService.getType());
+            for (final UiExtension uiExtension : uiExtensions) {
+                if (UiExtensionType.ProcessorConfiguration.equals(uiExtension.getExtensionType())) {
+                    controllerService.setCustomUiUrl(uiExtension.getContextPath() + "/configure");
+                }
+            }
+        }
+        
         return controllerService;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/24d787e5/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index 75995dd..6fa6f21 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -70,6 +70,9 @@ import org.apache.nifi.web.api.request.IntegerParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.ui.extension.UiExtension;
+import org.apache.nifi.ui.extension.UiExtensionMapping;
+import org.apache.nifi.web.UiExtensionType;
 import org.codehaus.enunciate.jaxrs.TypeHint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -118,9 +121,21 @@ public class ProcessorResource extends ApplicationResource {
         // get the config details and see if there is a custom ui for this processor type
         ProcessorConfigDTO config = processor.getConfig();
         if (config != null) {
+            // consider legacy custom ui fist
             String customUiUrl = servletContext.getInitParameter(processor.getType());
             if (StringUtils.isNotBlank(customUiUrl)) {
                 config.setCustomUiUrl(customUiUrl);
+            } else {
+                // see if this processor has any ui extensions
+                final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
+                if (uiExtensionMapping.hasUiExtension(processor.getType())) {
+                    final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(processor.getType());
+                    for (final UiExtension uiExtension : uiExtensions) {
+                        if (UiExtensionType.ProcessorConfiguration.equals(uiExtension.getExtensionType())) {
+                            config.setCustomUiUrl(uiExtension.getContextPath() + "/configure");
+                        }
+                    }
+                }
             }
         }