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 2017/04/06 17:51:53 UTC

[6/7] nifi git commit: NIFI-3520 Refactoring instance class loading - Fixing FlowController to use appropriate class loader when instantiating processor - Updating ExtensionManager to leverage new flag in MANIFEST from NAR plugin - Adding ReloadComponent

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
index 3bbe9e3..1db80fc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/fingerprint/FingerprintFactory.java
@@ -20,17 +20,13 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.FlowController;
-import org.apache.nifi.controller.exception.ProcessorInstantiationException;
 import org.apache.nifi.controller.serialization.FlowFromDOMFactory;
-import org.apache.nifi.util.LoggingXmlParserErrorHandler;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.nar.ExtensionManager;
-import org.apache.nifi.processor.Processor;
-import org.apache.nifi.reporting.ReportingTask;
 import org.apache.nifi.util.BundleUtils;
 import org.apache.nifi.util.DomUtils;
+import org.apache.nifi.util.LoggingXmlParserErrorHandler;
 import org.apache.nifi.web.api.dto.BundleDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.ReportingTaskDTO;
@@ -58,7 +54,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.UUID;
 
 /**
  * <p>Creates a fingerprint of a flow.xml. The order of elements or attributes in the flow.xml does not influence the fingerprint generation.
@@ -232,7 +227,7 @@ public class FingerprintFactory {
             });
 
             for (final ControllerServiceDTO dto : serviceDtos) {
-                addControllerServiceFingerprint(builder, dto, controller);
+                addControllerServiceFingerprint(builder, dto);
             }
         }
 
@@ -262,7 +257,7 @@ public class FingerprintFactory {
             });
 
             for (final ReportingTaskDTO dto : reportingTaskDtos) {
-                addReportingTaskFingerprint(builder, dto, controller);
+                addReportingTaskFingerprint(builder, dto);
             }
         }
 
@@ -277,7 +272,7 @@ public class FingerprintFactory {
         final List<Element> processorElems = DomUtils.getChildElementsByTagName(processGroupElem, "processor");
         Collections.sort(processorElems, getIdsComparator());
         for (final Element processorElem : processorElems) {
-            addFlowFileProcessorFingerprint(builder, processorElem, controller);
+            addFlowFileProcessorFingerprint(builder, processorElem);
         }
 
         // input ports
@@ -332,7 +327,7 @@ public class FingerprintFactory {
         return builder;
     }
 
-    private StringBuilder addFlowFileProcessorFingerprint(final StringBuilder builder, final Element processorElem, final FlowController controller) throws FingerprintException {
+    private StringBuilder addFlowFileProcessorFingerprint(final StringBuilder builder, final Element processorElem) throws FingerprintException {
         // id
         appendFirstValue(builder, DomUtils.getChildNodesByTagName(processorElem, "id"));
         // class
@@ -346,24 +341,11 @@ public class FingerprintFactory {
         final BundleDTO bundle = FlowFromDOMFactory.getBundle(DomUtils.getChild(processorElem, "bundle"));
         addBundleFingerprint(builder, bundle);
 
-        // create an instance of the Processor so that we know the default property values
-        Processor processor = null;
-        try {
-            if (controller != null) {
-                final BundleCoordinate coordinate = getCoordinate(className, bundle);
-                processor = controller.createProcessor(className, UUID.randomUUID().toString(), coordinate, false).getProcessor();
-            }
-        } catch (ProcessorInstantiationException | IllegalStateException e) {
-            logger.warn("Unable to create Processor of type {} due to {}; its default properties will be fingerprinted instead of being ignored.", className, e.toString());
-            if (logger.isDebugEnabled()) {
-                logger.warn("", e);
-            }
-        } finally {
-            // The processor instance is only for fingerprinting so we can remove the InstanceClassLoader here
-            // since otherwise it will stick around in the map forever
-            if (processor != null) {
-                ExtensionManager.removeInstanceClassLoaderIfExists(processor.getIdentifier());
-            }
+        // get the temp instance of the Processor so that we know the default property values
+        final BundleCoordinate coordinate = getCoordinate(className, bundle);
+        final ConfigurableComponent configurableComponent = ExtensionManager.getTempComponent(className, coordinate);
+        if (configurableComponent == null) {
+            logger.warn("Unable to get Processor of type {}; its default properties will be fingerprinted instead of being ignored.", className);
         }
 
         // properties
@@ -372,7 +354,7 @@ public class FingerprintFactory {
         for (final Element propertyElem : sortedPropertyElems) {
             final String propName = DomUtils.getChildElementsByTagName(propertyElem, "name").get(0).getTextContent();
             String propValue = getFirstValue(DomUtils.getChildNodesByTagName(propertyElem, "value"), null);
-            addPropertyFingerprint(builder, processor, propName, propValue);
+            addPropertyFingerprint(builder, configurableComponent, propName, propValue);
         }
 
         final NodeList autoTerminateElems = DomUtils.getChildNodesByTagName(processorElem, "autoTerminatedRelationship");
@@ -571,7 +553,7 @@ public class FingerprintFactory {
         return builder;
     }
 
-    private void addControllerServiceFingerprint(final StringBuilder builder, final ControllerServiceDTO dto, final FlowController controller) {
+    private void addControllerServiceFingerprint(final StringBuilder builder, final ControllerServiceDTO dto) {
         builder.append(dto.getId());
         builder.append(dto.getType());
         builder.append(dto.getName());
@@ -582,21 +564,14 @@ public class FingerprintFactory {
         builder.append(dto.getAnnotationData());
         builder.append(dto.getState());
 
-        // create an instance of the ControllerService so that we know the default property values
-        ControllerService controllerService = null;
-        try {
-            if (controller != null) {
-                final BundleCoordinate coordinate = getCoordinate(dto.getType(), dto.getBundle());
-                controllerService = controller.createControllerService(dto.getType(), UUID.randomUUID().toString(), coordinate, false).getControllerServiceImplementation();
-            }
-        } catch (Exception e) {
-            logger.warn("Unable to create ControllerService of type {} due to {}; its default properties will be fingerprinted instead of being ignored.", dto.getType(), e.toString());
-            if (logger.isDebugEnabled()) {
-                logger.warn("", e);
-            }
+        // get the temp instance of the ControllerService so that we know the default property values
+        final BundleCoordinate coordinate = getCoordinate(dto.getType(), dto.getBundle());
+        final ConfigurableComponent configurableComponent = ExtensionManager.getTempComponent(dto.getType(), coordinate);
+        if (configurableComponent == null) {
+            logger.warn("Unable to get ControllerService of type {}; its default properties will be fingerprinted instead of being ignored.", dto.getType());
         }
 
-        addPropertiesFingerprint(builder, controllerService, dto.getProperties());
+        addPropertiesFingerprint(builder, configurableComponent, dto.getProperties());
     }
 
     private void addPropertiesFingerprint(final StringBuilder builder, final ConfigurableComponent component, final Map<String, String> properties) {
@@ -634,7 +609,7 @@ public class FingerprintFactory {
         return coordinate;
     }
 
-    private void addReportingTaskFingerprint(final StringBuilder builder, final ReportingTaskDTO dto, final FlowController controller) {
+    private void addReportingTaskFingerprint(final StringBuilder builder, final ReportingTaskDTO dto) {
         builder.append(dto.getId());
         builder.append(dto.getType());
         builder.append(dto.getName());
@@ -646,21 +621,14 @@ public class FingerprintFactory {
         builder.append(dto.getSchedulingStrategy());
         builder.append(dto.getAnnotationData());
 
-        // create an instance of the ReportingTask so that we know the default property values
-        ReportingTask reportingTask = null;
-        try {
-            if (controller != null) {
-                final BundleCoordinate coordinate = getCoordinate(dto.getType(), dto.getBundle());
-                reportingTask = controller.createReportingTask(dto.getType(), UUID.randomUUID().toString(), coordinate, false, false).getReportingTask();
-            }
-        } catch (Exception e) {
-            logger.warn("Unable to create ReportingTask of type {} due to {}; its default properties will be fingerprinted instead of being ignored.", dto.getType(), e.toString());
-            if (logger.isDebugEnabled()) {
-                logger.warn("", e);
-            }
+        // get the temp instance of the ReportingTask so that we know the default property values
+        final BundleCoordinate coordinate = getCoordinate(dto.getType(), dto.getBundle());
+        final ConfigurableComponent configurableComponent = ExtensionManager.getTempComponent(dto.getType(), coordinate);
+        if (configurableComponent == null) {
+            logger.warn("Unable to get ReportingTask of type {}; its default properties will be fingerprinted instead of being ignored.", dto.getType());
         }
 
-        addPropertiesFingerprint(builder, reportingTask, dto.getProperties());
+        addPropertiesFingerprint(builder, configurableComponent, dto.getProperties());
     }
 
     private Comparator<Element> getIdsComparator() {

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
index 8d8fd19..452f3cc 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/groups/StandardProcessGroup.java
@@ -767,7 +767,7 @@ public final class StandardProcessGroup implements ProcessGroup {
         } finally {
             if (removed) {
                 try {
-                    ExtensionManager.removeInstanceClassLoaderIfExists(id);
+                    ExtensionManager.removeInstanceClassLoader(id);
                 } catch (Throwable t) {
                 }
             }
@@ -1914,7 +1914,7 @@ public final class StandardProcessGroup implements ProcessGroup {
         } finally {
             if (removed) {
                 try {
-                    ExtensionManager.removeInstanceClassLoaderIfExists(service.getIdentifier());
+                    ExtensionManager.removeInstanceClassLoader(service.getIdentifier());
                 } catch (Throwable t) {
                 }
             }

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/StandardFlowSynchronizerSpec.groovy
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/StandardFlowSynchronizerSpec.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/StandardFlowSynchronizerSpec.groovy
index 738b25d..098814a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/StandardFlowSynchronizerSpec.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/StandardFlowSynchronizerSpec.groovy
@@ -26,6 +26,7 @@ import org.apache.nifi.controller.queue.FlowFileQueue
 import org.apache.nifi.groups.ProcessGroup
 import org.apache.nifi.groups.RemoteProcessGroup
 import org.apache.nifi.nar.ExtensionManager
+import org.apache.nifi.nar.SystemBundle
 import org.apache.nifi.processor.Relationship
 import org.apache.nifi.reporting.BulletinRepository
 import org.apache.nifi.util.NiFiProperties
@@ -43,7 +44,7 @@ class StandardFlowSynchronizerSpec extends Specification {
         System.setProperty NiFiProperties.PROPERTIES_FILE_PATH, propFile
 
         def niFiProperties = NiFiProperties.createBasicNiFiProperties(null, null);
-        systemBundle = ExtensionManager.createSystemBundle(niFiProperties);
+        systemBundle = SystemBundle.create(niFiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestFlowController.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestFlowController.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestFlowController.java
index 557bd62..1b51be6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestFlowController.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestFlowController.java
@@ -44,6 +44,8 @@ import org.apache.nifi.logging.LogLevel;
 import org.apache.nifi.logging.LogRepository;
 import org.apache.nifi.logging.LogRepositoryFactory;
 import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.InstanceClassLoader;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.provenance.MockProvenanceRepository;
 import org.apache.nifi.registry.VariableRegistry;
@@ -67,6 +69,8 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -115,7 +119,7 @@ public class TestFlowController {
         encryptor = StringEncryptor.createEncryptor(nifiProperties);
 
         // use the system bundle
-        systemBundle = ExtensionManager.createSystemBundle(nifiProperties);
+        systemBundle = SystemBundle.create(nifiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         User user1 = new User.Builder().identifier("user-id-1").identity("user-1").build();
@@ -465,7 +469,7 @@ public class TestFlowController {
     @Test
     public void testCreateMissingControllerService() throws ProcessorInstantiationException {
         final ControllerServiceNode serviceNode = controller.createControllerService("org.apache.nifi.NonExistingControllerService", "1234-Controller-Service",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         assertNotNull(serviceNode);
         assertEquals("org.apache.nifi.NonExistingControllerService", serviceNode.getCanonicalClassName());
         assertEquals("(Missing) NonExistingControllerService", serviceNode.getComponentType());
@@ -518,7 +522,7 @@ public class TestFlowController {
         ProcessGroup pg = controller.createProcessGroup("my-process-group");
         pg.setName("my-process-group");
         ControllerServiceNode cs = controller.createControllerService("org.apache.nifi.NonExistingControllerService", "my-controller-service",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         pg.addControllerService(cs);
         controller.getRootGroup().addProcessGroup(pg);
         controller.getRootGroup().removeProcessGroup(pg);
@@ -527,7 +531,7 @@ public class TestFlowController {
     }
 
     @Test
-    public void testChangeProcessorType() throws ProcessorInstantiationException {
+    public void testReloadProcessor() throws ProcessorInstantiationException {
         final String id = "1234-ScheduledProcessor" + System.currentTimeMillis();
         final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
         final ProcessorNode processorNode = controller.createProcessor(DummyScheduledProcessor.class.getName(), id, coordinate);
@@ -548,7 +552,7 @@ public class TestFlowController {
         assertEquals(LogLevel.WARN, processorNode.getBulletinLevel());
 
         // now change the type of the processor from DummyScheduledProcessor to DummySettingsProcessor
-        controller.changeProcessorType(processorNode, DummySettingsProcessor.class.getName(), coordinate);
+        controller.reload(processorNode, DummySettingsProcessor.class.getName(), coordinate, Collections.emptySet());
 
         // ids and coordinate should stay the same
         assertEquals(id, processorNode.getIdentifier());
@@ -573,10 +577,42 @@ public class TestFlowController {
     }
 
     @Test
-    public void testChangeControllerServiceType() {
+    public void testReloadProcessorWithAdditionalResources() throws ProcessorInstantiationException, MalformedURLException {
+        final URL resource1 = new File("src/test/resources/TestClasspathResources/resource1.txt").toURI().toURL();
+        final URL resource2 = new File("src/test/resources/TestClasspathResources/resource2.txt").toURI().toURL();
+        final URL resource3 = new File("src/test/resources/TestClasspathResources/resource3.txt").toURI().toURL();
+        final Set<URL> additionalUrls = new LinkedHashSet<>(Arrays.asList(resource1, resource2, resource3));
+
+        final String id = "1234-ScheduledProcessor" + System.currentTimeMillis();
+        final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
+        final ProcessorNode processorNode = controller.createProcessor(DummyScheduledProcessor.class.getName(), id, coordinate);
+        final String originalName = processorNode.getName();
+
+        // the instance class loader shouldn't have any of the resources yet
+        InstanceClassLoader instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertTrue(instanceClassLoader.getAdditionalResourceUrls().isEmpty());
+
+        // now change the type of the processor from DummyScheduledProcessor to DummySettingsProcessor
+        controller.reload(processorNode, DummySettingsProcessor.class.getName(), coordinate, additionalUrls);
+
+        // the instance class loader shouldn't have any of the resources yet
+        instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertEquals(3, instanceClassLoader.getAdditionalResourceUrls().size());
+    }
+
+    @Test
+    public void testReloadControllerService() {
         final String id = "ServiceA" + System.currentTimeMillis();
         final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
-        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, true);
+        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, null, true);
         final String originalName = controllerServiceNode.getName();
 
         assertEquals(id, controllerServiceNode.getIdentifier());
@@ -586,7 +622,7 @@ public class TestFlowController {
         assertEquals(ServiceA.class.getSimpleName(), controllerServiceNode.getComponentType());
         assertEquals(ServiceA.class.getCanonicalName(), controllerServiceNode.getComponent().getClass().getCanonicalName());
 
-        controller.changeControllerServiceType(controllerServiceNode, ServiceB.class.getName(), coordinate);
+        controller.reload(controllerServiceNode, ServiceB.class.getName(), coordinate, Collections.emptySet());
 
         // ids and coordinate should stay the same
         assertEquals(id, controllerServiceNode.getIdentifier());
@@ -603,7 +639,38 @@ public class TestFlowController {
     }
 
     @Test
-    public void testChangeReportingTaskType() throws ReportingTaskInstantiationException {
+    public void testReloadControllerServiceWithAdditionalResources() throws MalformedURLException {
+        final URL resource1 = new File("src/test/resources/TestClasspathResources/resource1.txt").toURI().toURL();
+        final URL resource2 = new File("src/test/resources/TestClasspathResources/resource2.txt").toURI().toURL();
+        final URL resource3 = new File("src/test/resources/TestClasspathResources/resource3.txt").toURI().toURL();
+        final Set<URL> additionalUrls = new LinkedHashSet<>(Arrays.asList(resource1, resource2, resource3));
+
+        final String id = "ServiceA" + System.currentTimeMillis();
+        final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
+        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, null, true);
+        final String originalName = controllerServiceNode.getName();
+
+        // the instance class loader shouldn't have any of the resources yet
+        InstanceClassLoader instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertTrue(instanceClassLoader.getAdditionalResourceUrls().isEmpty());
+
+        controller.reload(controllerServiceNode, ServiceB.class.getName(), coordinate, additionalUrls);
+
+        // the instance class loader shouldn't have any of the resources yet
+        instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertEquals(3, instanceClassLoader.getAdditionalResourceUrls().size());
+    }
+
+    @Test
+    public void testReloadReportingTask() throws ReportingTaskInstantiationException {
         final String id = "ReportingTask" + System.currentTimeMillis();
         final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
         final ReportingTaskNode node = controller.createReportingTask(DummyReportingTask.class.getName(), id, coordinate, true);
@@ -616,7 +683,7 @@ public class TestFlowController {
         assertEquals(DummyReportingTask.class.getSimpleName(), node.getComponentType());
         assertEquals(DummyReportingTask.class.getCanonicalName(), node.getComponent().getClass().getCanonicalName());
 
-        controller.changeReportingTaskType(node, DummyScheduledReportingTask.class.getName(), coordinate);
+        controller.reload(node, DummyScheduledReportingTask.class.getName(), coordinate, Collections.emptySet());
 
         // ids and coordinate should stay the same
         assertEquals(id, node.getIdentifier());
@@ -630,7 +697,45 @@ public class TestFlowController {
         assertEquals(DummyReportingTask.class.getCanonicalName(), node.getCanonicalClassName());
         assertEquals(DummyReportingTask.class.getSimpleName(), node.getComponentType());
         assertEquals(DummyScheduledReportingTask.class.getCanonicalName(), node.getComponent().getClass().getCanonicalName());
+    }
+
+    @Test
+    public void testReloadReportingTaskWithAdditionalResources() throws ReportingTaskInstantiationException, MalformedURLException {
+        final URL resource1 = new File("src/test/resources/TestClasspathResources/resource1.txt").toURI().toURL();
+        final URL resource2 = new File("src/test/resources/TestClasspathResources/resource2.txt").toURI().toURL();
+        final URL resource3 = new File("src/test/resources/TestClasspathResources/resource3.txt").toURI().toURL();
+        final Set<URL> additionalUrls = new LinkedHashSet<>(Arrays.asList(resource1, resource2, resource3));
 
+        final String id = "ReportingTask" + System.currentTimeMillis();
+        final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
+        final ReportingTaskNode node = controller.createReportingTask(DummyReportingTask.class.getName(), id, coordinate, true);
+
+        // the instance class loader shouldn't have any of the resources yet
+        InstanceClassLoader instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertFalse(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertTrue(instanceClassLoader.getAdditionalResourceUrls().isEmpty());
+
+        controller.reload(node, DummyScheduledReportingTask.class.getName(), coordinate, additionalUrls);
+
+        // the instance class loader shouldn't have any of the resources yet
+        instanceClassLoader = ExtensionManager.getInstanceClassLoader(id);
+        assertNotNull(instanceClassLoader);
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource1));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource2));
+        assertTrue(containsResource(instanceClassLoader.getURLs(), resource3));
+        assertEquals(3, instanceClassLoader.getAdditionalResourceUrls().size());
+    }
+
+    private boolean containsResource(URL[] resources, URL resourceToFind) {
+        for (URL resource : resources) {
+            if (resourceToFind.getPath().equals(resource.getPath())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -744,7 +849,7 @@ public class TestFlowController {
     public void testInstantiateSnippetWhenControllerServiceMissingBundle() throws ProcessorInstantiationException {
         final String id = UUID.randomUUID().toString();
         final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
-        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, true);
+        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, null, true);
 
         // create the controller service dto
         final ControllerServiceDTO csDto = new ControllerServiceDTO();
@@ -775,7 +880,7 @@ public class TestFlowController {
     public void testInstantiateSnippetWithControllerService() throws ProcessorInstantiationException {
         final String id = UUID.randomUUID().toString();
         final BundleCoordinate coordinate = systemBundle.getBundleDetails().getCoordinate();
-        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, true);
+        final ControllerServiceNode controllerServiceNode = controller.createControllerService(ServiceA.class.getName(), id, coordinate, null, true);
 
         // create the controller service dto
         final ControllerServiceDTO csDto = new ControllerServiceDTO();

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java
index a86b7b3..c248d25 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java
@@ -25,12 +25,16 @@ import org.apache.nifi.bundle.BundleCoordinate;
 import org.apache.nifi.components.PropertyDescriptor;
 import org.apache.nifi.components.PropertyValue;
 import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.controller.exception.ControllerServiceInstantiationException;
+import org.apache.nifi.controller.exception.ProcessorInstantiationException;
+import org.apache.nifi.controller.reporting.ReportingTaskInstantiationException;
+import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.engine.FlowEngine;
 import org.apache.nifi.expression.ExpressionLanguageCompiler;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.nar.ExtensionManager;
-import org.apache.nifi.nar.InstanceClassLoader;
 import org.apache.nifi.nar.NarCloseable;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
@@ -55,10 +59,10 @@ import org.mockito.Mockito;
 import java.io.File;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
@@ -88,11 +92,12 @@ public class TestStandardProcessorNode {
         ProcessorInitializationContext initContext = new StandardProcessorInitializationContext(uuid, null, null, null, null);
         processor.initialize(initContext);
 
+        final ReloadComponent reloadComponent = Mockito.mock(ReloadComponent.class);
         final BundleCoordinate coordinate = Mockito.mock(BundleCoordinate.class);
 
         final LoggableComponent<Processor> loggableComponent = new LoggableComponent<>(processor, coordinate, null);
         final StandardProcessorNode procNode = new StandardProcessorNode(loggableComponent, uuid, createValidationContextFactory(), null, null,
-                NiFiProperties.createBasicNiFiProperties(null, null), VariableRegistry.EMPTY_REGISTRY);
+                NiFiProperties.createBasicNiFiProperties(null, null), VariableRegistry.EMPTY_REGISTRY, reloadComponent);
         final ScheduledExecutorService taskScheduler = new FlowEngine(2, "TestClasspathResources", true);
 
         final StandardProcessContext processContext = new StandardProcessContext(procNode, null, null, null, null);
@@ -122,8 +127,9 @@ public class TestStandardProcessorNode {
 
     @Test
     public void testDisabledValidationErrors() {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
         final ModifiesClasspathNoAnnotationProcessor processor = new ModifiesClasspathNoAnnotationProcessor();
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         // Set a property to an invalid value
         final Map<String, String> properties = new HashMap<>();
@@ -138,22 +144,19 @@ public class TestStandardProcessorNode {
 
     @Test
     public void testSinglePropertyDynamicallyModifiesClasspath() throws MalformedURLException {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
+
         final PropertyDescriptor classpathProp = new PropertyDescriptor.Builder().name("Classpath Resources")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
         final ModifiesClasspathProcessor processor = new ModifiesClasspathProcessor(Arrays.asList(classpathProp));
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(procNode.getProcessor().getClass(), procNode.getIdentifier())){
-            // Should have an InstanceClassLoader here
-            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-            assertTrue(contextClassLoader instanceof InstanceClassLoader);
-
-            final InstanceClassLoader instanceClassLoader = (InstanceClassLoader) contextClassLoader;
 
             // Should not have any of the test resources loaded at this point
             final URL[] testResources = getTestResources();
             for (URL testResource : testResources) {
-                if (containsResource(instanceClassLoader.getInstanceResources(), testResource)) {
+                if (containsResource(reloadComponent.getAdditionalUrls(), testResource)) {
                     fail("found resource that should not have been loaded");
                 }
             }
@@ -165,18 +168,22 @@ public class TestStandardProcessorNode {
 
             // Should have all of the resources loaded into the InstanceClassLoader now
             for (URL testResource : testResources) {
-                assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResource));
+                assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResource));
             }
 
+            assertEquals(ModifiesClasspathProcessor.class.getCanonicalName(), reloadComponent.getNewType());
+
             // Should pass validation
             assertTrue(procNode.isValid());
         } finally {
-            ExtensionManager.removeInstanceClassLoaderIfExists(procNode.getIdentifier());
+            ExtensionManager.removeInstanceClassLoader(procNode.getIdentifier());
         }
     }
 
     @Test
     public void testUpdateOtherPropertyDoesNotImpactClasspath() throws MalformedURLException {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
+
         final PropertyDescriptor classpathProp = new PropertyDescriptor.Builder().name("Classpath Resources")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
 
@@ -184,19 +191,13 @@ public class TestStandardProcessorNode {
                 .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
 
         final ModifiesClasspathProcessor processor = new ModifiesClasspathProcessor(Arrays.asList(classpathProp, otherProp));
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(procNode.getProcessor().getClass(), procNode.getIdentifier())){
-            // Should have an InstanceClassLoader here
-            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-            assertTrue(contextClassLoader instanceof InstanceClassLoader);
-
-            final InstanceClassLoader instanceClassLoader = (InstanceClassLoader) contextClassLoader;
-
             // Should not have any of the test resources loaded at this point
             final URL[] testResources = getTestResources();
             for (URL testResource : testResources) {
-                if (containsResource(instanceClassLoader.getInstanceResources(), testResource)) {
+                if (containsResource(reloadComponent.getAdditionalUrls(), testResource)) {
                     fail("found resource that should not have been loaded");
                 }
             }
@@ -208,7 +209,7 @@ public class TestStandardProcessorNode {
 
             // Should have all of the resources loaded into the InstanceClassLoader now
             for (URL testResource : testResources) {
-                assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResource));
+                assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResource));
             }
 
             // Should pass validation
@@ -221,7 +222,7 @@ public class TestStandardProcessorNode {
 
             // Should STILL have all of the resources loaded into the InstanceClassLoader now
             for (URL testResource : testResources) {
-                assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResource));
+                assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResource));
             }
 
             // Should STILL pass validation
@@ -233,38 +234,37 @@ public class TestStandardProcessorNode {
             procNode.setProperties(newClasspathProperties);
 
             // Should only have resource1 loaded now
-            assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResources[0]));
-            assertFalse(containsResource(instanceClassLoader.getInstanceResources(), testResources[1]));
-            assertFalse(containsResource(instanceClassLoader.getInstanceResources(), testResources[2]));
+            assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResources[0]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[1]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[2]));
+
+            assertEquals(ModifiesClasspathProcessor.class.getCanonicalName(), reloadComponent.getNewType());
 
             // Should STILL pass validation
             assertTrue(procNode.isValid());
         } finally {
-            ExtensionManager.removeInstanceClassLoaderIfExists(procNode.getIdentifier());
+            ExtensionManager.removeInstanceClassLoader(procNode.getIdentifier());
         }
     }
 
     @Test
     public void testMultiplePropertiesDynamicallyModifyClasspathWithExpressionLanguage() throws MalformedURLException {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
+
         final PropertyDescriptor classpathProp1 = new PropertyDescriptor.Builder().name("Classpath Resource 1")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
         final PropertyDescriptor classpathProp2 = new PropertyDescriptor.Builder().name("Classpath Resource 2")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
 
         final ModifiesClasspathProcessor processor = new ModifiesClasspathProcessor(Arrays.asList(classpathProp1, classpathProp2));
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(procNode.getProcessor().getClass(), procNode.getIdentifier())){
-            // Should have an InstanceClassLoader here
-            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-            assertTrue(contextClassLoader instanceof InstanceClassLoader);
-
-            final InstanceClassLoader instanceClassLoader = (InstanceClassLoader) contextClassLoader;
 
             // Should not have any of the test resources loaded at this point
             final URL[] testResources = getTestResources();
             for (URL testResource : testResources) {
-                if (containsResource(instanceClassLoader.getInstanceResources(), testResource)) {
+                if (containsResource(reloadComponent.getAdditionalUrls(), testResource)) {
                     fail("found resource that should not have been loaded");
                 }
             }
@@ -279,38 +279,37 @@ public class TestStandardProcessorNode {
             procNode.setProperties(properties);
 
             // Should have resources 1 and 3 loaded into the InstanceClassLoader now
-            assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResources[0]));
-            assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResources[2]));
-            assertFalse(containsResource(instanceClassLoader.getInstanceResources(), testResources[1]));
+            assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResources[0]));
+            assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResources[2]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[1]));
+
+            assertEquals(ModifiesClasspathProcessor.class.getCanonicalName(), reloadComponent.getNewType());
 
             // Should pass validation
             assertTrue(procNode.isValid());
         } finally {
-            ExtensionManager.removeInstanceClassLoaderIfExists(procNode.getIdentifier());
+            ExtensionManager.removeInstanceClassLoader(procNode.getIdentifier());
         }
     }
 
     @Test
     public void testSomeNonExistentPropertiesDynamicallyModifyClasspath() throws MalformedURLException {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
+
         final PropertyDescriptor classpathProp1 = new PropertyDescriptor.Builder().name("Classpath Resource 1")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
         final PropertyDescriptor classpathProp2 = new PropertyDescriptor.Builder().name("Classpath Resource 2")
                 .dynamicallyModifiesClasspath(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
 
         final ModifiesClasspathProcessor processor = new ModifiesClasspathProcessor(Arrays.asList(classpathProp1, classpathProp2));
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(procNode.getProcessor().getClass(), procNode.getIdentifier())){
-            // Should have an InstanceClassLoader here
-            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
-            assertTrue(contextClassLoader instanceof InstanceClassLoader);
-
-            final InstanceClassLoader instanceClassLoader = (InstanceClassLoader) contextClassLoader;
 
             // Should not have any of the test resources loaded at this point
             final URL[] testResources = getTestResources();
             for (URL testResource : testResources) {
-                if (containsResource(instanceClassLoader.getInstanceResources(), testResource)) {
+                if (containsResource(reloadComponent.getAdditionalUrls(), testResource)) {
                     fail("found resource that should not have been loaded");
                 }
             }
@@ -322,53 +321,52 @@ public class TestStandardProcessorNode {
             procNode.setProperties(properties);
 
             // Should have resources 1 and 3 loaded into the InstanceClassLoader now
-            assertTrue(containsResource(instanceClassLoader.getInstanceResources(), testResources[0]));
-            assertFalse(containsResource(instanceClassLoader.getInstanceResources(), testResources[1]));
-            assertFalse(containsResource(instanceClassLoader.getInstanceResources(), testResources[2]));
+            assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResources[0]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[1]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[2]));
+
+            assertEquals(ModifiesClasspathProcessor.class.getCanonicalName(), reloadComponent.getNewType());
 
             // Should pass validation
             assertTrue(procNode.isValid());
         } finally {
-            ExtensionManager.removeInstanceClassLoaderIfExists(procNode.getIdentifier());
+            ExtensionManager.removeInstanceClassLoader(procNode.getIdentifier());
         }
     }
 
     @Test
     public void testPropertyModifiesClasspathWhenProcessorMissingAnnotation() throws MalformedURLException {
+        final MockReloadComponent reloadComponent = new MockReloadComponent();
         final ModifiesClasspathNoAnnotationProcessor processor = new ModifiesClasspathNoAnnotationProcessor();
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
 
         try (final NarCloseable narCloseable = NarCloseable.withComponentNarLoader(procNode.getProcessor().getClass(), procNode.getIdentifier())){
-            // Can't validate the ClassLoader here b/c the class is missing the annotation
 
-            // Simulate setting the properties pointing to two of the resources
             final Map<String, String> properties = new HashMap<>();
             properties.put(ModifiesClasspathNoAnnotationProcessor.CLASSPATH_RESOURCE.getName(),
                     "src/test/resources/TestClasspathResources/resource1.txt");
             procNode.setProperties(properties);
 
-            // Should not have loaded any of the resources
-            final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-            assertTrue(classLoader instanceof URLClassLoader);
-
             final URL[] testResources = getTestResources();
-            final URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
-            assertFalse(containsResource(urlClassLoader.getURLs(), testResources[0]));
-            assertFalse(containsResource(urlClassLoader.getURLs(), testResources[1]));
-            assertFalse(containsResource(urlClassLoader.getURLs(), testResources[2]));
+            assertTrue(containsResource(reloadComponent.getAdditionalUrls(), testResources[0]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[1]));
+            assertFalse(containsResource(reloadComponent.getAdditionalUrls(), testResources[2]));
+
+            assertEquals(ModifiesClasspathNoAnnotationProcessor.class.getCanonicalName(), reloadComponent.getNewType());
 
             // Should pass validation
             assertTrue(procNode.isValid());
 
         } finally {
-            ExtensionManager.removeInstanceClassLoaderIfExists(procNode.getIdentifier());
+            ExtensionManager.removeInstanceClassLoader(procNode.getIdentifier());
         }
     }
 
     @Test
     public void testVerifyCanUpdateBundle() {
+        final ReloadComponent reloadComponent = new MockReloadComponent();
         final ModifiesClasspathNoAnnotationProcessor processor = new ModifiesClasspathNoAnnotationProcessor();
-        final StandardProcessorNode procNode = createProcessorNode(processor);
+        final StandardProcessorNode procNode = createProcessorNode(processor, reloadComponent);
         final BundleCoordinate existingCoordinate = procNode.getBundleCoordinate();
 
         // should be allowed to update when the bundle is the same
@@ -400,30 +398,68 @@ public class TestStandardProcessorNode {
         }
     }
 
-    @Test
-    public void testValidateControllerServiceApiRequired() {
-
-    }
-
-    private StandardProcessorNode createProcessorNode(Processor processor) {
+    private StandardProcessorNode createProcessorNode(final Processor processor, final ReloadComponent reloadComponent) {
         final String uuid = UUID.randomUUID().toString();
         final ValidationContextFactory validationContextFactory = createValidationContextFactory();
         final NiFiProperties niFiProperties = NiFiProperties.createBasicNiFiProperties("src/test/resources/conf/nifi.properties", null);
         final ProcessScheduler processScheduler = Mockito.mock(ProcessScheduler.class);
         final ComponentLog componentLog = Mockito.mock(ComponentLog.class);
 
-        final Bundle systemBundle = ExtensionManager.createSystemBundle(niFiProperties);
+        final Bundle systemBundle = SystemBundle.create(niFiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
-        ExtensionManager.createInstanceClassLoader(processor.getClass().getName(), uuid, systemBundle);
+        ExtensionManager.createInstanceClassLoader(processor.getClass().getName(), uuid, systemBundle, null);
 
         ProcessorInitializationContext initContext = new StandardProcessorInitializationContext(uuid, componentLog, null, null, null);
         processor.initialize(initContext);
 
         final LoggableComponent<Processor> loggableComponent = new LoggableComponent<>(processor, systemBundle.getBundleDetails().getCoordinate(), componentLog);
-        return new StandardProcessorNode(loggableComponent, uuid, validationContextFactory, processScheduler, null, niFiProperties, variableRegistry);
+        return new StandardProcessorNode(loggableComponent, uuid, validationContextFactory, processScheduler, null, niFiProperties, variableRegistry, reloadComponent);
+    }
+
+    private static class MockReloadComponent implements ReloadComponent {
+
+        private String newType;
+        private BundleCoordinate bundleCoordinate;
+        private final Set<URL> additionalUrls = new LinkedHashSet<>();
+
+        public Set<URL> getAdditionalUrls() {
+            return this.additionalUrls;
+        }
+
+        public String getNewType() {
+            return newType;
+        }
+
+        public BundleCoordinate getBundleCoordinate() {
+            return bundleCoordinate;
+        }
+
+        @Override
+        public void reload(ProcessorNode existingNode, String newType, BundleCoordinate bundleCoordinate, Set<URL> additionalUrls) throws ProcessorInstantiationException {
+            reload(newType, bundleCoordinate, additionalUrls);
+        }
+
+        @Override
+        public void reload(ControllerServiceNode existingNode, String newType, BundleCoordinate bundleCoordinate, Set<URL> additionalUrls) throws ControllerServiceInstantiationException {
+            reload(newType, bundleCoordinate, additionalUrls);
+        }
+
+        @Override
+        public void reload(ReportingTaskNode existingNode, String newType, BundleCoordinate bundleCoordinate, Set<URL> additionalUrls) throws ReportingTaskInstantiationException {
+            reload(newType, bundleCoordinate, additionalUrls);
+        }
+
+        private void reload(String newType, BundleCoordinate bundleCoordinate, Set<URL> additionalUrls) {
+            this.newType = newType;
+            this.bundleCoordinate = bundleCoordinate;
+            this.additionalUrls.clear();
+            if (additionalUrls != null) {
+                this.additionalUrls.addAll(additionalUrls);
+            }
+        }
     }
 
-    private boolean containsResource(URL[] resources, URL resourceToFind) {
+    private boolean containsResource(Set<URL> resources, URL resourceToFind) {
         for (URL resource : resources) {
             if (resourceToFind.getPath().equals(resource.getPath())) {
                 return true;

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestProcessorLifecycle.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestProcessorLifecycle.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestProcessorLifecycle.java
index 5c8d447..9533751 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestProcessorLifecycle.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestProcessorLifecycle.java
@@ -39,6 +39,7 @@ import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.events.VolatileBulletinRepository;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
@@ -542,7 +543,7 @@ public class TestProcessorLifecycle {
         this.setControllerRootGroup(fc, testGroup);
 
         ControllerServiceNode testServiceNode = fc.createControllerService(TestService.class.getName(), "serv",
-                fcsb.getSystemBundle().getBundleDetails().getCoordinate(), true);
+                fcsb.getSystemBundle().getBundleDetails().getCoordinate(), null, true);
         ProcessorNode testProcNode = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
                 fcsb.getSystemBundle().getBundleDetails().getCoordinate());
 
@@ -569,7 +570,7 @@ public class TestProcessorLifecycle {
         this.setControllerRootGroup(fc, testGroup);
 
         ControllerServiceNode testServiceNode = fc.createControllerService(TestService.class.getName(), "foo",
-                fcsb.getSystemBundle().getBundleDetails().getCoordinate(), true);
+                fcsb.getSystemBundle().getBundleDetails().getCoordinate(), null, true);
         testGroup.addControllerService(testServiceNode);
 
         ProcessorNode testProcNode = fc.createProcessor(TestProcessor.class.getName(), UUID.randomUUID().toString(),
@@ -731,7 +732,7 @@ public class TestProcessorLifecycle {
         }
         final NiFiProperties nifiProperties = NiFiProperties.createBasicNiFiProperties(null, addProps);
 
-        final Bundle systemBundle = ExtensionManager.createSystemBundle(nifiProperties);
+        final Bundle systemBundle = SystemBundle.create(nifiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         final FlowController flowController = FlowController.createStandaloneInstance(mock(FlowFileEventRepository.class), nifiProperties,

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestStandardProcessScheduler.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestStandardProcessScheduler.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestStandardProcessScheduler.java
index b69701e..dbbd614 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestStandardProcessScheduler.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/TestStandardProcessScheduler.java
@@ -28,6 +28,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.LoggableComponent;
 import org.apache.nifi.controller.ProcessScheduler;
 import org.apache.nifi.controller.ProcessorNode;
+import org.apache.nifi.controller.ReloadComponent;
 import org.apache.nifi.controller.ReportingTaskNode;
 import org.apache.nifi.controller.StandardProcessorNode;
 import org.apache.nifi.controller.ValidationContextFactory;
@@ -43,6 +44,7 @@ import org.apache.nifi.controller.service.mock.MockProcessGroup;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
@@ -98,7 +100,7 @@ public class TestStandardProcessScheduler {
         this.nifiProperties = NiFiProperties.createBasicNiFiProperties(null, null);
 
         // load the system bundle
-        systemBundle = ExtensionManager.createSystemBundle(nifiProperties);
+        systemBundle = SystemBundle.create(nifiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         scheduler = new StandardProcessScheduler(Mockito.mock(ControllerServiceProvider.class), null, stateMgrProvider, variableRegistry, nifiProperties);
@@ -111,8 +113,9 @@ public class TestStandardProcessScheduler {
 
         final ValidationContextFactory validationContextFactory = new StandardValidationContextFactory(null, variableRegistry);
         final ComponentLog logger = Mockito.mock(ComponentLog.class);
+        final ReloadComponent reloadComponent = Mockito.mock(ReloadComponent.class);
         final LoggableComponent<ReportingTask> loggableComponent = new LoggableComponent<>(reportingTask, systemBundle.getBundleDetails().getCoordinate(), logger);
-        taskNode = new StandardReportingTaskNode(loggableComponent, UUID.randomUUID().toString(), null, scheduler, validationContextFactory, variableRegistry);
+        taskNode = new StandardReportingTaskNode(loggableComponent, UUID.randomUUID().toString(), null, scheduler, validationContextFactory, variableRegistry, reloadComponent);
 
         controller = Mockito.mock(FlowController.class);
         rootGroup = new MockProcessGroup();
@@ -150,16 +153,18 @@ public class TestStandardProcessScheduler {
         final Processor proc = new ServiceReferencingProcessor();
         proc.initialize(new StandardProcessorInitializationContext(uuid, null, null, null, null));
 
+        final ReloadComponent reloadComponent = Mockito.mock(ReloadComponent.class);
+
         final StandardControllerServiceProvider serviceProvider =
                 new StandardControllerServiceProvider(controller, scheduler, null, Mockito.mock(StateManagerProvider.class), variableRegistry, nifiProperties);
         final ControllerServiceNode service = serviceProvider.createControllerService(NoStartServiceImpl.class.getName(), "service",
-                systemBundle.getBundleDetails().getCoordinate(), true);
+                systemBundle.getBundleDetails().getCoordinate(), null, true);
         rootGroup.addControllerService(service);
 
         final LoggableComponent<Processor> loggableComponent = new LoggableComponent<>(proc, systemBundle.getBundleDetails().getCoordinate(), null);
         final ProcessorNode procNode = new StandardProcessorNode(loggableComponent, uuid,
                 new StandardValidationContextFactory(serviceProvider, variableRegistry),
-                scheduler, serviceProvider, nifiProperties, VariableRegistry.EMPTY_REGISTRY);
+                scheduler, serviceProvider, nifiProperties, VariableRegistry.EMPTY_REGISTRY, reloadComponent);
         rootGroup.addProcessor(procNode);
 
         Map<String,String> procProps = new HashMap<>();
@@ -233,7 +238,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(SimpleTestService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         assertFalse(serviceNode.isActive());
         final SimpleTestService ts = (SimpleTestService) serviceNode.getControllerServiceImplementation();
         final ExecutorService executor = Executors.newCachedThreadPool();
@@ -272,7 +277,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(SimpleTestService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         final SimpleTestService ts = (SimpleTestService) serviceNode.getControllerServiceImplementation();
         final ExecutorService executor = Executors.newCachedThreadPool();
 
@@ -310,7 +315,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(SimpleTestService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         final SimpleTestService ts = (SimpleTestService) serviceNode.getControllerServiceImplementation();
         scheduler.enableControllerService(serviceNode);
         assertTrue(serviceNode.isActive());
@@ -344,7 +349,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(FailingService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         scheduler.enableControllerService(serviceNode);
         Thread.sleep(1000);
         scheduler.shutdown();
@@ -378,7 +383,7 @@ public class TestStandardProcessScheduler {
         final ExecutorService executor = Executors.newCachedThreadPool();
         for (int i = 0; i < 200; i++) {
             final ControllerServiceNode serviceNode = provider.createControllerService(RandomShortDelayEnablingService.class.getName(), "1",
-                    systemBundle.getBundleDetails().getCoordinate(), false);
+                    systemBundle.getBundleDetails().getCoordinate(), null, false);
 
             executor.execute(new Runnable() {
                 @Override
@@ -419,7 +424,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(LongEnablingService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         final LongEnablingService ts = (LongEnablingService) serviceNode.getControllerServiceImplementation();
         ts.setLimit(Long.MAX_VALUE);
         scheduler.enableControllerService(serviceNode);
@@ -445,7 +450,7 @@ public class TestStandardProcessScheduler {
         final ProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null, stateMgrProvider, variableRegistry, nifiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(LongEnablingService.class.getName(),
-                "1", systemBundle.getBundleDetails().getCoordinate(), false);
+                "1", systemBundle.getBundleDetails().getCoordinate(), null, false);
         final LongEnablingService ts = (LongEnablingService) serviceNode.getControllerServiceImplementation();
         ts.setLimit(3000);
         scheduler.enableControllerService(serviceNode);

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
index 15c35d9..4a97b8a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderTest.java
@@ -22,6 +22,7 @@ import org.apache.nifi.components.state.StateManagerProvider;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.nar.NarClassLoaders;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.registry.VariableRegistry;
 import org.apache.nifi.reporting.InitializationException;
 import org.apache.nifi.util.FileBasedVariableRegistry;
@@ -48,7 +49,7 @@ public class StandardControllerServiceProviderTest {
         NarClassLoaders.getInstance().init(nifiProperties.getFrameworkWorkingDirectory(), nifiProperties.getExtensionsWorkingDirectory());
 
         // load the system bundle
-        systemBundle = ExtensionManager.createSystemBundle(nifiProperties);
+        systemBundle = SystemBundle.create(nifiProperties);
         ExtensionManager.discoverExtensions(systemBundle, NarClassLoaders.getInstance().getBundles());
 
         variableRegistry = new FileBasedVariableRegistry(nifiProperties.getVariableRegistryPropertiesPaths());
@@ -80,7 +81,7 @@ public class StandardControllerServiceProviderTest {
             public void onComponentRemoved(String componentId) {
             }
         }, variableRegistry, nifiProperties);
-        ControllerServiceNode node = provider.createControllerService(clazz, id, systemBundle.getBundleDetails().getCoordinate(), true);
+        ControllerServiceNode node = provider.createControllerService(clazz, id, systemBundle.getBundleDetails().getCoordinate(), null, true);
         proxied = node.getProxiedControllerService();
         implementation = node.getControllerServiceImplementation();
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
index 40d9357..3a28cb0 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/TestStandardControllerServiceProvider.java
@@ -24,6 +24,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.LoggableComponent;
 import org.apache.nifi.controller.ProcessScheduler;
 import org.apache.nifi.controller.ProcessorNode;
+import org.apache.nifi.controller.ReloadComponent;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.StandardProcessorNode;
 import org.apache.nifi.controller.scheduling.StandardProcessScheduler;
@@ -35,6 +36,7 @@ import org.apache.nifi.controller.service.mock.ServiceC;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.StandardProcessGroup;
 import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.processor.StandardValidationContextFactory;
 import org.apache.nifi.registry.VariableRegistry;
@@ -93,7 +95,7 @@ public class TestStandardControllerServiceProvider {
         niFiProperties = NiFiProperties.createBasicNiFiProperties(null, null);
 
         // load the system bundle
-        systemBundle = ExtensionManager.createSystemBundle(niFiProperties);
+        systemBundle = SystemBundle.create(niFiProperties);
         ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
     }
 
@@ -118,7 +120,7 @@ public class TestStandardControllerServiceProvider {
                 new StandardControllerServiceProvider(controller, scheduler, null, stateManagerProvider, variableRegistry, niFiProperties);
 
         final ControllerServiceNode serviceNode = provider.createControllerService(ServiceB.class.getName(), "B",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null,false);
         provider.enableControllerService(serviceNode);
         provider.disableControllerService(serviceNode);
     }
@@ -134,9 +136,9 @@ public class TestStandardControllerServiceProvider {
                 new StandardControllerServiceProvider(controller, scheduler, null, stateManagerProvider, variableRegistry, niFiProperties);
 
         final ControllerServiceNode serviceNodeB = provider.createControllerService(ServiceB.class.getName(), "B",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNodeA = provider.createControllerService(ServiceA.class.getName(), "A",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         group.addControllerService(serviceNodeA);
         group.addControllerService(serviceNodeB);
 
@@ -208,13 +210,13 @@ public class TestStandardControllerServiceProvider {
         // we enable C and B, even if we attempt to enable C before B... i.e., if we try to enable C, we cannot do so
         // until B is first enabled so ensure that we enable B first.
         final ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceA.class.getName(), "2",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         procGroup.addControllerService(serviceNode1);
         procGroup.addControllerService(serviceNode2);
@@ -251,9 +253,9 @@ public class TestStandardControllerServiceProvider {
         final StandardControllerServiceProvider provider =
                 new StandardControllerServiceProvider(controller, null, null, stateManagerProvider, variableRegistry, niFiProperties);
         final ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceB.class.getName(), "2",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         setProperty(serviceNode1, ServiceA.OTHER_SERVICE.getName(), "2");
 
@@ -312,7 +314,7 @@ public class TestStandardControllerServiceProvider {
         // like that.
         nodeMap.clear();
         final ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         setProperty(serviceNode1, ServiceA.OTHER_SERVICE.getName(), "3");
         setProperty(serviceNode3, ServiceA.OTHER_SERVICE.getName(), "1");
         nodeMap.put("1", serviceNode1);
@@ -338,9 +340,9 @@ public class TestStandardControllerServiceProvider {
         nodeMap.clear();
         setProperty(serviceNode1, ServiceA.OTHER_SERVICE.getName(), "2");
         final ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         final ControllerServiceNode serviceNode5 = provider.createControllerService(ServiceB.class.getName(), "5",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         setProperty(serviceNode3, ServiceA.OTHER_SERVICE.getName(), "4");
         nodeMap.put("1", serviceNode1);
         nodeMap.put("2", serviceNode2);
@@ -397,10 +399,11 @@ public class TestStandardControllerServiceProvider {
     }
 
     private ProcessorNode createProcessor(final StandardProcessScheduler scheduler, final ControllerServiceProvider serviceProvider) {
+        final ReloadComponent reloadComponent = Mockito.mock(ReloadComponent.class);
         final LoggableComponent<Processor> dummyProcessor = new LoggableComponent<>(new DummyProcessor(), systemBundle.getBundleDetails().getCoordinate(), null);
         final ProcessorNode procNode = new StandardProcessorNode(dummyProcessor, UUID.randomUUID().toString(),
                 new StandardValidationContextFactory(serviceProvider, null), scheduler, serviceProvider, niFiProperties,
-                VariableRegistry.EMPTY_REGISTRY);
+                VariableRegistry.EMPTY_REGISTRY, reloadComponent);
 
         final ProcessGroup group = new StandardProcessGroup(UUID.randomUUID().toString(), serviceProvider, scheduler, null, null, null, variableRegistry);
         group.addProcessor(procNode);
@@ -419,7 +422,7 @@ public class TestStandardControllerServiceProvider {
         final StandardControllerServiceProvider provider =
                 new StandardControllerServiceProvider(controller, null, null, stateManagerProvider, variableRegistry, niFiProperties);
         final ControllerServiceNode serviceNode = provider.createControllerService(ServiceA.class.getName(), "1",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         final ProcessorNode procNode = createProcessor(scheduler, provider);
         serviceNode.addReference(procNode);
@@ -443,17 +446,17 @@ public class TestStandardControllerServiceProvider {
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
 
         ControllerServiceNode A = provider.createControllerService(ServiceA.class.getName(), "A",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode B = provider.createControllerService(ServiceA.class.getName(), "B",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode C = provider.createControllerService(ServiceA.class.getName(), "C",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode D = provider.createControllerService(ServiceB.class.getName(), "D",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode E = provider.createControllerService(ServiceA.class.getName(), "E",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode F = provider.createControllerService(ServiceB.class.getName(), "F",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         procGroup.addControllerService(A);
         procGroup.addControllerService(B);
@@ -494,15 +497,15 @@ public class TestStandardControllerServiceProvider {
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
 
         ControllerServiceNode A = provider.createControllerService(ServiceC.class.getName(), "A",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode B = provider.createControllerService(ServiceA.class.getName(), "B",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode C = provider.createControllerService(ServiceB.class.getName(), "C",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode D = provider.createControllerService(ServiceA.class.getName(), "D",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode F = provider.createControllerService(ServiceA.class.getName(), "F",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         procGroup.addControllerService(A);
         procGroup.addControllerService(B);
@@ -536,19 +539,19 @@ public class TestStandardControllerServiceProvider {
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
 
         ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode2 = provider.createControllerService(ServiceA.class.getName(), "2",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode3 = provider.createControllerService(ServiceA.class.getName(), "3",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode4 = provider.createControllerService(ServiceB.class.getName(), "4",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode5 = provider.createControllerService(ServiceA.class.getName(), "5",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode6 = provider.createControllerService(ServiceB.class.getName(), "6",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
         ControllerServiceNode serviceNode7 = provider.createControllerService(ServiceC.class.getName(), "7",
-                systemBundle.getBundleDetails().getCoordinate(), false);
+                systemBundle.getBundleDetails().getCoordinate(), null, false);
 
         procGroup.addControllerService(serviceNode1);
         procGroup.addControllerService(serviceNode2);

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/pom.xml
new file mode 100644
index 0000000..ba20a37
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/pom.xml
@@ -0,0 +1,42 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <!--
+      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.
+    -->
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-framework</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-framework-nar-utils</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-nar-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-properties</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-framework-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/556f309d/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializer.java
new file mode 100644
index 0000000..9a1149c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.init;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.reporting.InitializationException;
+
+/**
+ * An interface for initializing and tearing down a ConfigurableComponent. It is up to the
+ * implementer to call "init" so that you can call
+ * ConfigurableComponent.getPropertyDescriptors()
+ *
+ */
+public interface ConfigurableComponentInitializer {
+
+    /**
+     * Initializes a configurable component to the point that you can call
+     * getPropertyDescriptors() on it
+     *
+     * @param component the component to initialize
+     * @throws InitializationException if the component could not be initialized
+     */
+    void initialize(ConfigurableComponent component) throws InitializationException;
+
+    /**
+     * Calls the lifecycle methods that should be called when a flow is shutdown.
+     *
+     * @param component the component to initialize
+     */
+    void teardown(ConfigurableComponent component);
+}