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 2018/11/01 18:41:42 UTC

[4/7] nifi git commit: NIFI-5673 Set up property/assembly for new auto-load directory - Set up NarAutoLoader to watch directory for new files - Move NarAutoLoader to JettyServer since it will need access to ExtensionManager - Created NarLoader to shared

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/StandardProcessSchedulerIT.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/StandardProcessSchedulerIT.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/StandardProcessSchedulerIT.java
index f8a4b43..fac0272 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/StandardProcessSchedulerIT.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/scheduling/StandardProcessSchedulerIT.java
@@ -17,12 +17,6 @@
 
 package org.apache.nifi.controller.scheduling;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Collections;
-
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.components.state.StateManagerProvider;
 import org.apache.nifi.controller.FlowController;
@@ -30,7 +24,8 @@ import org.apache.nifi.controller.service.ControllerServiceNode;
 import org.apache.nifi.controller.service.ControllerServiceState;
 import org.apache.nifi.controller.service.StandardControllerServiceProvider;
 import org.apache.nifi.engine.FlowEngine;
-import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.registry.VariableRegistry;
 import org.apache.nifi.reporting.InitializationException;
@@ -40,12 +35,19 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 public class StandardProcessSchedulerIT {
     private final StateManagerProvider stateMgrProvider = Mockito.mock(StateManagerProvider.class);
     private VariableRegistry variableRegistry = VariableRegistry.ENVIRONMENT_SYSTEM_REGISTRY;
     private FlowController controller;
     private NiFiProperties nifiProperties;
     private Bundle systemBundle;
+    private ExtensionDiscoveringManager extensionManager;
     private volatile String propsFile = TestStandardProcessScheduler.class.getResource("/standardprocessschedulertest.nifi.properties").getFile();
 
     @Before
@@ -54,9 +56,11 @@ public class StandardProcessSchedulerIT {
 
         // load the system bundle
         systemBundle = SystemBundle.create(nifiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         controller = Mockito.mock(FlowController.class);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/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 3c382cf..92e6e5d 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
@@ -44,7 +44,8 @@ import org.apache.nifi.controller.service.mock.MockProcessGroup;
 import org.apache.nifi.engine.FlowEngine;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.logging.ComponentLog;
-import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
@@ -104,6 +105,7 @@ public class TestStandardProcessScheduler {
     private ProcessGroup rootGroup;
     private NiFiProperties nifiProperties;
     private Bundle systemBundle;
+    private ExtensionDiscoveringManager extensionManager;
     private volatile String propsFile = TestStandardProcessScheduler.class.getResource("/standardprocessschedulertest.nifi.properties").getFile();
 
     @Before
@@ -115,7 +117,8 @@ public class TestStandardProcessScheduler {
 
         // load the system bundle
         systemBundle = SystemBundle.create(nifiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         scheduler = new StandardProcessScheduler(new FlowEngine(1, "Unit Test", true), Mockito.mock(FlowController.class), null, stateMgrProvider, nifiProperties);
         scheduler.setSchedulingAgent(SchedulingStrategy.TIMER_DRIVEN, Mockito.mock(SchedulingAgent.class));
@@ -130,9 +133,10 @@ public class TestStandardProcessScheduler {
         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,
-            new StandardComponentVariableRegistry(variableRegistry), reloadComponent, new SynchronousValidationTrigger());
+            new StandardComponentVariableRegistry(variableRegistry), reloadComponent, extensionManager, new SynchronousValidationTrigger());
 
         controller = Mockito.mock(FlowController.class);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final ConcurrentMap<String, ProcessorNode> processorMap = new ConcurrentHashMap<>();
         Mockito.doAnswer(new Answer<ProcessorNode>() {
@@ -205,7 +209,8 @@ public class TestStandardProcessScheduler {
         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, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, new SynchronousValidationTrigger());
+            scheduler, serviceProvider, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY),
+                reloadComponent, extensionManager, new SynchronousValidationTrigger());
         rootGroup.addProcessor(procNode);
 
         Map<String, String> procProps = new HashMap<>();
@@ -507,7 +512,8 @@ public class TestStandardProcessScheduler {
 
         final ProcessorNode procNode = new StandardProcessorNode(loggableComponent, UUID.randomUUID().toString(),
             new StandardValidationContextFactory(controller, variableRegistry),
-            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, new SynchronousValidationTrigger());
+            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY),
+                reloadComponent, extensionManager, new SynchronousValidationTrigger());
 
         procNode.performValidation();
         rootGroup.addProcessor(procNode);
@@ -533,7 +539,8 @@ public class TestStandardProcessScheduler {
 
         final ProcessorNode procNode = new StandardProcessorNode(loggableComponent, UUID.randomUUID().toString(),
             new StandardValidationContextFactory(controller, variableRegistry),
-            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, new SynchronousValidationTrigger());
+            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY),
+                reloadComponent, extensionManager, new SynchronousValidationTrigger());
 
         rootGroup.addProcessor(procNode);
 
@@ -562,7 +569,8 @@ public class TestStandardProcessScheduler {
 
         final ProcessorNode procNode = new StandardProcessorNode(loggableComponent, UUID.randomUUID().toString(),
             new StandardValidationContextFactory(controller, variableRegistry),
-            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, new SynchronousValidationTrigger());
+            scheduler, controller, nifiProperties, new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY),
+                reloadComponent, extensionManager, new SynchronousValidationTrigger());
 
         rootGroup.addProcessor(procNode);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/StandardFlowSerializerTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/StandardFlowSerializerTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/StandardFlowSerializerTest.java
index 91a52a0..f019257 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/StandardFlowSerializerTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/serialization/StandardFlowSerializerTest.java
@@ -16,16 +16,6 @@
  */
 package org.apache.nifi.controller.serialization;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
 import org.apache.commons.io.FileUtils;
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.authorization.AbstractPolicyBasedAuthorizer;
@@ -36,7 +26,8 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.repository.FlowFileEventRepository;
 import org.apache.nifi.encrypt.StringEncryptor;
-import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.provenance.MockProvenanceRepository;
 import org.apache.nifi.registry.VariableRegistry;
@@ -50,6 +41,17 @@ import org.junit.Test;
 import org.mockito.Mockito;
 import org.w3c.dom.Document;
 
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 public class StandardFlowSerializerTest {
 
     private static final String RAW_COMMENTS
@@ -60,6 +62,7 @@ public class StandardFlowSerializerTest {
 
     private FlowController controller;
     private Bundle systemBundle;
+    private ExtensionDiscoveringManager extensionManager;
     private StandardFlowSerializer serializer;
 
     @Before
@@ -78,14 +81,15 @@ public class StandardFlowSerializerTest {
 
         // use the system bundle
         systemBundle = SystemBundle.create(nifiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         final AbstractPolicyBasedAuthorizer authorizer = new MockPolicyBasedAuthorizer();
         final VariableRegistry variableRegistry = new FileBasedVariableRegistry(nifiProperties.getVariableRegistryPropertiesPaths());
 
         final BulletinRepository bulletinRepo = Mockito.mock(BulletinRepository.class);
         controller = FlowController.createStandaloneInstance(flowFileEventRepo, nifiProperties, authorizer,
-            auditService, encryptor, bulletinRepo, variableRegistry, Mockito.mock(FlowRegistryClient.class));
+            auditService, encryptor, bulletinRepo, variableRegistry, Mockito.mock(FlowRegistryClient.class), extensionManager);
 
         serializer = new StandardFlowSerializer(encryptor);
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderIT.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderIT.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderIT.java
index e956944..0f4d3ce 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderIT.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderIT.java
@@ -17,16 +17,6 @@
 
 package org.apache.nifi.controller.service;
 
-import static org.junit.Assert.assertTrue;
-
-import java.beans.PropertyDescriptor;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.components.state.StateManager;
 import org.apache.nifi.components.state.StateManagerProvider;
@@ -37,7 +27,8 @@ import org.apache.nifi.controller.service.mock.ServiceA;
 import org.apache.nifi.controller.service.mock.ServiceB;
 import org.apache.nifi.engine.FlowEngine;
 import org.apache.nifi.groups.ProcessGroup;
-import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.registry.VariableRegistry;
 import org.apache.nifi.util.NiFiProperties;
@@ -46,9 +37,20 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import java.beans.PropertyDescriptor;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import static org.junit.Assert.assertTrue;
+
 public class StandardControllerServiceProviderIT {
     private static Bundle systemBundle;
     private static NiFiProperties niFiProperties;
+    private static ExtensionDiscoveringManager extensionManager;
     private static VariableRegistry variableRegistry = VariableRegistry.ENVIRONMENT_SYSTEM_REGISTRY;
 
     private static StateManagerProvider stateManagerProvider = new StateManagerProvider() {
@@ -81,7 +83,8 @@ public class StandardControllerServiceProviderIT {
 
         // load the system bundle
         systemBundle = SystemBundle.create(niFiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
     }
 
     /**
@@ -102,6 +105,7 @@ public class StandardControllerServiceProviderIT {
         final FlowController controller = Mockito.mock(FlowController.class);
         final ProcessGroup procGroup = new MockProcessGroup(controller);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final StandardControllerServiceProvider provider = new StandardControllerServiceProvider(controller, scheduler, null,
             stateManagerProvider, variableRegistry, niFiProperties, new SynchronousValidationTrigger());

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/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 f7ee72f..0a0b05f 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
@@ -20,8 +20,9 @@ import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.components.state.StateManager;
 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.controller.FlowController;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.registry.VariableRegistry;
 import org.apache.nifi.registry.variable.FileBasedVariableRegistry;
@@ -33,6 +34,8 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import java.util.Collections;
+
 
 public class StandardControllerServiceProviderTest {
 
@@ -40,27 +43,31 @@ public class StandardControllerServiceProviderTest {
     private ControllerService implementation;
     private static VariableRegistry variableRegistry;
     private static NiFiProperties nifiProperties;
+    private static ExtensionDiscoveringManager extensionManager;
     private static Bundle systemBundle;
+    private static FlowController flowController;
 
     @BeforeClass
     public static void setupSuite() throws Exception {
         System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, StandardControllerServiceProviderTest.class.getResource("/conf/nifi.properties").getFile());
         nifiProperties = NiFiProperties.createBasicNiFiProperties(null, null);
 
-        NarClassLoaders.getInstance().init(nifiProperties.getFrameworkWorkingDirectory(), nifiProperties.getExtensionsWorkingDirectory());
-
         // load the system bundle
         systemBundle = SystemBundle.create(nifiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, NarClassLoaders.getInstance().getBundles());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
 
         variableRegistry = new FileBasedVariableRegistry(nifiProperties.getVariableRegistryPropertiesPaths());
+
+        flowController = Mockito.mock(FlowController.class);
+        Mockito.when(flowController.getExtensionManager()).thenReturn(extensionManager);
     }
 
     @Before
     public void setup() throws Exception {
         String id = "id";
         String clazz = "org.apache.nifi.controller.service.util.TestControllerService";
-        ControllerServiceProvider provider = new StandardControllerServiceProvider(null, null, null, new StateManagerProvider() {
+        ControllerServiceProvider provider = new StandardControllerServiceProvider(flowController, null, null, new StateManagerProvider() {
             @Override
             public StateManager getStateManager(final String componentId) {
                 return Mockito.mock(StateManager.class);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/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 b37dbb2..d317000 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
@@ -17,20 +17,6 @@
  */
 package org.apache.nifi.controller.service;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.TimeUnit;
-
 import org.apache.nifi.bundle.Bundle;
 import org.apache.nifi.components.state.StateManager;
 import org.apache.nifi.components.state.StateManagerProvider;
@@ -49,7 +35,8 @@ import org.apache.nifi.controller.service.mock.ServiceC;
 import org.apache.nifi.engine.FlowEngine;
 import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.groups.StandardProcessGroup;
-import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.nar.SystemBundle;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.processor.StandardValidationContextFactory;
@@ -68,6 +55,20 @@ import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 public class TestStandardControllerServiceProvider {
 
     private static StateManagerProvider stateManagerProvider = new StateManagerProvider() {
@@ -95,6 +96,7 @@ public class TestStandardControllerServiceProvider {
 
     private static VariableRegistry variableRegistry = VariableRegistry.ENVIRONMENT_SYSTEM_REGISTRY;
     private static NiFiProperties niFiProperties;
+    private static ExtensionDiscoveringManager extensionManager;
     private static Bundle systemBundle;
     private FlowController controller;
 
@@ -105,12 +107,14 @@ public class TestStandardControllerServiceProvider {
 
         // load the system bundle
         systemBundle = SystemBundle.create(niFiProperties);
-        ExtensionManager.discoverExtensions(systemBundle, Collections.emptySet());
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, Collections.emptySet());
     }
 
     @Before
     public void setup() {
         controller = Mockito.mock(FlowController.class);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final ConcurrentMap<String, ProcessorNode> processorMap = new ConcurrentHashMap<>();
         Mockito.doAnswer(new Answer<ProcessorNode>() {
@@ -146,6 +150,7 @@ public class TestStandardControllerServiceProvider {
         final ProcessGroup procGroup = new MockProcessGroup(controller);
         final FlowController controller = Mockito.mock(FlowController.class);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final StandardProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider =
@@ -164,6 +169,7 @@ public class TestStandardControllerServiceProvider {
         final ProcessGroup group = new MockProcessGroup(controller);
         final FlowController controller = Mockito.mock(FlowController.class);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(group);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final StandardProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider =
@@ -219,6 +225,7 @@ public class TestStandardControllerServiceProvider {
         final ProcessGroup procGroup = new MockProcessGroup(controller);
         final FlowController controller = Mockito.mock(FlowController.class);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final StandardControllerServiceProvider provider =
             new StandardControllerServiceProvider(controller, null, null, stateManagerProvider, variableRegistry, niFiProperties, new SynchronousValidationTrigger());
@@ -379,7 +386,7 @@ public class TestStandardControllerServiceProvider {
         final LoggableComponent<Processor> dummyProcessor = new LoggableComponent<>(processor, systemBundle.getBundleDetails().getCoordinate(), null);
         final ProcessorNode procNode = new StandardProcessorNode(dummyProcessor, mockInitContext.getIdentifier(),
                 new StandardValidationContextFactory(serviceProvider, null), scheduler, serviceProvider, niFiProperties,
-            new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, new SynchronousValidationTrigger());
+            new StandardComponentVariableRegistry(VariableRegistry.EMPTY_REGISTRY), reloadComponent, extensionManager, new SynchronousValidationTrigger());
 
         final ProcessGroup group = new StandardProcessGroup(UUID.randomUUID().toString(), serviceProvider, scheduler, null, null, Mockito.mock(FlowController.class),
             new MutableVariableRegistry(variableRegistry));
@@ -394,6 +401,7 @@ public class TestStandardControllerServiceProvider {
         final ProcessGroup procGroup = new MockProcessGroup(controller);
         final FlowController controller = Mockito.mock(FlowController.class);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         final StandardProcessScheduler scheduler = createScheduler();
         final StandardControllerServiceProvider provider =
@@ -421,6 +429,7 @@ public class TestStandardControllerServiceProvider {
             new StandardControllerServiceProvider(controller, scheduler, null, stateManagerProvider, variableRegistry, niFiProperties, new SynchronousValidationTrigger());
         ProcessGroup procGroup = new MockProcessGroup(controller);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         ControllerServiceNode A = provider.createControllerService(ServiceA.class.getName(), "A",
                 systemBundle.getBundleDetails().getCoordinate(), null, false);
@@ -474,6 +483,7 @@ public class TestStandardControllerServiceProvider {
             stateManagerProvider, variableRegistry, niFiProperties, new SynchronousValidationTrigger());
         ProcessGroup procGroup = new MockProcessGroup(controller);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         ControllerServiceNode A = provider.createControllerService(ServiceC.class.getName(), "A",
                 systemBundle.getBundleDetails().getCoordinate(), null, false);
@@ -519,6 +529,7 @@ public class TestStandardControllerServiceProvider {
             new StandardControllerServiceProvider(controller, scheduler, null, stateManagerProvider, variableRegistry, niFiProperties, new SynchronousValidationTrigger());
         ProcessGroup procGroup = new MockProcessGroup(controller);
         Mockito.when(controller.getGroup(Mockito.anyString())).thenReturn(procGroup);
+        Mockito.when(controller.getExtensionManager()).thenReturn(extensionManager);
 
         ControllerServiceNode serviceNode1 = provider.createControllerService(ServiceA.class.getName(), "1",
                 systemBundle.getBundleDetails().getCoordinate(), null, false);

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java
index 30294a4..09acbc9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/fingerprint/FingerprintFactoryTest.java
@@ -43,6 +43,8 @@ import org.apache.nifi.controller.serialization.ScheduledStateLookup;
 import org.apache.nifi.controller.serialization.StandardFlowSerializer;
 import org.apache.nifi.encrypt.StringEncryptor;
 import org.apache.nifi.groups.RemoteProcessGroup;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
 import org.apache.nifi.remote.RemoteGroupPort;
 import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol;
 import org.junit.Before;
@@ -58,12 +60,14 @@ import org.xml.sax.SAXParseException;
 public class FingerprintFactoryTest {
 
     private StringEncryptor encryptor;
+    private ExtensionManager extensionManager;
     private FingerprintFactory fingerprinter;
 
     @Before
     public void setup() {
         encryptor = new StringEncryptor("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC", "nififtw!");
-        fingerprinter = new FingerprintFactory(encryptor);
+        extensionManager = new StandardExtensionDiscoveringManager();
+        fingerprinter = new FingerprintFactory(encryptor, extensionManager);
     }
 
     @Test
@@ -122,7 +126,7 @@ public class FingerprintFactoryTest {
 
     @Test
     public void testSchemaValidation() throws IOException {
-        FingerprintFactory fp = new FingerprintFactory(null, getValidatingDocumentBuilder());
+        FingerprintFactory fp = new FingerprintFactory(null, getValidatingDocumentBuilder(), extensionManager);
         final String fingerprint = fp.createFingerprint(getResourceBytes("/nifi/fingerprint/validating-flow.xml"), null);
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/pom.xml
new file mode 100644
index 0000000..1d4e2de
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/pom.xml
@@ -0,0 +1,46 @@
+<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.9.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-framework-nar-loading-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-documentation</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/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/ExtensionUiLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/ExtensionUiLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/ExtensionUiLoader.java
new file mode 100644
index 0000000..0c5b02b
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/ExtensionUiLoader.java
@@ -0,0 +1,33 @@
+/*
+ * 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.nar;
+
+import org.apache.nifi.bundle.Bundle;
+
+import java.util.Set;
+
+/**
+ * Loads WARs from extensions and makes them available to the running application.
+ */
+public interface ExtensionUiLoader {
+
+    /**
+     * @param bundles the set of bundles to load WARs from
+     */
+    void loadExtensionUis(Set<Bundle> bundles);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoader.java
new file mode 100644
index 0000000..2ac55a0
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoader.java
@@ -0,0 +1,87 @@
+/*
+ * 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.nar;
+
+import org.apache.nifi.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchService;
+import java.util.Objects;
+
+/**
+ * Starts a thread to monitor the auto-load directory for new NARs.
+ */
+public class NarAutoLoader {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NarAutoLoader.class);
+
+    private static final long POLL_INTERVAL_MS = 5000;
+
+    private final File autoLoadDir;
+    private final NarLoader narLoader;
+
+    private volatile NarAutoLoaderTask narAutoLoaderTask;
+    private volatile boolean started = false;
+
+    public NarAutoLoader(final File autoLoadDir, final NarLoader narLoader) {
+        this.autoLoadDir = Objects.requireNonNull(autoLoadDir);
+        this.narLoader = Objects.requireNonNull(narLoader);
+    }
+
+    public synchronized void start() throws IOException {
+        if (started) {
+            return;
+        }
+
+        FileUtils.ensureDirectoryExistAndCanReadAndWrite(autoLoadDir);
+
+        final WatchService watcher = FileSystems.getDefault().newWatchService();
+
+        final Path autoLoadPath = autoLoadDir.toPath();
+        autoLoadPath.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
+
+        narAutoLoaderTask = new NarAutoLoaderTask.Builder()
+                .autoLoadPath(autoLoadPath)
+                .watchService(watcher)
+                .pollIntervalMillis(POLL_INTERVAL_MS)
+                .narLoader(narLoader)
+                .build();
+
+        LOGGER.info("Starting NAR Auto-Loader for directory {} ...", new Object[]{autoLoadPath});
+
+        final Thread thread = new Thread(narAutoLoaderTask);
+        thread.setName("NAR Auto-Loader");
+        thread.setDaemon(true);
+        thread.start();
+
+        LOGGER.info("NAR Auto-Loader started");
+        started = true;
+    }
+
+    public synchronized void stop() {
+        started = false;
+        narAutoLoaderTask.stop();
+        LOGGER.info("NAR Auto-Loader stopped");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoaderTask.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoaderTask.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoaderTask.java
new file mode 100644
index 0000000..260fe10
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarAutoLoaderTask.java
@@ -0,0 +1,178 @@
+/*
+ * 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.nar;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The runnable task that polls the WatchService for new NAR files found in the auto-load directory.
+ *
+ * Each new NAR file found will be passed to the NarLoader to be unpacked and loaded into the ExtensionManager.
+ *
+ */
+public class NarAutoLoaderTask implements Runnable {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(NarAutoLoaderTask.class);
+
+    private static final long MIN_FILE_AGE = 5000;
+
+    private final Path autoLoadPath;
+    private final WatchService watchService;
+    private final long pollIntervalMillis;
+    private final NarLoader narLoader;
+    private final List<File> candidateNars;
+
+    private volatile boolean stopped = false;
+
+    private NarAutoLoaderTask(final Builder builder) {
+        this.autoLoadPath = builder.autoLoadPath;
+        this.watchService = builder.watchService;
+        this.pollIntervalMillis = builder.pollIntervalMillis;
+        this.narLoader = builder.narLoader;
+        this.candidateNars = new ArrayList<>();
+    }
+
+    @Override
+    public void run() {
+        while (!stopped) {
+            try {
+                WatchKey key;
+                try {
+                    LOGGER.debug("Polling for new NARs at {}", new Object[]{autoLoadPath});
+                    key = watchService.poll(pollIntervalMillis, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException x) {
+                    LOGGER.info("WatchService interrupted, returning...");
+                    return;
+                }
+
+                // Key comes back as null when there are no new create events, but we still want to continue processing
+                // so we can consider files added to the candidateNars list in previous iterations
+
+                if (key != null) {
+                    for (WatchEvent<?> event : key.pollEvents()) {
+                        final WatchEvent.Kind<?> kind = event.kind();
+                        if (kind == StandardWatchEventKinds.OVERFLOW) {
+                            continue;
+                        }
+
+                        final WatchEvent<Path> ev = (WatchEvent<Path>) event;
+                        final Path filename = ev.context();
+
+                        final Path autoLoadFile = autoLoadPath.resolve(filename);
+                        final String autoLoadFilename = autoLoadFile.toFile().getName().toLowerCase();
+
+                        if (!autoLoadFilename.endsWith(".nar")) {
+                            LOGGER.info("Skipping non-nar file {}", new Object[]{autoLoadFilename});
+                            continue;
+                        }
+
+                        if (autoLoadFilename.startsWith(".")) {
+                            LOGGER.debug("Skipping partially written file {}", new Object[]{autoLoadFilename});
+                            continue;
+                        }
+
+                        LOGGER.info("Found {} in auto-load directory", new Object[]{autoLoadFile});
+                        candidateNars.add(autoLoadFile.toFile());
+                    }
+
+                    final boolean valid = key.reset();
+                    if (!valid) {
+                        LOGGER.error("NAR auto-load directory is no longer valid");
+                        stop();
+                    }
+                }
+
+                // Make sure that the created file is done being written by checking the last modified date of the file and
+                // make sure a certain amount of time has passed indicating it is done being written to
+
+                final List<File> readyNars = new ArrayList<>();
+                final Iterator<File> candidateNarIter = candidateNars.iterator();
+                while (candidateNarIter.hasNext()) {
+                    final File candidateNar = candidateNarIter.next();
+                    final long fileAge = System.currentTimeMillis() - candidateNar.lastModified();
+                    if (fileAge >= MIN_FILE_AGE) {
+                        readyNars.add(candidateNar);
+                        candidateNarIter.remove();
+                    } else {
+                        LOGGER.debug("Candidate NAR {} not ready yet, will check again next time", new Object[]{candidateNar.getName()});
+                    }
+                }
+
+                if (!readyNars.isEmpty()) {
+                    narLoader.load(readyNars);
+                }
+
+            } catch (final Exception e) {
+                LOGGER.error("Error loading NARs due to: " + e.getMessage(), e);
+            }
+        }
+    }
+
+    public void stop() {
+        LOGGER.info("Stopping NAR Auto-loader");
+        stopped = true;
+    }
+
+    /**
+     * Builder for NarAutoLoaderTask.
+     */
+    public static class Builder {
+
+        private Path autoLoadPath;
+        private WatchService watchService;
+        private long pollIntervalMillis;
+        private NarLoader narLoader;
+
+        public Builder autoLoadPath(final Path autoLoadPath) {
+            this.autoLoadPath = autoLoadPath;
+            return this;
+        }
+
+        public Builder watchService(final WatchService watchService) {
+            this.watchService = watchService;
+            return this;
+        }
+
+        public Builder pollIntervalMillis(final long pollIntervalMillis) {
+            this.pollIntervalMillis = pollIntervalMillis;
+            return this;
+        }
+
+        public Builder narLoader(final NarLoader narLoader) {
+            this.narLoader = narLoader;
+            return this;
+        }
+
+        public NarAutoLoaderTask build() {
+            return new NarAutoLoaderTask(this);
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarLoader.java
new file mode 100644
index 0000000..6aa4d43
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/NarLoader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.nar;
+
+import java.io.File;
+import java.util.Collection;
+
+/**
+ * Responsible for loading NARs into the running application.
+ */
+public interface NarLoader {
+
+    /**
+     * Loads the given NARs.
+     *
+     * @param narFiles the collection of NAR files to load
+     * @return the result of loading the NARs (i.e. bundles that were loaded, bundles that were skipped)
+     */
+    NarLoadResult load(final Collection<File> narFiles);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/StandardNarLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/StandardNarLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/StandardNarLoader.java
new file mode 100644
index 0000000..2ddca01
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/main/java/org/apache/nifi/nar/StandardNarLoader.java
@@ -0,0 +1,167 @@
+/*
+ * 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.nar;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.documentation.DocGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * Loads a set of NARs from the file system into the running application.
+ *
+ * NOTE: Initially this will only be used from the NarAutoLoader which is watching a directory for new files, but eventually
+ * this may also be used for loading a NAR that was downloaded from the extension registry, and thus the load method
+ * is synchronized to ensure only one set of NARs can be in process of loading at a given time.
+ */
+public class StandardNarLoader implements NarLoader {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(StandardNarLoader.class);
+
+    private final File extensionsWorkingDir;
+    private final File docsWorkingDir;
+    private final NarClassLoaders narClassLoaders;
+    private final ExtensionDiscoveringManager extensionManager;
+    private final ExtensionMapping extensionMapping;
+    private final ExtensionUiLoader extensionUiLoader;
+
+    private Set<BundleDetails> previouslySkippedBundles;
+
+    public StandardNarLoader(final File extensionsWorkingDir,
+                             final File docsWorkingDir,
+                             final NarClassLoaders narClassLoaders,
+                             final ExtensionDiscoveringManager extensionManager,
+                             final ExtensionMapping extensionMapping,
+                             final ExtensionUiLoader extensionUiLoader) {
+        this.extensionsWorkingDir = extensionsWorkingDir;
+        this.docsWorkingDir = docsWorkingDir;
+        this.narClassLoaders = narClassLoaders;
+        this.extensionManager = extensionManager;
+        this.extensionMapping = extensionMapping;
+        this.extensionUiLoader = extensionUiLoader;
+    }
+
+    @Override
+    public synchronized NarLoadResult load(final Collection<File> narFiles) {
+        LOGGER.info("Starting load process for {} NARs...", new Object[]{narFiles.size()});
+
+        final List<File> unpackedNars = new ArrayList<>();
+
+        for (final File narFile : narFiles) {
+            LOGGER.debug("Unpacking {}...", new Object[]{narFile.getName()});
+            final File unpackedNar = unpack(narFile);
+            if (unpackedNar != null) {
+                LOGGER.debug("Completed unpacking {}", new Object[]{narFile.getName()});
+                unpackedNars.add(unpackedNar);
+            }
+        }
+
+        if (previouslySkippedBundles != null && !previouslySkippedBundles.isEmpty()) {
+            LOGGER.info("Including {} previously skipped bundle(s)", new Object[]{previouslySkippedBundles.size()});
+            previouslySkippedBundles.forEach(b -> unpackedNars.add(b.getWorkingDirectory()));
+        }
+
+        if (unpackedNars.isEmpty()) {
+            LOGGER.info("No NARs were unpacked, nothing to do");
+            return new NarLoadResult(Collections.emptySet(), Collections.emptySet());
+        }
+
+        LOGGER.info("Creating class loaders for {} NARs...", new Object[]{unpackedNars.size()});
+
+        final NarLoadResult narLoadResult = narClassLoaders.loadAdditionalNars(unpackedNars);
+        final Set<Bundle> loadedBundles = narLoadResult.getLoadedBundles();
+        final Set<BundleDetails> skippedBundles = narLoadResult.getSkippedBundles();
+
+        LOGGER.info("Successfully created class loaders for {} NARs, {} were skipped",
+                new Object[]{loadedBundles.size(), skippedBundles.size()});
+
+        // Store skipped bundles for next iteration
+        previouslySkippedBundles = new HashSet<>(skippedBundles);
+
+        if (!loadedBundles.isEmpty()) {
+            LOGGER.debug("Discovering extensions...");
+            extensionManager.discoverExtensions(loadedBundles);
+
+            // Call the DocGenerator for the classes that were loaded from each Bundle
+            for (final Bundle bundle : loadedBundles) {
+                final BundleCoordinate bundleCoordinate = bundle.getBundleDetails().getCoordinate();
+                final Set<Class> extensions = extensionManager.getTypes(bundleCoordinate);
+                if (extensions.isEmpty()) {
+                    LOGGER.debug("No documentation to generate for {} because no extensions were found",
+                            new Object[]{bundleCoordinate.getCoordinate()});
+                } else {
+                    LOGGER.debug("Generating documentation for {} extensions in {}",
+                            new Object[]{extensions.size(), bundleCoordinate.getCoordinate()});
+                    DocGenerator.documentConfigurableComponent(extensions, docsWorkingDir, extensionManager);
+                }
+            }
+
+            LOGGER.debug("Loading custom UIs for extensions...");
+            extensionUiLoader.loadExtensionUis(loadedBundles);
+        }
+
+        LOGGER.info("Finished NAR loading process!");
+        return narLoadResult;
+    }
+
+    private File unpack(final File narFile) {
+        try (final JarFile nar = new JarFile(narFile)) {
+            final Manifest manifest = nar.getManifest();
+
+            final Attributes attributes = manifest.getMainAttributes();
+            final String groupId = attributes.getValue(NarManifestEntry.NAR_GROUP.getManifestName());
+            final String narId = attributes.getValue(NarManifestEntry.NAR_ID.getManifestName());
+            final String version = attributes.getValue(NarManifestEntry.NAR_VERSION.getManifestName());
+
+            if (NarClassLoaders.FRAMEWORK_NAR_ID.equals(narId)) {
+                LOGGER.error("Found a framework NAR, will not load {}", new Object[]{narFile.getAbsolutePath()});
+                return null;
+            }
+
+            final BundleCoordinate coordinate = new BundleCoordinate(groupId, narId, version);
+
+            final Bundle bundle = extensionManager.getBundle(coordinate);
+            if (bundle != null) {
+                LOGGER.warn("Found existing bundle with coordinate {}, will not load {}",
+                        new Object[]{coordinate, narFile.getAbsolutePath()});
+                return null;
+            }
+
+            final File unpackedExtension = NarUnpacker.unpackNar(narFile, extensionsWorkingDir);
+            NarUnpacker.mapExtension(unpackedExtension, coordinate, docsWorkingDir, extensionMapping);
+            return unpackedExtension;
+
+        } catch (Exception e) {
+            LOGGER.error("Error unpacking " + narFile.getAbsolutePath(), e);
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/java/org/apache/nifi/nar/TestNarLoader.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/java/org/apache/nifi/nar/TestNarLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/java/org/apache/nifi/nar/TestNarLoader.java
new file mode 100644
index 0000000..66cb829
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/java/org/apache/nifi/nar/TestNarLoader.java
@@ -0,0 +1,189 @@
+/*
+ * 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.nar;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.processor.Processor;
+import org.apache.nifi.reporting.ReportingTask;
+import org.apache.nifi.util.NiFiProperties;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class TestNarLoader {
+
+    static final String WORK_DIR = "./target/work";
+    static final String NAR_AUTOLOAD_DIR = "./target/extensions";
+    static final String EXTENSIONS_DIR = "./src/test/resources/extensions";
+
+    private NiFiProperties properties;
+    private ExtensionMapping extensionMapping;
+
+    private StandardNarLoader narLoader;
+    private NarClassLoaders narClassLoaders;
+    private ExtensionDiscoveringManager extensionManager;
+
+    @Before
+    public void setup() throws IOException, ClassNotFoundException {
+        deleteDir(WORK_DIR);
+        deleteDir(NAR_AUTOLOAD_DIR);
+
+        final File extensionsDir = new File(NAR_AUTOLOAD_DIR);
+        assertTrue(extensionsDir.mkdirs());
+
+        // Create NiFiProperties
+        final String propertiesFile = "./src/test/resources/conf/nifi.properties";
+        properties = NiFiProperties.createBasicNiFiProperties(propertiesFile , Collections.emptyMap());
+
+        // Unpack NARs
+        final Bundle systemBundle = SystemBundle.create(properties);
+        extensionMapping = NarUnpacker.unpackNars(properties, systemBundle);
+        assertEquals(0, extensionMapping.getAllExtensionNames().size());
+
+        // Initialize NarClassLoaders
+        narClassLoaders = new NarClassLoaders();
+        narClassLoaders.init(properties.getFrameworkWorkingDirectory(), properties.getExtensionsWorkingDirectory());
+
+        extensionManager = new StandardExtensionDiscoveringManager();
+        extensionManager.discoverExtensions(systemBundle, narClassLoaders.getBundles());
+
+        // Should have Framework and Jetty NARs loaded here
+        assertEquals(2, narClassLoaders.getBundles().size());
+
+        // No extensions should be loaded yet
+        assertEquals(0, extensionManager.getExtensions(Processor.class).size());
+        assertEquals(0, extensionManager.getExtensions(ControllerService.class).size());
+        assertEquals(0, extensionManager.getExtensions(ReportingTask.class).size());
+
+        // Create class we are testing
+        narLoader = new StandardNarLoader(
+                properties.getExtensionsWorkingDirectory(),
+                properties.getComponentDocumentationWorkingDirectory(),
+                narClassLoaders,
+                extensionManager,
+                extensionMapping,
+                (bundles) -> {});
+    }
+
+    @Test
+    public void testNarLoaderWhenAllAvailable() throws IOException {
+        // Copy all NARs from src/test/resources/extensions to target/extensions
+        final File extensionsDir = new File(EXTENSIONS_DIR);
+        final Path narAutoLoadDir = Paths.get(NAR_AUTOLOAD_DIR);
+        for (final File extensionFile : extensionsDir.listFiles()) {
+            Files.copy(extensionFile.toPath(), narAutoLoadDir.resolve(extensionFile.getName()), StandardCopyOption.REPLACE_EXISTING);
+        }
+
+        final List<File> narFiles = Arrays.asList(narAutoLoadDir.toFile().listFiles());
+        assertEquals(3, narFiles.size());
+
+        final NarLoadResult narLoadResult = narLoader.load(narFiles);
+        assertNotNull(narLoadResult);
+        assertEquals(3, narLoadResult.getLoadedBundles().size());
+        assertEquals(0, narLoadResult.getSkippedBundles().size());
+
+        assertEquals(5, narClassLoaders.getBundles().size());
+        assertEquals(1, extensionManager.getExtensions(Processor.class).size());
+        assertEquals(1, extensionManager.getExtensions(ControllerService.class).size());
+        assertEquals(0, extensionManager.getExtensions(ReportingTask.class).size());
+    }
+
+    @Test
+    public void testNarLoaderWhenDependentNarsAreMissing() throws IOException {
+        final File extensionsDir = new File(EXTENSIONS_DIR);
+        final Path narAutoLoadDir = Paths.get(NAR_AUTOLOAD_DIR);
+
+        // Copy processors NAR first which depends on service API NAR
+        final File processorsNar = new File(extensionsDir, "nifi-example-processors-nar-1.0.nar");
+        final File targetProcessorNar = new File(narAutoLoadDir.toFile(), processorsNar.getName());
+        Files.copy(processorsNar.toPath(), targetProcessorNar.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+        // Attempt to load while only processor NAR is available
+        final List<File> narFiles1 = Arrays.asList(targetProcessorNar);
+        final NarLoadResult narLoadResult1 = narLoader.load(narFiles1);
+        assertNotNull(narLoadResult1);
+        assertEquals(0, narLoadResult1.getLoadedBundles().size());
+        assertEquals(1, narLoadResult1.getSkippedBundles().size());
+
+        // Copy the service impl which also depends on service API NAR
+        final File serviceImplNar = new File(extensionsDir, "nifi-example-service-nar-1.1.nar");
+        final File targetServiceImplNar = new File(narAutoLoadDir.toFile(), serviceImplNar.getName());
+        Files.copy(serviceImplNar.toPath(), targetServiceImplNar.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+        // Attempt to load while processor and service impl NARs available
+        final List<File> narFiles2 = Arrays.asList(targetServiceImplNar);
+        final NarLoadResult narLoadResult2 = narLoader.load(narFiles2);
+        assertNotNull(narLoadResult2);
+        assertEquals(0, narLoadResult2.getLoadedBundles().size());
+        assertEquals(2, narLoadResult2.getSkippedBundles().size());
+
+        // Copy service API NAR
+        final File serviceApiNar = new File(extensionsDir, "nifi-example-service-api-nar-1.0.nar");
+        final File targetServiceApiNar = new File(narAutoLoadDir.toFile(), serviceApiNar.getName());
+        Files.copy(serviceApiNar.toPath(), targetServiceApiNar.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+        // Attempt to load while all NARs available
+        final List<File> narFiles3 = Arrays.asList(targetServiceApiNar);
+        final NarLoadResult narLoadResult3 = narLoader.load(narFiles3);
+        assertNotNull(narLoadResult3);
+        assertEquals(3, narLoadResult3.getLoadedBundles().size());
+        assertEquals(0, narLoadResult3.getSkippedBundles().size());
+
+        assertEquals(5, narClassLoaders.getBundles().size());
+        assertEquals(1, extensionManager.getExtensions(Processor.class).size());
+        assertEquals(1, extensionManager.getExtensions(ControllerService.class).size());
+        assertEquals(0, extensionManager.getExtensions(ReportingTask.class).size());
+    }
+
+    private void deleteDir(String path) throws IOException {
+        Path directory = Paths.get(path);
+        if (!directory.toFile().exists()) {
+            return;
+        }
+
+        Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/README
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/README b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/README
new file mode 100644
index 0000000..1782c2f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/README
@@ -0,0 +1,9 @@
+The NARs in src/test/resources/extensions came from building the source code found in nifi-example-nars-source.zip.
+
+If you need to make a change to these NARs, perform the following steps:
+
+1) Copy nifi-example-nars-source.zip somewhere and extract it
+2) Modify code and build
+3) Replace NARs in src/test/resources/extensions with results from build in step 2
+4) Create a zip archive of the modified source code
+5) Replace nifi-example-nars-source.zip with the new zip archive
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/conf/nifi.properties
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/conf/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/conf/nifi.properties
new file mode 100644
index 0000000..987a550
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/conf/nifi.properties
@@ -0,0 +1,124 @@
+# 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.
+
+# Core Properties #
+nifi.flow.configuration.file=./target/flow.xml.gz
+nifi.flow.configuration.archive.dir=./target/archive/
+nifi.flowcontroller.autoResumeState=true
+nifi.flowcontroller.graceful.shutdown.period=10 sec
+nifi.flowservice.writedelay.interval=2 sec
+nifi.administrative.yield.duration=30 sec
+
+nifi.reporting.task.configuration.file=./target/reporting-tasks.xml
+nifi.controller.service.configuration.file=./target/controller-services.xml
+nifi.templates.directory=./target/templates
+nifi.ui.banner.text=UI Banner Text
+nifi.ui.autorefresh.interval=30 sec
+nifi.nar.library.directory=./src/test/resources/lib/
+nifi.nar.library.autoload.directory=./target/extensions
+
+nifi.nar.working.directory=./target/work/nar/
+nifi.documentation.working.directory=./target/work/docs/components
+
+# H2 Settings
+nifi.database.directory=./target/database_repository
+nifi.h2.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE
+
+# FlowFile Repository
+nifi.flowfile.repository.directory=./target/test-repo
+nifi.flowfile.repository.partitions=1
+nifi.flowfile.repository.checkpoint.interval=2 mins
+nifi.queue.swap.threshold=20000
+nifi.swap.storage.directory=./target/test-repo/swap
+nifi.swap.in.period=5 sec
+nifi.swap.in.threads=1
+nifi.swap.out.period=5 sec
+nifi.swap.out.threads=4
+
+# Content Repository
+nifi.content.claim.max.appendable.size=10 MB
+nifi.content.claim.max.flow.files=100
+nifi.content.repository.directory.default=./target/content_repository
+
+# Provenance Repository Properties
+nifi.provenance.repository.storage.directory=./target/provenance_repository
+nifi.provenance.repository.max.storage.time=24 hours
+nifi.provenance.repository.max.storage.size=1 GB
+nifi.provenance.repository.rollover.time=30 secs
+nifi.provenance.repository.rollover.size=100 MB
+
+# Site to Site properties
+nifi.remote.input.socket.port=9990
+nifi.remote.input.secure=true
+
+# web properties #
+nifi.web.war.directory=./target/lib
+nifi.web.http.host=
+nifi.web.http.port=8080
+nifi.web.https.host=
+nifi.web.https.port=
+nifi.web.jetty.working.directory=./target/work/jetty
+
+# security properties #
+nifi.sensitive.props.key=key
+nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
+nifi.sensitive.props.provider=BC
+
+nifi.security.keystore=
+nifi.security.keystoreType=
+nifi.security.keystorePasswd=
+nifi.security.keyPasswd=
+nifi.security.truststore=
+nifi.security.truststoreType=
+nifi.security.truststorePasswd=
+nifi.security.user.authorizer=
+
+# cluster common properties (cluster manager and nodes must have same values) #
+nifi.cluster.protocol.heartbeat.interval=5 sec
+nifi.cluster.protocol.is.secure=false
+nifi.cluster.protocol.socket.timeout=30 sec
+nifi.cluster.protocol.connection.handshake.timeout=45 sec
+# if multicast is used, then nifi.cluster.protocol.multicast.xxx properties must be configured #
+nifi.cluster.protocol.use.multicast=false
+nifi.cluster.protocol.multicast.address=
+nifi.cluster.protocol.multicast.port=
+nifi.cluster.protocol.multicast.service.broadcast.delay=500 ms
+nifi.cluster.protocol.multicast.service.locator.attempts=3
+nifi.cluster.protocol.multicast.service.locator.attempts.delay=1 sec
+
+# cluster node properties (only configure for cluster nodes) #
+nifi.cluster.is.node=false
+nifi.cluster.node.address=
+nifi.cluster.node.protocol.port=
+nifi.cluster.node.protocol.threads=2
+# if multicast is not used, nifi.cluster.node.unicast.xxx must have same values as nifi.cluster.manager.xxx #
+nifi.cluster.node.unicast.manager.address=
+nifi.cluster.node.unicast.manager.protocol.port=
+nifi.cluster.node.unicast.manager.authority.provider.port=
+
+# cluster manager properties (only configure for cluster manager) #
+nifi.cluster.is.manager=false
+nifi.cluster.manager.address=
+nifi.cluster.manager.protocol.port=
+nifi.cluster.manager.authority.provider.port=
+nifi.cluster.manager.authority.provider.threads=10
+nifi.cluster.manager.node.firewall.file=
+nifi.cluster.manager.node.event.history.size=10
+nifi.cluster.manager.node.api.connection.timeout=30 sec
+nifi.cluster.manager.node.api.read.timeout=30 sec
+nifi.cluster.manager.node.api.request.threads=10
+nifi.cluster.manager.flow.retrieval.delay=5 sec
+nifi.cluster.manager.protocol.threads=10
+nifi.cluster.manager.safemode.duration=0 sec

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-processors-nar-1.0.nar
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-processors-nar-1.0.nar b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-processors-nar-1.0.nar
new file mode 100644
index 0000000..317024a
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-processors-nar-1.0.nar differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-api-nar-1.0.nar
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-api-nar-1.0.nar b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-api-nar-1.0.nar
new file mode 100644
index 0000000..a540b2d
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-api-nar-1.0.nar differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-nar-1.1.nar
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-nar-1.1.nar b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-nar-1.1.nar
new file mode 100644
index 0000000..82f02a0
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/extensions/nifi-example-service-nar-1.1.nar differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-framework-nar.nar
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-framework-nar.nar b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-framework-nar.nar
new file mode 100644
index 0000000..0d0319b
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-framework-nar.nar differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-jetty-bundle.nar
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-jetty-bundle.nar b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-jetty-bundle.nar
new file mode 100644
index 0000000..6fb2c5f
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/lib/nifi-jetty-bundle.nar differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/nifi-example-nars-source.zip
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/nifi-example-nars-source.zip b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/nifi-example-nars-source.zip
new file mode 100644
index 0000000..e4477a5
Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-loading-utils/src/test/resources/nifi-example-nars-source.zip differ

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializerFactory.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializerFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializerFactory.java
index f6ab922..61d1073 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializerFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ConfigurableComponentInitializerFactory.java
@@ -18,6 +18,7 @@ package org.apache.nifi.init;
 
 import org.apache.nifi.components.ConfigurableComponent;
 import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.processor.Processor;
 import org.apache.nifi.reporting.ReportingTask;
 
@@ -30,13 +31,14 @@ public class ConfigurableComponentInitializerFactory {
      * @param componentClass the class that requires a ConfigurableComponentInitializer
      * @return a ConfigurableComponentInitializer capable of initializing that specific type of class
      */
-    public static ConfigurableComponentInitializer createComponentInitializer(final Class<? extends ConfigurableComponent> componentClass) {
+    public static ConfigurableComponentInitializer createComponentInitializer(
+            final ExtensionManager extensionManager, final Class<? extends ConfigurableComponent> componentClass) {
         if (Processor.class.isAssignableFrom(componentClass)) {
-            return new ProcessorInitializer();
+            return new ProcessorInitializer(extensionManager);
         } else if (ControllerService.class.isAssignableFrom(componentClass)) {
-            return new ControllerServiceInitializer();
+            return new ControllerServiceInitializer(extensionManager);
         } else if (ReportingTask.class.isAssignableFrom(componentClass)) {
-            return new ReportingTaskingInitializer();
+            return new ReportingTaskingInitializer(extensionManager);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/nifi/blob/fdd8cdbb/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ControllerServiceInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ControllerServiceInitializer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ControllerServiceInitializer.java
index 21b107f..1c3f136 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ControllerServiceInitializer.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/init/ControllerServiceInitializer.java
@@ -35,25 +35,31 @@ import org.apache.nifi.reporting.InitializationException;
  */
 public class ControllerServiceInitializer implements ConfigurableComponentInitializer {
 
+    private final ExtensionManager extensionManager;
+
+    public ControllerServiceInitializer(final ExtensionManager extensionManager) {
+        this.extensionManager = extensionManager;
+    }
+
     @Override
     public void initialize(ConfigurableComponent component) throws InitializationException {
         ControllerService controllerService = (ControllerService) component;
         ControllerServiceInitializationContext context = new MockControllerServiceInitializationContext();
-        try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(component.getClass(), context.getIdentifier())) {
+        try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(extensionManager, component.getClass(), context.getIdentifier())) {
             controllerService.initialize(context);
         }
     }
 
     @Override
     public void teardown(ConfigurableComponent component) {
-        try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(component.getClass(), component.getIdentifier())) {
+        try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(extensionManager, component.getClass(), component.getIdentifier())) {
             ControllerService controllerService = (ControllerService) component;
 
             final ComponentLog logger = new MockComponentLogger();
             final MockConfigurationContext context = new MockConfigurationContext();
             ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, controllerService, logger, context);
         } finally {
-            ExtensionManager.removeInstanceClassLoader(component.getIdentifier());
+            extensionManager.removeInstanceClassLoader(component.getIdentifier());
         }
     }
 }