You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2017/03/24 15:10:53 UTC

[06/17] nifi git commit: NIFI-3380 Bumping NAR plugin to 1.2.0-SNAPSHOT development to leverage changes from master, adding buildnumber-maven-plugin to nifi-nar-bundles to properly set build info in MANIFEST of NARs - Refactoring NarDetails to include al

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateAuthorizable.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateAuthorizable.java
deleted file mode 100644
index 17a8896..0000000
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateAuthorizable.java
+++ /dev/null
@@ -1,48 +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.authorization;
-
-import org.apache.nifi.authorization.resource.Authorizable;
-
-import java.util.Set;
-
-/**
- * Authorizable for a Template.
- */
-public interface TemplateAuthorizable {
-    /**
-     * Returns the authorizable for this template. Non null
-     *
-     * @return authorizable
-     */
-    Authorizable getAuthorizable();
-
-    /**
-     * Returns temporary instances of all encapsulated processors. Non null
-     *
-     * @return temporary instances of all encapsulated processors
-     */
-    Set<ConfigurableComponentAuthorizable> getEncapsulatedProcessors();
-
-    /**
-     * Returns temporary instances of all encapsulated controller services. Non null
-     *
-     * @return temporary instances of all encapsulated controller services
-     */
-    Set<ConfigurableComponentAuthorizable> getEncapsulatedControllerServices();
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateContentsAuthorizable.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateContentsAuthorizable.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateContentsAuthorizable.java
new file mode 100644
index 0000000..7222765
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/authorization/TemplateContentsAuthorizable.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authorization;
+
+import java.util.Set;
+
+/**
+ * Authorizable for a Template.
+ */
+public interface TemplateContentsAuthorizable {
+    /**
+     * Returns temporary instances of all encapsulated processors. Non null
+     *
+     * @return temporary instances of all encapsulated processors
+     */
+    Set<ConfigurableComponentAuthorizable> getEncapsulatedProcessors();
+
+    /**
+     * Returns temporary instances of all encapsulated controller services. Non null
+     *
+     * @return temporary instances of all encapsulated controller services
+     */
+    Set<ConfigurableComponentAuthorizable> getEncapsulatedControllerServices();
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 1a72127..039cbf8 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -153,6 +153,8 @@ public interface NiFiServiceFacade {
      */
     Set<Revision> getRevisionsFromSnippet(String snippetId);
 
+
+
     // ----------------------------------------
     // Controller methods
     // ----------------------------------------
@@ -328,24 +330,37 @@ public interface NiFiServiceFacade {
     /**
      * Returns the list of processor types.
      *
-     * @return The list of available processor types
+     * @param bundleGroupFilter if specified, must be member of bundle group
+     * @param bundleArtifactFilter if specified, must be member of bundle artifact
+     * @param typeFilter if specified, type must match
+     * @return The list of available processor types matching specified criteria
      */
-    Set<DocumentedTypeDTO> getProcessorTypes();
+    Set<DocumentedTypeDTO> getProcessorTypes(final String bundleGroupFilter, final String bundleArtifactFilter, final String typeFilter);
 
     /**
      * Returns the list of controller service types.
      *
      * @param serviceType Filters only service types that implement this type
-     * @return The list of available controller types
+     * @param serviceBundleGroup if serviceType specified, the bundle group of the serviceType
+     * @param serviceBundleArtifact if serviceType specified, the bundle artifact of the serviceType
+     * @param serviceBundleVersion if serviceType specified, the bundle version of the serviceType
+     * @param bundleGroupFilter if specified, must be member of bundle group
+     * @param bundleArtifactFilter if specified, must be member of bundle artifact
+     * @param typeFilter if specified, type must match
+     * @return The list of available controller types matching specified criteria
      */
-    Set<DocumentedTypeDTO> getControllerServiceTypes(String serviceType);
+    Set<DocumentedTypeDTO> getControllerServiceTypes(final String serviceType, final String serviceBundleGroup, final String serviceBundleArtifact, final String serviceBundleVersion,
+                                                     final String bundleGroupFilter, final String bundleArtifactFilter, final String typeFilter);
 
     /**
      * Returns the list of reporting task types.
      *
-     * @return The list of available reporting task types
+     * @param bundleGroupFilter if specified, must be member of bundle group
+     * @param bundleArtifactFilter if specified, must be member of bundle artifact
+     * @param typeFilter if specified, type must match
+     * @return The list of available reporting task types matching specified criteria
      */
-    Set<DocumentedTypeDTO> getReportingTaskTypes();
+    Set<DocumentedTypeDTO> getReportingTaskTypes(final String bundleGroupFilter, final String bundleArtifactFilter, final String typeFilter);
 
     /**
      * Returns the list of prioritizer types.
@@ -400,13 +415,14 @@ public interface NiFiServiceFacade {
      * Instantiate the corresponding template.
      *
      * @param groupId group id
-     * @param templateId template id
      * @param originX x
      * @param originY y
+     * @param templateEncodingVersion template encoding version
+     * @param snippet template snippet
      * @param idGenerationSeed the ID to use for generating UUID's. May be null.
      * @return snapshot
      */
-    FlowEntity createTemplateInstance(String groupId, Double originX, Double originY, String templateId, String idGenerationSeed);
+    FlowEntity createTemplateInstance(String groupId, Double originX, Double originY, String templateEncodingVersion, FlowSnippetDTO snippet, String idGenerationSeed);
 
     /**
      * Gets the template with the specified id.
@@ -441,6 +457,14 @@ public interface NiFiServiceFacade {
     // ----------------------------------------
     // Processor methods
     // ----------------------------------------
+
+    /**
+     * Verifies the specified processor can be created.
+     *
+     * @param processorDTO processor
+     */
+    void verifyCreateProcessor(ProcessorDTO processorDTO);
+
     /**
      * Creates a new Processor.
      *
@@ -1321,6 +1345,14 @@ public interface NiFiServiceFacade {
     // ----------------------------------------
     // Controller Services methods
     // ----------------------------------------
+
+    /**
+     * Verifies the specified controller service can be created.
+     *
+     * @param controllerServiceDTO service
+     */
+    void verifyCreateControllerService(ControllerServiceDTO controllerServiceDTO);
+
     /**
      * Creates a controller service.
      *
@@ -1420,6 +1452,14 @@ public interface NiFiServiceFacade {
     // ----------------------------------------
     // Reporting Task methods
     // ----------------------------------------
+
+    /**
+     * Verifies the specified reporting task can be created.
+     *
+     * @param reportingTaskDTO task
+     */
+    void verifyCreateReportingTask(ReportingTaskDTO reportingTaskDTO);
+
     /**
      * Creates a reporting task.
      *

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index cc472b8..1f8fd92 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -396,11 +396,18 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public void verifyCreateProcessor(ProcessorDTO processorDTO) {
+        processorDAO.verifyCreate(processorDTO);
+    }
+
+    @Override
     public void verifyUpdateProcessor(final ProcessorDTO processorDTO) {
         // if group does not exist, then the update request is likely creating it
         // so we don't verify since it will fail
         if (processorDAO.hasProcessor(processorDTO.getId())) {
             processorDAO.verifyUpdate(processorDTO);
+        } else {
+            verifyCreateProcessor(processorDTO);
         }
     }
 
@@ -444,11 +451,18 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public void verifyCreateControllerService(ControllerServiceDTO controllerServiceDTO) {
+        controllerServiceDAO.verifyCreate(controllerServiceDTO);
+    }
+
+    @Override
     public void verifyUpdateControllerService(final ControllerServiceDTO controllerServiceDTO) {
         // if service does not exist, then the update request is likely creating it
         // so we don't verify since it will fail
         if (controllerServiceDAO.hasControllerService(controllerServiceDTO.getId())) {
             controllerServiceDAO.verifyUpdate(controllerServiceDTO);
+        } else {
+            verifyCreateControllerService(controllerServiceDTO);
         }
     }
 
@@ -463,11 +477,18 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
+    public void verifyCreateReportingTask(ReportingTaskDTO reportingTaskDTO) {
+        reportingTaskDAO.verifyCreate(reportingTaskDTO);
+    }
+
+    @Override
     public void verifyUpdateReportingTask(final ReportingTaskDTO reportingTaskDTO) {
         // if tasks does not exist, then the update request is likely creating it
         // so we don't verify since it will fail
         if (reportingTaskDAO.hasReportingTask(reportingTaskDTO.getId())) {
             reportingTaskDAO.verifyUpdate(reportingTaskDTO);
+        } else {
+            verifyCreateReportingTask(reportingTaskDTO);
         }
     }
 
@@ -1657,7 +1678,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                     }
 
                     try {
-                        final ControllerService controllerService = controllerFacade.createTemporaryControllerService(dto.getType()).getControllerServiceImplementation();
+                        final ControllerService controllerService = controllerFacade.createTemporaryControllerService(dto.getType(), dto.getBundle()).getControllerServiceImplementation();
                         controllerService.getPropertyDescriptors().forEach(descriptor -> {
                             if (dto.getProperties().get(descriptor.getName()) == null) {
                                 dto.getProperties().put(descriptor.getName(), descriptor.getDefaultValue());
@@ -1681,7 +1702,7 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
                     }
 
                     try {
-                        final ProcessorNode processorNode = controllerFacade.createTemporaryProcessor(dto.getType());
+                        final ProcessorNode processorNode = controllerFacade.createTemporaryProcessor(dto.getType(), dto.getBundle());
                         processorNode.getPropertyDescriptors().forEach(descriptor -> {
                             if (config.getProperties().get(descriptor.getName()) == null) {
                                 config.getProperties().put(descriptor.getName(), descriptor.getDefaultValue());
@@ -1774,10 +1795,12 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public FlowEntity createTemplateInstance(final String groupId, final Double originX, final Double originY, final String templateId, final String idGenerationSeed) {
+    public FlowEntity createTemplateInstance(final String groupId, final Double originX, final Double originY, final String templateEncodingVersion,
+                                             final FlowSnippetDTO requestSnippet, final String idGenerationSeed) {
+
         // instantiate the template - there is no need to make another copy of the flow snippet since the actual template
         // was copied and this dto is only used to instantiate it's components (which as already completed)
-        final FlowSnippetDTO snippet = templateDAO.instantiateTemplate(groupId, originX, originY, templateId, idGenerationSeed);
+        final FlowSnippetDTO snippet = templateDAO.instantiateTemplate(groupId, originX, originY, templateEncodingVersion, requestSnippet, idGenerationSeed);
 
         // save the flow
         controllerFacade.save();
@@ -2365,18 +2388,19 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade {
     }
 
     @Override
-    public Set<DocumentedTypeDTO> getProcessorTypes() {
-        return controllerFacade.getFlowFileProcessorTypes();
+    public Set<DocumentedTypeDTO> getProcessorTypes(final String bundleGroup, final String bundleArtifact, final String type) {
+        return controllerFacade.getFlowFileProcessorTypes(bundleGroup, bundleArtifact, type);
     }
 
     @Override
-    public Set<DocumentedTypeDTO> getControllerServiceTypes(final String serviceType) {
-        return controllerFacade.getControllerServiceTypes(serviceType);
+    public Set<DocumentedTypeDTO> getControllerServiceTypes(final String serviceType, final String serviceBundleGroup, final String serviceBundleArtifact, final String serviceBundleVersion,
+                                                            final String bundleGroup, final String bundleArtifact, final String type) {
+        return controllerFacade.getControllerServiceTypes(serviceType, serviceBundleGroup, serviceBundleArtifact, serviceBundleVersion, bundleGroup, bundleArtifact, type);
     }
 
     @Override
-    public Set<DocumentedTypeDTO> getReportingTaskTypes() {
-        return controllerFacade.getReportingTaskTypes();
+    public Set<DocumentedTypeDTO> getReportingTaskTypes(final String bundleGroup, final String bundleArtifact, final String type) {
+        return controllerFacade.getReportingTaskTypes(bundleGroup, bundleArtifact, type);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
index 79ddc81..c159273 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ApplicationResource.java
@@ -662,6 +662,11 @@ public abstract class ApplicationResource {
             // authorize access
             serviceFacade.authorizeAccess(authorizer);
 
+            // verify if necessary
+            if (verifier != null) {
+                verifier.run();
+            }
+
             // run the action
             return action.apply(entity);
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
index 1e56dbb..63f7c18 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java
@@ -295,16 +295,24 @@ public class ControllerResource extends ApplicationResource {
                 lookup -> {
                     authorizeController(RequestAction.WRITE);
 
-                    final ConfigurableComponentAuthorizable authorizable = lookup.getReportingTaskByType(requestReportingTask.getType());
-                    if (authorizable.isRestricted()) {
-                        lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
-                    }
-
-                    if (requestReportingTask.getProperties() != null) {
-                        AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestReportingTask.getProperties(), authorizable, authorizer, lookup);
+                    ConfigurableComponentAuthorizable authorizable = null;
+                    try {
+                        authorizable = lookup.getReportingTaskByType(requestReportingTask.getType(), requestReportingTask.getBundle());
+
+                        if (authorizable.isRestricted()) {
+                            lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+                        }
+
+                        if (requestReportingTask.getProperties() != null) {
+                            AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestReportingTask.getProperties(), authorizable, authorizer, lookup);
+                        }
+                    } finally {
+                        if (authorizable != null) {
+                            authorizable.cleanUpResources();
+                        }
                     }
                 },
-                null,
+                () -> serviceFacade.verifyCreateReportingTask(requestReportingTask),
                 (reportingTaskEntity) -> {
                     final ReportingTaskDTO reportingTask = reportingTaskEntity.getComponent();
 
@@ -393,16 +401,24 @@ public class ControllerResource extends ApplicationResource {
                 lookup -> {
                     authorizeController(RequestAction.WRITE);
 
-                    final ConfigurableComponentAuthorizable authorizable = lookup.getControllerServiceByType(requestControllerService.getType());
-                    if (authorizable.isRestricted()) {
-                        lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
-                    }
-
-                    if (requestControllerService.getProperties() != null) {
-                        AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup);
+                    ConfigurableComponentAuthorizable authorizable = null;
+                    try {
+                        authorizable = lookup.getControllerServiceByType(requestControllerService.getType(), requestControllerService.getBundle());
+
+                        if (authorizable.isRestricted()) {
+                            lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+                        }
+
+                        if (requestControllerService.getProperties() != null) {
+                            AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup);
+                        }
+                    } finally {
+                        if (authorizable != null) {
+                            authorizable.cleanUpResources();
+                        }
                     }
                 },
-                null,
+                () -> serviceFacade.verifyCreateControllerService(requestControllerService),
                 (controllerServiceEntity) -> {
                     final ControllerServiceDTO controllerService = controllerServiceEntity.getComponent();
 
@@ -561,7 +577,7 @@ public class ControllerResource extends ApplicationResource {
             )
             @PathParam("id") String id,
             @ApiParam(
-                    value = "The node configuration. The only configuration that will be honored at this endpoint is the status or primary flag.",
+                    value = "The node configuration. The only configuration that will be honored at this endpoint is the status.",
                     required = true
             ) NodeEntity nodeEntity) {
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/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-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
index 26ebd2b..46e95f1 100644
--- a/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-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
@@ -36,6 +36,7 @@ import org.apache.nifi.ui.extension.UiExtensionMapping;
 import org.apache.nifi.web.NiFiServiceFacade;
 import org.apache.nifi.web.Revision;
 import org.apache.nifi.web.UiExtensionType;
+import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.ComponentStateDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
@@ -124,10 +125,12 @@ public class ControllerServiceResource extends ApplicationResource {
      * Populates the uri for the specified controller service.
      */
     public ControllerServiceDTO populateRemainingControllerServiceContent(final ControllerServiceDTO controllerService) {
+        final BundleDTO bundle = controllerService.getBundle();
+
         // 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());
+        if (uiExtensionMapping.hasUiExtension(controllerService.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion())) {
+            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(controllerService.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion());
             for (final UiExtension uiExtension : uiExtensions) {
                 if (UiExtensionType.ControllerServiceConfiguration.equals(uiExtension.getExtensionType())) {
                     controllerService.setCustomUiUrl(uiExtension.getContextPath() + "/configure");

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
index a031725..bc45118 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
@@ -35,12 +35,15 @@ import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.ResourceFactory;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserUtils;
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleDetails;
 import org.apache.nifi.cluster.coordination.ClusterCoordinator;
 import org.apache.nifi.cluster.coordination.node.NodeConnectionState;
 import org.apache.nifi.cluster.manager.NodeResponse;
 import org.apache.nifi.cluster.protocol.NodeIdentifier;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.groups.ProcessGroup;
+import org.apache.nifi.nar.NarClassLoaders;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.web.IllegalClusterResourceRequestException;
 import org.apache.nifi.web.NiFiServiceFacade;
@@ -925,12 +928,32 @@ public class FlowResource extends ApplicationResource {
                     @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
             }
     )
-    public Response getProcessorTypes() throws InterruptedException {
+    public Response getProcessorTypes(
+            @ApiParam(
+                value = "If specified, will only return types that are a member of this bundle group.",
+                required = false
+            )
+            @QueryParam("bundleGroupFilter") String bundleGroupFilter,
+            @ApiParam(
+                    value = "If specified, will only return types that are a member of this bundle artifact.",
+                    required = false
+            )
+            @QueryParam("bundleArtifactFilter") String bundleArtifactFilter,
+            @ApiParam(
+                    value = "If specified, will only return types whose fully qualified classname matches.",
+                    required = false
+            )
+            @QueryParam("type") String typeFilter) throws InterruptedException {
+
         authorizeFlow();
 
+        if (isReplicateRequest()) {
+            return replicate(HttpMethod.GET);
+        }
+
         // create response entity
         final ProcessorTypesEntity entity = new ProcessorTypesEntity();
-        entity.setProcessorTypes(serviceFacade.getProcessorTypes());
+        entity.setProcessorTypes(serviceFacade.getProcessorTypes(bundleGroupFilter, bundleArtifactFilter, typeFilter));
 
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();
@@ -965,16 +988,57 @@ public class FlowResource extends ApplicationResource {
     )
     public Response getControllerServiceTypes(
             @ApiParam(
-                    value = "If specified, will only return controller services of this type.",
+                    value = "If specified, will only return controller services that are compatible with this type of service.",
+                    required = false
+            )
+            @QueryParam("serviceType") String serviceType,
+            @ApiParam(
+                    value = "If serviceType specified, is the bundle group of the serviceType.",
+                    required = false
+            )
+            @QueryParam("serviceBundleGroup") String serviceBundleGroup,
+            @ApiParam(
+                    value = "If serviceType specified, is the bundle artifact of the serviceType.",
                     required = false
             )
-            @QueryParam("serviceType") String serviceType) throws InterruptedException {
+            @QueryParam("serviceBundleArtifact") String serviceBundleArtifact,
+            @ApiParam(
+                    value = "If serviceType specified, is the bundle version of the serviceType.",
+                    required = false
+            )
+            @QueryParam("serviceBundleVersion") String serviceBundleVersion,
+            @ApiParam(
+                    value = "If specified, will only return types that are a member of this bundle group.",
+                    required = false
+            )
+            @QueryParam("bundleGroupFilter") String bundleGroupFilter,
+            @ApiParam(
+                    value = "If specified, will only return types that are a member of this bundle artifact.",
+                    required = false
+            )
+            @QueryParam("bundleArtifactFilter") String bundleArtifactFilter,
+            @ApiParam(
+                    value = "If specified, will only return types whose fully qualified classname matches.",
+                    required = false
+            )
+            @QueryParam("typeFilter") String typeFilter) throws InterruptedException {
 
         authorizeFlow();
 
+        if (serviceType != null) {
+            if (serviceBundleGroup == null || serviceBundleArtifact == null || serviceBundleVersion == null) {
+                throw new IllegalArgumentException("When specifying the serviceType the serviceBundleGroup, serviceBundleArtifact, and serviceBundleVersion must be specified.");
+            }
+        }
+
+        if (isReplicateRequest()) {
+            return replicate(HttpMethod.GET);
+        }
+
         // create response entity
         final ControllerServiceTypesEntity entity = new ControllerServiceTypesEntity();
-        entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes(serviceType));
+        entity.setControllerServiceTypes(serviceFacade.getControllerServiceTypes(serviceType, serviceBundleGroup, serviceBundleArtifact, serviceBundleVersion,
+                bundleGroupFilter, bundleArtifactFilter, typeFilter));
 
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();
@@ -1006,12 +1070,32 @@ public class FlowResource extends ApplicationResource {
                     @ApiResponse(code = 409, message = "The request was valid but NiFi was not in the appropriate state to process it. Retrying the same request later may be successful.")
             }
     )
-    public Response getReportingTaskTypes() throws InterruptedException {
+    public Response getReportingTaskTypes(
+            @ApiParam(
+                    value = "If specified, will only return types that are a member of this bundle group.",
+                    required = false
+            )
+            @QueryParam("bundleGroupFilter") String bundleGroupFilter,
+            @ApiParam(
+                    value = "If specified, will only return types that are a member of this bundle artifact.",
+                    required = false
+            )
+            @QueryParam("bundleArtifactFilter") String bundleArtifactFilter,
+            @ApiParam(
+                    value = "If specified, will only return types whose fully qualified classname matches.",
+                    required = false
+            )
+            @QueryParam("type") String typeFilter) throws InterruptedException {
+
         authorizeFlow();
 
+        if (isReplicateRequest()) {
+            return replicate(HttpMethod.GET);
+        }
+
         // create response entity
         final ReportingTaskTypesEntity entity = new ReportingTaskTypesEntity();
-        entity.setReportingTaskTypes(serviceFacade.getReportingTaskTypes());
+        entity.setReportingTaskTypes(serviceFacade.getReportingTaskTypes(bundleGroupFilter, bundleArtifactFilter, typeFilter));
 
         // generate the response
         return clusterContext(generateOkResponse(entity)).build();
@@ -1046,6 +1130,10 @@ public class FlowResource extends ApplicationResource {
     public Response getPrioritizers() throws InterruptedException {
         authorizeFlow();
 
+        if (isReplicateRequest()) {
+            return replicate(HttpMethod.GET);
+        }
+
         // create response entity
         final PrioritizerTypesEntity entity = new PrioritizerTypesEntity();
         entity.setPrioritizerTypes(serviceFacade.getWorkQueuePrioritizerTypes());
@@ -1084,7 +1172,6 @@ public class FlowResource extends ApplicationResource {
         // create the about dto
         final AboutDTO aboutDTO = new AboutDTO();
         aboutDTO.setTitle("NiFi");
-        aboutDTO.setVersion(getProperties().getUiTitle());
         aboutDTO.setUri(generateResourceUri());
         aboutDTO.setTimezone(new Date());
 
@@ -1092,11 +1179,19 @@ public class FlowResource extends ApplicationResource {
         final NiFiProperties properties = getProperties();
         aboutDTO.setContentViewerUrl(properties.getProperty(NiFiProperties.CONTENT_VIEWER_URL));
 
-        // Get build info
-        aboutDTO.setBuildTag(properties.getProperty(NiFiProperties.BUILD_TAG));
-        aboutDTO.setBuildRevision(properties.getProperty(NiFiProperties.BUILD_REVISION));
-        aboutDTO.setBuildBranch(properties.getProperty(NiFiProperties.BUILD_BRANCH));
-        aboutDTO.setBuildTimestamp(properties.getBuildTimestamp());
+        final Bundle frameworkBundle = NarClassLoaders.getInstance().getFrameworkBundle();
+        if (frameworkBundle != null) {
+            final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails();
+
+            // set the version
+            aboutDTO.setVersion(frameworkDetails.getCoordinate().getVersion());
+
+            // Get build info
+            aboutDTO.setBuildTag(frameworkDetails.getBuildTag());
+            aboutDTO.setBuildRevision(frameworkDetails.getBuildRevision());
+            aboutDTO.setBuildBranch(frameworkDetails.getBuildBranch());
+            aboutDTO.setBuildTimestamp(frameworkDetails.getBuildTimestampDate());
+        }
 
         // create the response entity
         final AboutEntity entity = new AboutEntity();

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index 58c9e30..ffcc084 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -32,17 +32,22 @@ import org.apache.nifi.authorization.ConfigurableComponentAuthorizable;
 import org.apache.nifi.authorization.ProcessGroupAuthorizable;
 import org.apache.nifi.authorization.RequestAction;
 import org.apache.nifi.authorization.SnippetAuthorizable;
-import org.apache.nifi.authorization.TemplateAuthorizable;
+import org.apache.nifi.authorization.TemplateContentsAuthorizable;
 import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.authorization.user.NiFiUserUtils;
+import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.connectable.ConnectableType;
+import org.apache.nifi.controller.serialization.FlowEncodingVersion;
 import org.apache.nifi.remote.util.SiteToSiteRestApiClient;
+import org.apache.nifi.util.BundleUtils;
 import org.apache.nifi.web.NiFiServiceFacade;
 import org.apache.nifi.web.ResourceNotFoundException;
 import org.apache.nifi.web.Revision;
+import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
+import org.apache.nifi.web.api.dto.FlowSnippetDTO;
 import org.apache.nifi.web.api.dto.PositionDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
@@ -652,17 +657,25 @@ public class ProcessGroupResource extends ApplicationResource {
                     final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
                     processGroup.authorize(authorizer, RequestAction.WRITE, user);
 
-                    final ConfigurableComponentAuthorizable authorizable = lookup.getProcessorByType(requestProcessor.getType());
-                    if (authorizable.isRestricted()) {
-                        lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, user);
-                    }
+                    ConfigurableComponentAuthorizable authorizable = null;
+                    try {
+                        authorizable = lookup.getProcessorByType(requestProcessor.getType(), requestProcessor.getBundle());
+
+                        if (authorizable.isRestricted()) {
+                            lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, user);
+                        }
 
-                    final ProcessorConfigDTO config = requestProcessor.getConfig();
-                    if (config != null && config.getProperties() != null) {
-                        AuthorizeControllerServiceReference.authorizeControllerServiceReferences(config.getProperties(), authorizable, authorizer, lookup);
+                        final ProcessorConfigDTO config = requestProcessor.getConfig();
+                        if (config != null && config.getProperties() != null) {
+                            AuthorizeControllerServiceReference.authorizeControllerServiceReferences(config.getProperties(), authorizable, authorizer, lookup);
+                        }
+                    } finally {
+                        if (authorizable != null) {
+                            authorizable.cleanUpResources();
+                        }
                     }
                 },
-                null,
+                () -> serviceFacade.verifyCreateProcessor(requestProcessor),
                 processorEntity -> {
                     final ProcessorDTO processor = processorEntity.getComponent();
 
@@ -1835,6 +1848,33 @@ public class ProcessGroupResource extends ApplicationResource {
     // -----------------
 
     /**
+     * Discovers the compatible bundle details for the components in the specified snippet.
+     *
+     * @param snippet the snippet
+     */
+    private void discoverCompatibleBundles(final FlowSnippetDTO snippet) {
+        if (snippet.getProcessors() != null) {
+            snippet.getProcessors().forEach(processor -> {
+                final BundleCoordinate coordinate = BundleUtils.getCompatibleBundle(processor.getType(), processor.getBundle());
+                processor.setBundle(new BundleDTO(coordinate.getGroup(), coordinate.getId(), coordinate.getVersion()));
+            });
+        }
+
+        if (snippet.getControllerServices() != null) {
+            snippet.getControllerServices().forEach(controllerService -> {
+                final BundleCoordinate coordinate = BundleUtils.getCompatibleBundle(controllerService.getType(), controllerService.getBundle());
+                controllerService.setBundle(new BundleDTO(coordinate.getGroup(), coordinate.getId(), coordinate.getVersion()));
+            });
+        }
+
+        if (snippet.getProcessGroups() != null) {
+            snippet.getProcessGroups().forEach(processGroup -> {
+                discoverCompatibleBundles(processGroup.getContents());
+            });
+        }
+    }
+
+    /**
      * Instantiates the specified template within this ProcessGroup. The template instance that is instantiated cannot be referenced at a later time, therefore there is no
      * corresponding URI. Instead the request URI is returned.
      * <p>
@@ -1881,7 +1921,43 @@ public class ProcessGroupResource extends ApplicationResource {
 
         // ensure the position has been specified
         if (requestInstantiateTemplateRequestEntity == null || requestInstantiateTemplateRequestEntity.getOriginX() == null || requestInstantiateTemplateRequestEntity.getOriginY() == null) {
-            throw new IllegalArgumentException("The  origin position (x, y) must be specified");
+            throw new IllegalArgumentException("The origin position (x, y) must be specified.");
+        }
+
+        // ensure the template id was provided
+        if (requestInstantiateTemplateRequestEntity.getTemplateId() == null) {
+            throw new IllegalArgumentException("The template id must be specified.");
+        }
+
+        // ensure the template encoding version is valid
+        if (requestInstantiateTemplateRequestEntity.getEncodingVersion() != null) {
+            try {
+                FlowEncodingVersion.parse(requestInstantiateTemplateRequestEntity.getEncodingVersion());
+            } catch (final IllegalArgumentException e) {
+                throw new IllegalArgumentException("The template encoding version is not valid. The expected format is <number>.<number>");
+            }
+        }
+
+        // populate the encoding version if necessary
+        if (requestInstantiateTemplateRequestEntity.getEncodingVersion() == null) {
+            // if the encoding version is not specified, use the latest encoding version as these options were
+            // not available pre 1.x, will be overridden if populating from the underlying template below
+            requestInstantiateTemplateRequestEntity.setEncodingVersion(TemplateDTO.MAX_ENCODING_VERSION);
+        }
+
+        // populate the component bundles if necessary
+        if (requestInstantiateTemplateRequestEntity.getSnippet() == null) {
+            // get the desired template in order to determine the supported bundles
+            final TemplateDTO requestedTemplate = serviceFacade.exportTemplate(requestInstantiateTemplateRequestEntity.getTemplateId());
+            final FlowSnippetDTO requestTemplateContents = requestedTemplate.getSnippet();
+
+            // determine the compatible bundles to use for each component in this template, this ensures the nodes in the cluster
+            // instantiate the components from the same bundles
+            discoverCompatibleBundles(requestTemplateContents);
+
+            // update the requested template as necessary - use the encoding version from the underlying template
+            requestInstantiateTemplateRequestEntity.setEncodingVersion(requestedTemplate.getEncodingVersion());
+            requestInstantiateTemplateRequestEntity.setSnippet(requestTemplateContents);
         }
 
         if (isReplicateRequest()) {
@@ -1898,9 +1974,11 @@ public class ProcessGroupResource extends ApplicationResource {
                     final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
                     processGroup.authorize(authorizer, RequestAction.WRITE, user);
 
+                    final Authorizable template = lookup.getTemplate(requestInstantiateTemplateRequestEntity.getTemplateId());
+                    template.authorize(authorizer, RequestAction.READ, user);
+
                     // ensure read on the template
-                    final TemplateAuthorizable template = lookup.getTemplate(requestInstantiateTemplateRequestEntity.getTemplateId());
-                    template.getAuthorizable().authorize(authorizer, RequestAction.READ, user);
+                    final TemplateContentsAuthorizable templateContents = lookup.getTemplateContents(requestInstantiateTemplateRequestEntity.getSnippet());
 
                     // flag to only perform the restricted check once, atomic reference so we can mark final and use in lambda
                     final AtomicBoolean restrictedCheckPerformed = new AtomicBoolean(false);
@@ -1911,14 +1989,14 @@ public class ProcessGroupResource extends ApplicationResource {
                     };
 
                     // ensure restricted access if necessary
-                    template.getEncapsulatedProcessors().forEach(authorizeRestricted);
-                    template.getEncapsulatedControllerServices().forEach(authorizeRestricted);
+                    templateContents.getEncapsulatedProcessors().forEach(authorizeRestricted);
+                    templateContents.getEncapsulatedControllerServices().forEach(authorizeRestricted);
                 },
-                null,
+                () -> serviceFacade.verifyComponentTypes(requestInstantiateTemplateRequestEntity.getSnippet()),
                 instantiateTemplateRequestEntity -> {
                     // create the template and generate the json
-                    final FlowEntity entity = serviceFacade.createTemplateInstance(groupId, instantiateTemplateRequestEntity.getOriginX(),
-                            instantiateTemplateRequestEntity.getOriginY(), instantiateTemplateRequestEntity.getTemplateId(), getIdGenerationSeed().orElse(null));
+                    final FlowEntity entity = serviceFacade.createTemplateInstance(groupId, instantiateTemplateRequestEntity.getOriginX(), instantiateTemplateRequestEntity.getOriginY(),
+                            instantiateTemplateRequestEntity.getEncodingVersion(), instantiateTemplateRequestEntity.getSnippet(), getIdGenerationSeed().orElse(null));
 
                     final FlowDTO flowSnippet = entity.getFlow();
 
@@ -2161,10 +2239,7 @@ public class ProcessGroupResource extends ApplicationResource {
                     final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
                     processGroup.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
                 },
-                () -> {
-                    serviceFacade.verifyCanAddTemplate(groupId, requestTemplateEntity.getTemplate().getName());
-                    serviceFacade.verifyComponentTypes(requestTemplateEntity.getTemplate().getSnippet());
-                },
+                () -> serviceFacade.verifyCanAddTemplate(groupId, requestTemplateEntity.getTemplate().getName()),
                 templateEntity -> {
                     try {
                         // import the template
@@ -2272,16 +2347,24 @@ public class ProcessGroupResource extends ApplicationResource {
                     final Authorizable processGroup = lookup.getProcessGroup(groupId).getAuthorizable();
                     processGroup.authorize(authorizer, RequestAction.WRITE, user);
 
-                    final ConfigurableComponentAuthorizable authorizable = lookup.getControllerServiceByType(requestControllerService.getType());
-                    if (authorizable.isRestricted()) {
-                        lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, user);
-                    }
+                    ConfigurableComponentAuthorizable authorizable = null;
+                    try {
+                        authorizable = lookup.getControllerServiceByType(requestControllerService.getType(), requestControllerService.getBundle());
+
+                        if (authorizable.isRestricted()) {
+                            lookup.getRestrictedComponents().authorize(authorizer, RequestAction.WRITE, user);
+                        }
 
-                    if (requestControllerService.getProperties() != null) {
-                        AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup);
+                        if (requestControllerService.getProperties() != null) {
+                            AuthorizeControllerServiceReference.authorizeControllerServiceReferences(requestControllerService.getProperties(), authorizable, authorizer, lookup);
+                        }
+                    } finally {
+                        if (authorizable != null) {
+                            authorizable.cleanUpResources();
+                        }
                     }
                 },
-                null,
+                () -> serviceFacade.verifyCreateControllerService(requestControllerService),
                 controllerServiceEntity -> {
                     final ControllerServiceDTO controllerService = controllerServiceEntity.getComponent();
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/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-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
index 1238e64..204d57b 100644
--- a/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-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
@@ -35,6 +35,7 @@ import org.apache.nifi.ui.extension.UiExtensionMapping;
 import org.apache.nifi.web.NiFiServiceFacade;
 import org.apache.nifi.web.Revision;
 import org.apache.nifi.web.UiExtensionType;
+import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.ComponentStateDTO;
 import org.apache.nifi.web.api.dto.PositionDTO;
 import org.apache.nifi.web.api.dto.ProcessorConfigDTO;
@@ -121,10 +122,12 @@ public class ProcessorResource extends ApplicationResource {
             if (StringUtils.isNotBlank(customUiUrl)) {
                 config.setCustomUiUrl(customUiUrl);
             } else {
+                final BundleDTO bundle = processor.getBundle();
+
                 // 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());
+                if (uiExtensionMapping.hasUiExtension(processor.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion())) {
+                    final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(processor.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion());
                     for (final UiExtension uiExtension : uiExtensions) {
                         if (UiExtensionType.ProcessorConfiguration.equals(uiExtension.getExtensionType())) {
                             config.setCustomUiUrl(uiExtension.getContextPath() + "/configure");

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
index 39c75f9..c62d24d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
@@ -34,6 +34,7 @@ import org.apache.nifi.ui.extension.UiExtensionMapping;
 import org.apache.nifi.web.NiFiServiceFacade;
 import org.apache.nifi.web.Revision;
 import org.apache.nifi.web.UiExtensionType;
+import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.ComponentStateDTO;
 import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
 import org.apache.nifi.web.api.dto.ReportingTaskDTO;
@@ -111,10 +112,12 @@ public class ReportingTaskResource extends ApplicationResource {
      * Populates the uri for the specified reporting task.
      */
     public ReportingTaskDTO populateRemainingReportingTaskContent(final ReportingTaskDTO reportingTask) {
+        final BundleDTO bundle = reportingTask.getBundle();
+
         // see if this processor has any ui extensions
         final UiExtensionMapping uiExtensionMapping = (UiExtensionMapping) servletContext.getAttribute("nifi-ui-extensions");
-        if (uiExtensionMapping.hasUiExtension(reportingTask.getType())) {
-            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(reportingTask.getType());
+        if (uiExtensionMapping.hasUiExtension(reportingTask.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion())) {
+            final List<UiExtension> uiExtensions = uiExtensionMapping.getUiExtension(reportingTask.getType(), bundle.getGroup(), bundle.getArtifact(), bundle.getVersion());
             for (final UiExtension uiExtension : uiExtensions) {
                 if (UiExtensionType.ReportingTaskConfiguration.equals(uiExtension.getExtensionType())) {
                     reportingTask.setCustomUiUrl(uiExtension.getContextPath() + "/configure");

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java
index 515ade6..52b31a5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/TemplateResource.java
@@ -122,7 +122,7 @@ public class TemplateResource extends ApplicationResource {
 
         // authorize access
         serviceFacade.authorizeAccess(lookup -> {
-            final Authorizable template = lookup.getTemplate(id).getAuthorizable();
+            final Authorizable template = lookup.getTemplate(id);
             template.authorize(authorizer, RequestAction.READ, NiFiUserUtils.getNiFiUser());
         });
 
@@ -197,7 +197,7 @@ public class TemplateResource extends ApplicationResource {
                 serviceFacade,
                 requestTemplateEntity,
                 lookup -> {
-                    final Authorizable template = lookup.getTemplate(id).getAuthorizable();
+                    final Authorizable template = lookup.getTemplate(id);
 
                     // ensure write permission to the template
                     template.authorize(authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());

http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
index 4e238d2..aaa33d0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
@@ -16,33 +16,8 @@
  */
 package org.apache.nifi.web.api.dto;
 
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TimeZone;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import javax.ws.rs.WebApplicationException;
-
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.component.details.ComponentDetails;
 import org.apache.nifi.action.component.details.ExtensionDetails;
@@ -72,6 +47,9 @@ import org.apache.nifi.authorization.User;
 import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.ComponentAuthorizable;
 import org.apache.nifi.authorization.user.NiFiUserUtils;
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.bundle.BundleDetails;
 import org.apache.nifi.cluster.coordination.heartbeat.NodeHeartbeat;
 import org.apache.nifi.cluster.coordination.node.NodeConnectionStatus;
 import org.apache.nifi.cluster.event.NodeEvent;
@@ -122,6 +100,8 @@ import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.ProcessGroupCounts;
 import org.apache.nifi.groups.RemoteProcessGroup;
 import org.apache.nifi.history.History;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarClassLoaders;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.provenance.lineage.ComputeLineageResult;
@@ -137,7 +117,6 @@ import org.apache.nifi.reporting.ReportingTask;
 import org.apache.nifi.scheduling.SchedulingStrategy;
 import org.apache.nifi.util.FormatUtils;
 import org.apache.nifi.util.NiFiProperties;
-import org.apache.nifi.util.StringUtils;
 import org.apache.nifi.web.FlowModification;
 import org.apache.nifi.web.Revision;
 import org.apache.nifi.web.api.dto.action.ActionDTO;
@@ -184,6 +163,32 @@ import org.apache.nifi.web.api.entity.TenantEntity;
 import org.apache.nifi.web.controller.ControllerFacade;
 import org.apache.nifi.web.revision.RevisionManager;
 
+import javax.ws.rs.WebApplicationException;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
 public final class DtoFactory {
 
     @SuppressWarnings("rawtypes")
@@ -1230,10 +1235,17 @@ public final class DtoFactory {
     }
 
     public ReportingTaskDTO createReportingTaskDto(final ReportingTaskNode reportingTaskNode) {
+        final BundleCoordinate bundleCoordinate = reportingTaskNode.getBundleCoordinate();
+        final List<Bundle> compatibleBundles = ExtensionManager.getBundles(reportingTaskNode.getCanonicalClassName()).stream().filter(bundle -> {
+            final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate();
+            return bundleCoordinate.getGroup().equals(coordinate.getGroup()) && bundleCoordinate.getId().equals(coordinate.getId());
+        }).collect(Collectors.toList());
+
         final ReportingTaskDTO dto = new ReportingTaskDTO();
         dto.setId(reportingTaskNode.getIdentifier());
         dto.setName(reportingTaskNode.getName());
         dto.setType(reportingTaskNode.getCanonicalClassName());
+        dto.setBundle(createBundleDto(bundleCoordinate));
         dto.setSchedulingStrategy(reportingTaskNode.getSchedulingStrategy().name());
         dto.setSchedulingPeriod(reportingTaskNode.getSchedulingPeriod());
         dto.setState(reportingTaskNode.getScheduledState().name());
@@ -1242,6 +1254,8 @@ public final class DtoFactory {
         dto.setComments(reportingTaskNode.getComments());
         dto.setPersistsState(reportingTaskNode.getReportingTask().getClass().isAnnotationPresent(Stateful.class));
         dto.setRestricted(reportingTaskNode.isRestricted());
+        dto.setExtensionMissing(reportingTaskNode.isExtensionMissing());
+        dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
 
         final Map<String, String> defaultSchedulingPeriod = new HashMap<>();
         defaultSchedulingPeriod.put(SchedulingStrategy.TIMER_DRIVEN.name(), SchedulingStrategy.TIMER_DRIVEN.getDefaultSchedulingPeriod());
@@ -1302,16 +1316,26 @@ public final class DtoFactory {
     }
 
     public ControllerServiceDTO createControllerServiceDto(final ControllerServiceNode controllerServiceNode) {
+        final BundleCoordinate bundleCoordinate = controllerServiceNode.getBundleCoordinate();
+        final List<Bundle> compatibleBundles = ExtensionManager.getBundles(controllerServiceNode.getCanonicalClassName()).stream().filter(bundle -> {
+            final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate();
+            return bundleCoordinate.getGroup().equals(coordinate.getGroup()) && bundleCoordinate.getId().equals(coordinate.getId());
+        }).collect(Collectors.toList());
+
         final ControllerServiceDTO dto = new ControllerServiceDTO();
         dto.setId(controllerServiceNode.getIdentifier());
         dto.setParentGroupId(controllerServiceNode.getProcessGroup() == null ? null : controllerServiceNode.getProcessGroup().getIdentifier());
         dto.setName(controllerServiceNode.getName());
         dto.setType(controllerServiceNode.getCanonicalClassName());
+        dto.setBundle(createBundleDto(bundleCoordinate));
+        dto.setControllerServiceApis(createControllerServiceApiDto(controllerServiceNode.getControllerServiceImplementation().getClass()));
         dto.setState(controllerServiceNode.getState().name());
         dto.setAnnotationData(controllerServiceNode.getAnnotationData());
         dto.setComments(controllerServiceNode.getComments());
         dto.setPersistsState(controllerServiceNode.getControllerServiceImplementation().getClass().isAnnotationPresent(Stateful.class));
         dto.setRestricted(controllerServiceNode.isRestricted());
+        dto.setExtensionMissing(controllerServiceNode.isExtensionMissing());
+        dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
 
         // sort a copy of the properties
         final Map<PropertyDescriptor, String> sortedProperties = new TreeMap<>(new Comparator<PropertyDescriptor>() {
@@ -2056,30 +2080,109 @@ public final class DtoFactory {
     }
 
     /**
+     * Creates a bundle DTO from the specified class.
+     *
+     * @param coordinate bundle coordinates
+     * @return dto
+     */
+    public BundleDTO createBundleDto(final BundleCoordinate coordinate) {
+        final BundleDTO dto = new BundleDTO();
+        dto.setGroup(coordinate.getGroup());
+        dto.setArtifact(coordinate.getId());
+        dto.setVersion(coordinate.getVersion());
+        return dto;
+    }
+
+    private List<ControllerServiceApiDTO> createControllerServiceApiDto(final Class cls) {
+        final Set<Class> serviceApis = new HashSet<>();
+
+        // if this is a controller service
+        if (ControllerService.class.isAssignableFrom(cls)) {
+            // get all of it's interfaces to determine the controller service api's it implements
+            final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(cls);
+            for (final Class i : interfaces) {
+                // add all controller services that's not ControllerService itself
+                if (ControllerService.class.isAssignableFrom(i) && !ControllerService.class.equals(i)) {
+                    serviceApis.add(i);
+                }
+            }
+
+            final List<ControllerServiceApiDTO> dtos = new ArrayList<>();
+            for (final Class serviceApi : serviceApis) {
+                final Bundle bundle = ExtensionManager.getBundle(serviceApi.getClassLoader());
+                final BundleCoordinate bundleCoordinate = bundle.getBundleDetails().getCoordinate();
+
+                final ControllerServiceApiDTO dto = new ControllerServiceApiDTO();
+                dto.setType(serviceApi.getName());
+                dto.setBundle(createBundleDto(bundleCoordinate));
+                dtos.add(dto);
+            }
+            return dtos;
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * Gets the DocumentedTypeDTOs from the specified classes.
      *
      * @param classes classes
+     * @param bundleGroupFilter if specified, must be member of bundle group
+     * @param bundleArtifactFilter if specified, must be member of bundle artifact
+     * @param typeFilter if specified, type must match
      * @return dtos
      */
-    @SuppressWarnings("rawtypes")
-    public Set<DocumentedTypeDTO> fromDocumentedTypes(final Set<Class> classes) {
+    public Set<DocumentedTypeDTO> fromDocumentedTypes(final Map<Class, Bundle> classes, final String bundleGroupFilter, final String bundleArtifactFilter, final String typeFilter) {
         final Set<DocumentedTypeDTO> types = new LinkedHashSet<>();
-        final Set<Class> sortedClasses = new TreeSet<>(CLASS_NAME_COMPARATOR);
-        sortedClasses.addAll(classes);
+        final List<Class> sortedClasses = new ArrayList<>(classes.keySet());
+        Collections.sort(sortedClasses, CLASS_NAME_COMPARATOR);
 
-        for (final Class<?> cls : sortedClasses) {
-            final DocumentedTypeDTO type = new DocumentedTypeDTO();
-            type.setType(cls.getName());
-            type.setDescription(getCapabilityDescription(cls));
-            type.setUsageRestriction(getUsageRestriction(cls));
-            type.setTags(getTags(cls));
-            types.add(type);
+        for (final Class cls : sortedClasses) {
+            final Bundle bundle = classes.get(cls);
+            final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate();
+
+            // only include classes that meet the criteria if specified
+            if (bundleGroupFilter != null && !bundleGroupFilter.equals(coordinate.getGroup())) {
+                continue;
+            }
+            if (bundleArtifactFilter != null && !bundleArtifactFilter.equals(coordinate.getId())) {
+                continue;
+            }
+            if (typeFilter != null && !typeFilter.equals(cls.getName())) {
+                continue;
+            }
+
+            final DocumentedTypeDTO dto = new DocumentedTypeDTO();
+            dto.setType(cls.getName());
+            dto.setBundle(createBundleDto(coordinate));
+            dto.setControllerServiceApis(createControllerServiceApiDto(cls));
+            dto.setDescription(getCapabilityDescription(cls));
+            dto.setUsageRestriction(getUsageRestriction(cls));
+            dto.setTags(getTags(cls));
+            types.add(dto);
         }
 
         return types;
     }
 
     /**
+     * Gets the DocumentedTypeDTOs from the specified classes.
+     *
+     * @param classes classes
+     * @param bundleGroupFilter if specified, must be member of bundle group
+     * @param bundleArtifactFilter if specified, must be member of bundle artifact
+     * @param typeFilter if specified, type must match
+     * @return dtos
+     */
+    public Set<DocumentedTypeDTO> fromDocumentedTypes(final Set<Class> classes, final String bundleGroupFilter, final String bundleArtifactFilter, final String typeFilter) {
+        final Map<Class, Bundle> classBundles = new HashMap<>();
+        for (final Class cls : classes) {
+            classBundles.put(cls, ExtensionManager.getBundle(cls.getClassLoader()));
+        }
+        return fromDocumentedTypes(classBundles, bundleGroupFilter, bundleArtifactFilter, typeFilter);
+    }
+
+    /**
      * Creates a ProcessorDTO from the specified ProcessorNode.
      *
      * @param node node
@@ -2090,6 +2193,12 @@ public final class DtoFactory {
             return null;
         }
 
+        final BundleCoordinate bundleCoordinate = node.getBundleCoordinate();
+        final List<Bundle> compatibleBundles = ExtensionManager.getBundles(node.getCanonicalClassName()).stream().filter(bundle -> {
+            final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate();
+            return bundleCoordinate.getGroup().equals(coordinate.getGroup()) && bundleCoordinate.getId().equals(coordinate.getId());
+        }).collect(Collectors.toList());
+
         final ProcessorDTO dto = new ProcessorDTO();
         dto.setId(node.getIdentifier());
         dto.setPosition(createPositionDto(node.getPosition()));
@@ -2098,8 +2207,11 @@ public final class DtoFactory {
         dto.setInputRequirement(node.getInputRequirement().name());
         dto.setPersistsState(node.getProcessor().getClass().isAnnotationPresent(Stateful.class));
         dto.setRestricted(node.isRestricted());
+        dto.setExtensionMissing(node.isExtensionMissing());
+        dto.setMultipleVersionsAvailable(compatibleBundles.size() > 1);
 
         dto.setType(node.getCanonicalClassName());
+        dto.setBundle(createBundleDto(bundleCoordinate));
         dto.setName(node.getName());
         dto.setState(node.getScheduledState().toString());
 
@@ -2466,16 +2578,25 @@ public final class DtoFactory {
 
     public SystemDiagnosticsSnapshotDTO.VersionInfoDTO createVersionInfoDTO() {
         final SystemDiagnosticsSnapshotDTO.VersionInfoDTO dto = new SystemDiagnosticsSnapshotDTO.VersionInfoDTO();
-        dto.setNiFiVersion(properties.getUiTitle());
         dto.setJavaVendor(System.getProperty("java.vendor"));
         dto.setJavaVersion(System.getProperty("java.version"));
         dto.setOsName(System.getProperty("os.name"));
         dto.setOsVersion(System.getProperty("os.version"));
         dto.setOsArchitecture(System.getProperty("os.arch"));
-        dto.setBuildTag(properties.getProperty(NiFiProperties.BUILD_TAG));
-        dto.setBuildRevision(properties.getProperty(NiFiProperties.BUILD_REVISION));
-        dto.setBuildBranch(properties.getProperty(NiFiProperties.BUILD_BRANCH));
-        dto.setBuildTimestamp(properties.getBuildTimestamp());
+
+        final Bundle frameworkBundle = NarClassLoaders.getInstance().getFrameworkBundle();
+        if (frameworkBundle != null) {
+            final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails();
+
+            dto.setNiFiVersion(frameworkDetails.getCoordinate().getVersion());
+
+            // Get build info
+            dto.setBuildTag(frameworkDetails.getBuildTag());
+            dto.setBuildRevision(frameworkDetails.getBuildRevision());
+            dto.setBuildBranch(frameworkDetails.getBuildBranch());
+            dto.setBuildTimestamp(frameworkDetails.getBuildTimestampDate());
+        }
+
         return dto;
     }
 
@@ -2598,7 +2719,11 @@ public final class DtoFactory {
 
         // set the identifies controller service is applicable
         if (propertyDescriptor.getControllerServiceDefinition() != null) {
-            dto.setIdentifiesControllerService(propertyDescriptor.getControllerServiceDefinition().getName());
+            final Class serviceClass = propertyDescriptor.getControllerServiceDefinition();
+            final Bundle serviceBundle = ExtensionManager.getBundle(serviceClass.getClassLoader());
+
+            dto.setIdentifiesControllerService(serviceClass.getName());
+            dto.setIdentifiesControllerServiceBundle(createBundleDto(serviceBundle.getBundleDetails().getCoordinate()));
         }
 
         final Class<? extends ControllerService> serviceDefinition = propertyDescriptor.getControllerServiceDefinition();
@@ -2654,6 +2779,7 @@ public final class DtoFactory {
     public ControllerServiceDTO copy(final ControllerServiceDTO original) {
         final ControllerServiceDTO copy = new ControllerServiceDTO();
         copy.setAnnotationData(original.getAnnotationData());
+        copy.setControllerServiceApis(original.getControllerServiceApis());
         copy.setComments(original.getComments());
         copy.setCustomUiUrl(original.getCustomUiUrl());
         copy.setDescriptors(copy(original.getDescriptors()));
@@ -2664,6 +2790,10 @@ public final class DtoFactory {
         copy.setReferencingComponents(copy(original.getReferencingComponents()));
         copy.setState(original.getState());
         copy.setType(original.getType());
+        copy.setBundle(copy(original.getBundle()));
+        copy.setExtensionMissing(original.getExtensionMissing());
+        copy.setMultipleVersionsAvailable(original.getMultipleVersionsAvailable());
+        copy.setPersistsState(original.getPersistsState());
         copy.setValidationErrors(copy(original.getValidationErrors()));
         return copy;
     }
@@ -2709,6 +2839,18 @@ public final class DtoFactory {
         }
     }
 
+    public BundleDTO copy(final BundleDTO original) {
+        if (original == null) {
+            return null;
+        }
+
+        final BundleDTO copy = new BundleDTO();
+        copy.setGroup(original.getGroup());
+        copy.setArtifact(original.getArtifact());
+        copy.setVersion(original.getVersion());
+        return copy;
+    }
+
     public ProcessorDTO copy(final ProcessorDTO original) {
         final ProcessorDTO copy = new ProcessorDTO();
         copy.setConfig(copy(original.getConfig()));
@@ -2721,8 +2863,13 @@ public final class DtoFactory {
         copy.setState(original.getState());
         copy.setStyle(copy(original.getStyle()));
         copy.setType(original.getType());
+        copy.setBundle(copy(original.getBundle()));
         copy.setSupportsParallelProcessing(original.getSupportsParallelProcessing());
         copy.setSupportsEventDriven(original.getSupportsEventDriven());
+        copy.setSupportsBatching(original.getSupportsBatching());
+        copy.setPersistsState(original.getPersistsState());
+        copy.setExtensionMissing(original.getExtensionMissing());
+        copy.setMultipleVersionsAvailable(original.getMultipleVersionsAvailable());
         copy.setValidationErrors(copy(original.getValidationErrors()));
 
         return copy;