This is an automated email from the ASF dual-hosted git repository.
briansolo1985 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new c05cf69585 NIFI-14364 MiNiFi Nar auto unload in case of the extension
is removed from the nar auto load directory
c05cf69585 is described below
commit c05cf69585de8292d0f19078a7779e937009c8ba
Author: Peter Kedvessy <[email protected]>
AuthorDate: Thu Mar 6 09:42:30 2025 +0100
NIFI-14364 MiNiFi Nar auto unload in case of the extension is removed from
the nar auto load directory
This closes #9800.
Signed-off-by: Ferenc Kis <[email protected]>
---
.../main/assembly/dependencies-windows-service.xml | 3 +
.../src/main/assembly/dependencies.xml | 3 +
.../minifi-framework/minifi-framework-core/pom.xml | 2 +-
.../minifi-framework/minifi-nar-unloader/pom.xml | 66 +++++++++++++++
.../nifi/minifi/nar/NarAutoUnloadService.java | 64 +++++++++++++++
.../apache/nifi/minifi/nar/NarAutoUnloader.java | 58 +++++++++++++
.../nifi/minifi/nar/NarAutoUnloaderTask.java | 95 ++++++++++++++++++++++
.../minifi/nar/NarAutoUnloaderTaskFactory.java | 55 +++++++++++++
.../nifi/minifi/nar/NarAutoUnloadServiceTest.java | 93 +++++++++++++++++++++
.../minifi-framework/minifi-runtime/pom.xml | 6 +-
.../minifi-framework/minifi-server/pom.xml | 12 +++
.../apache/nifi/minifi/StandardMiNiFiServer.java | 39 +++++++++
.../minifi-framework/pom.xml | 1 +
13 files changed, 495 insertions(+), 2 deletions(-)
diff --git
a/minifi/minifi-assembly/src/main/assembly/dependencies-windows-service.xml
b/minifi/minifi-assembly/src/main/assembly/dependencies-windows-service.xml
index 02068b4e41..ed6a5a0473 100644
--- a/minifi/minifi-assembly/src/main/assembly/dependencies-windows-service.xml
+++ b/minifi/minifi-assembly/src/main/assembly/dependencies-windows-service.xml
@@ -50,10 +50,13 @@
<include>org.apache.nifi:nifi-server-api</include>
<include>org.apache.nifi:nifi-stateless-api</include>
<include>org.apache.nifi:*:nar</include>
+
<include>org.apache.nifi:nifi-framework-nar-loading-utils</include>
+ <include>org.apache.nifi:nifi-framework-nar-utils</include>
<!-- Apache NiFi MiNiFi -->
<include>org.apache.nifi.minifi:minifi-commons-utils</include>
<include>org.apache.nifi.minifi:minifi-framework-api</include>
<include>org.apache.nifi.minifi:minifi-runtime</include>
+ <include>org.apache.nifi.minifi:minifi-nar-unloader</include>
<include>org.apache.nifi.minifi:*:nar</include>
<include>org.apache.nifi:nifi-mqtt:nar</include>
<!-- Unfortunately minifi-runtime depends on jackson databind
and annotations
diff --git a/minifi/minifi-assembly/src/main/assembly/dependencies.xml
b/minifi/minifi-assembly/src/main/assembly/dependencies.xml
index b7e0296181..38e1aa61d2 100644
--- a/minifi/minifi-assembly/src/main/assembly/dependencies.xml
+++ b/minifi/minifi-assembly/src/main/assembly/dependencies.xml
@@ -50,11 +50,14 @@
<include>org.apache.nifi:nifi-runtime</include>
<include>org.apache.nifi:nifi-server-api</include>
<include>org.apache.nifi:nifi-stateless-api</include>
+
<include>org.apache.nifi:nifi-framework-nar-loading-utils</include>
+ <include>org.apache.nifi:nifi-framework-nar-utils</include>
<include>org.apache.nifi:*:nar</include>
<!-- Apache NiFi MiNiFi -->
<include>org.apache.nifi.minifi:minifi-commons-utils</include>
<include>org.apache.nifi.minifi:minifi-framework-api</include>
<include>org.apache.nifi.minifi:minifi-runtime</include>
+ <include>org.apache.nifi.minifi:minifi-nar-unloader</include>
<include>org.apache.nifi.minifi:*:nar</include>
<include>org.apache.nifi:nifi-mqtt:nar</include>
<!-- Unfortunately minifi-runtime depends on jackson databind
and annotations
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/pom.xml
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/pom.xml
index 1312b64cb5..673821c593 100644
---
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/pom.xml
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-framework-core/pom.xml
@@ -156,4 +156,4 @@ limitations under the License.
<scope>test</scope>
</dependency>
</dependencies>
-</project>
+</project>
\ No newline at end of file
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/pom.xml
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/pom.xml
new file mode 100644
index 0000000000..b9d1c973e8
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.nifi.minifi</groupId>
+ <artifactId>minifi-framework</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>minifi-nar-unloader</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-properties</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi.minifi</groupId>
+ <artifactId>minifi-commons-utils</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi.minifi</groupId>
+ <artifactId>minifi-commons-api</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-nar-utils</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-framework-nar-loading-utils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-python-framework-api</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloadService.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloadService.java
new file mode 100644
index 0000000000..6a1c56891f
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloadService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.minifi.nar;
+
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.File;
+
+public class NarAutoUnloadService {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(NarAutoUnloadService.class);
+ private static final String UNPACKED_POSTFIX = "-unpacked";
+
+ private final ExtensionManager extensionManager;
+ private final File extensionWorkDirectory;
+ private final NarLoader narLoader;
+
+ public NarAutoUnloadService(ExtensionManager extensionManager, File
extensionWorkDirectory, NarLoader narLoader) {
+ this.extensionManager = extensionManager;
+ this.extensionWorkDirectory = extensionWorkDirectory;
+ this.narLoader = narLoader;
+ }
+
+ public void unloadNarFile(String fileName) {
+ if (isSupported(fileName)) {
+ File narWorkingDirectory = new File(extensionWorkDirectory,
fileName + UNPACKED_POSTFIX);
+ extensionManager.getAllBundles().stream()
+ .filter(bundle ->
bundle.getBundleDetails().getWorkingDirectory().getPath().equals(narWorkingDirectory.getPath()))
+ .findFirst()
+ .ifPresentOrElse(narLoader::unload, () -> LOGGER.warn("NAR
bundle not found for {}", fileName));
+ }
+ }
+
+ private boolean isSupported(String fileName) {
+ if (!fileName.endsWith(".nar")) {
+ LOGGER.info("Skipping non-nar file {}", fileName);
+ return false;
+ } else if (fileName.startsWith(".")) {
+ LOGGER.debug("Skipping partially written file {}", fileName);
+ return false;
+ }
+ return true;
+ }
+
+
+
+}
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloader.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloader.java
new file mode 100644
index 0000000000..70390b56e0
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloader.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.minifi.nar;
+
+import static java.util.Objects.requireNonNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NarAutoUnloader {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(NarAutoUnloader.class);
+ private final NarAutoUnloaderTaskFactory narAutoUnloaderTaskFactory;
+
+ private NarAutoUnloaderTask narAutoUnLoaderTask;
+ private boolean started = false;
+
+ public NarAutoUnloader(NarAutoUnloaderTaskFactory
narAutoUnloaderTaskFactory) {
+ this.narAutoUnloaderTaskFactory =
requireNonNull(narAutoUnloaderTaskFactory);
+ }
+
+ public synchronized void start() throws Exception {
+ if (!started) {
+ narAutoUnLoaderTask =
narAutoUnloaderTaskFactory.createNarAutoUnloaderTask();
+
+ LOGGER.info("Starting NAR Auto-Unloader Thread for directory {}",
narAutoUnLoaderTask.getAutoLoadPath());
+
+ Thread.ofVirtual().name("NAR
Auto-Unloader").start(narAutoUnLoaderTask);
+
+ started = true;
+ }
+ }
+
+ public synchronized void stop() {
+ started = false;
+ if (narAutoUnLoaderTask != null) {
+ narAutoUnLoaderTask.stop();
+ narAutoUnLoaderTask = null;
+ }
+
+ LOGGER.info("NAR Auto-Unloader stopped");
+ }
+}
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTask.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTask.java
new file mode 100644
index 0000000000..6d15d8f95c
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTask.java
@@ -0,0 +1,95 @@
+/*
+ * 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.minifi.nar;
+
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.util.Objects.requireNonNull;
+
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NarAutoUnloaderTask implements Runnable {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(NarAutoUnloaderTask.class);
+ private static final long POLL_INTERVAL_MS = 5000;
+
+ private final Path autoLoadPath;
+ private final WatchService watchService;
+ private final NarAutoUnloadService narAutoUnloadService;
+
+ private volatile boolean stopped = false;
+
+ public NarAutoUnloaderTask(
+ Path autoLoadPath,
+ WatchService watchService,
+ NarAutoUnloadService narAutoUnloadService) {
+ this.autoLoadPath = requireNonNull(autoLoadPath);
+ this.watchService = requireNonNull(watchService);
+ this.narAutoUnloadService = requireNonNull(narAutoUnloadService);
+ }
+
+ @Override
+ public void run() {
+ while (!stopped) {
+ try {
+ WatchKey key;
+ try {
+ LOGGER.debug("Polling for removed NARs at {}",
autoLoadPath);
+ key = watchService.poll(POLL_INTERVAL_MS,
TimeUnit.MILLISECONDS);
+ } catch (InterruptedException x) {
+ LOGGER.info("WatchService interrupted, returning...");
+ return;
+ }
+ if (key != null) {
+ for (WatchEvent<?> event : key.pollEvents()) {
+ if (event.kind() == ENTRY_DELETE) {
+
narAutoUnloadService.unloadNarFile(getFileName(event));
+ }
+ }
+ if (!key.reset()) {
+ LOGGER.error("NAR auto-load directory is no longer
valid");
+ stop();
+ }
+ }
+ } catch (final Throwable t) {
+ LOGGER.error("Error un-loading NARs", t);
+ }
+ }
+ }
+
+ public void stop() {
+ LOGGER.info("Stopping NAR Auto-Unloader");
+ stopped = true;
+ }
+
+ public Path getAutoLoadPath() {
+ return autoLoadPath;
+ }
+
+ private String getFileName(WatchEvent<?> event) {
+ WatchEvent<Path> ev = (WatchEvent<Path>) event;
+ Path filename = ev.context();
+ Path autoUnLoadFile = autoLoadPath.resolve(filename);
+ return autoUnLoadFile.toFile().getName().toLowerCase();
+ }
+}
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTaskFactory.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTaskFactory.java
new file mode 100644
index 0000000000..4052165b91
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/main/java/org/apache/nifi/minifi/nar/NarAutoUnloaderTaskFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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.minifi.nar;
+
+import static java.util.Objects.requireNonNull;
+
+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 org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarLoader;
+import org.apache.nifi.util.FileUtils;
+import org.apache.nifi.util.NiFiProperties;
+
+public class NarAutoUnloaderTaskFactory {
+
+ private final NiFiProperties properties;
+ private final ExtensionManager extensionManager;
+ private final NarLoader narLoader;
+
+ public NarAutoUnloaderTaskFactory(NiFiProperties properties,
ExtensionManager extensionManager, NarLoader narLoader) {
+ this.properties = requireNonNull(properties);
+ this.extensionManager = requireNonNull(extensionManager);
+ this.narLoader = requireNonNull(narLoader);
+ }
+
+ public NarAutoUnloaderTask createNarAutoUnloaderTask() throws IOException {
+ File autoLoadDir = properties.getNarAutoLoadDirectory();
+ FileUtils.ensureDirectoryExistAndCanRead(autoLoadDir);
+ WatchService watcher = FileSystems.getDefault().newWatchService();
+ Path autoLoadPath = autoLoadDir.toPath();
+ autoLoadPath.register(watcher, StandardWatchEventKinds.ENTRY_DELETE);
+ NarAutoUnloadService narAutoUnloadService = new
NarAutoUnloadService(extensionManager,
properties.getExtensionsWorkingDirectory(), narLoader);
+
+ return new NarAutoUnloaderTask(autoLoadPath, watcher,
narAutoUnloadService);
+ }
+}
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/test/java/org/apache/nifi/minifi/nar/NarAutoUnloadServiceTest.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/test/java/org/apache/nifi/minifi/nar/NarAutoUnloadServiceTest.java
new file mode 100644
index 0000000000..45e95166f0
--- /dev/null
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-nar-unloader/src/test/java/org/apache/nifi/minifi/nar/NarAutoUnloadServiceTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.minifi.nar;
+
+import java.io.File;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleDetails;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.NarLoader;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+
+@ExtendWith(MockitoExtension.class)
+public class NarAutoUnloadServiceTest {
+
+ private static final String SUPPORTED_FILENAME = "test.nar";
+ private static final String UNPACKED_POSTFIX = "-unpacked";
+
+ @Mock
+ private ExtensionManager extensionManager;
+ @Mock
+ private NarLoader narLoader;
+ private File extensionWorkDirectory;
+
+ private NarAutoUnloadService victim;
+
+ @BeforeEach
+ public void initTest() {
+ extensionWorkDirectory = new File(".");
+ victim = new NarAutoUnloadService(extensionManager,
extensionWorkDirectory, narLoader);
+ }
+
+ @ParameterizedTest
+ @MethodSource("notSupportedFileNames")
+ public void testNotSupportedFileDoesNotTriggerUnload(String fileName) {
+
+ victim.unloadNarFile(fileName);
+
+ verifyNoInteractions(extensionManager, narLoader);
+ }
+
+ @Test
+ public void testUnload() {
+ Bundle bundle = mock(Bundle.class);
+ BundleDetails bundleDetails = mock(BundleDetails.class);
+ File workingDirectory = new File(extensionWorkDirectory,
SUPPORTED_FILENAME + UNPACKED_POSTFIX);
+
+ when(extensionManager.getAllBundles()).thenReturn(Set.of(bundle));
+ when(bundle.getBundleDetails()).thenReturn(bundleDetails);
+ when(bundleDetails.getWorkingDirectory()).thenReturn(workingDirectory);
+
+ victim.unloadNarFile(SUPPORTED_FILENAME);
+
+ verify(narLoader).unload(bundle);
+ }
+
+ private static Stream<Arguments> notSupportedFileNames() {
+ return Stream.of(
+ Arguments.of("nar.jar"),
+ Arguments.of(".test.nar")
+ );
+ }
+}
\ No newline at end of file
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
index a2432d70aa..8a1241a949 100644
---
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
@@ -45,6 +45,11 @@ limitations under the License.
<artifactId>minifi-flow-status-report</artifactId>
<version>2.4.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi.minifi</groupId>
+ <artifactId>minifi-nar-unloader</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-nar-utils</artifactId>
@@ -82,6 +87,5 @@ limitations under the License.
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
-
</dependencies>
</project>
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/pom.xml
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/pom.xml
index ddb0d16e61..3fe3b3a07c 100644
---
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/pom.xml
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/pom.xml
@@ -51,6 +51,18 @@
<artifactId>nifi-framework-api</artifactId>
<version>2.4.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-framework-nar-utils</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-framework-nar-loading-utils</artifactId>
+ <version>2.4.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId>
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/src/main/java/org/apache/nifi/minifi/StandardMiNiFiServer.java
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/src/main/java/org/apache/nifi/minifi/StandardMiNiFiServer.java
index b922105d46..396931bb14 100644
---
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/src/main/java/org/apache/nifi/minifi/StandardMiNiFiServer.java
+++
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-server/src/main/java/org/apache/nifi/minifi/StandardMiNiFiServer.java
@@ -20,11 +20,14 @@ package org.apache.nifi.minifi;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.nifi.minifi.validator.FlowValidator.validate;
+import static org.apache.nifi.nar.NarUnpackMode.UNPACK_INDIVIDUAL_JARS;
+import static org.apache.nifi.nar.NarUnpackMode.UNPACK_TO_UBER_JAR;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Optional;
+
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.headless.HeadlessNiFiServer;
@@ -32,8 +35,18 @@ import org.apache.nifi.minifi.bootstrap.BootstrapListener;
import org.apache.nifi.minifi.c2.C2NifiClientService;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
import org.apache.nifi.minifi.commons.status.FlowStatusReport;
+import org.apache.nifi.minifi.nar.NarAutoUnloader;
+import org.apache.nifi.minifi.nar.NarAutoUnloaderTaskFactory;
import org.apache.nifi.minifi.status.StatusConfigReporter;
import org.apache.nifi.minifi.status.StatusRequestException;
+import org.apache.nifi.nar.ExtensionDiscoveringManager;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.nar.ExtensionManagerHolder;
+import org.apache.nifi.nar.ExtensionMapping;
+import org.apache.nifi.nar.NarClassLoadersHolder;
+import org.apache.nifi.nar.NarLoader;
+import org.apache.nifi.nar.NarUnpackMode;
+import org.apache.nifi.nar.StandardNarLoader;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,6 +59,7 @@ public class StandardMiNiFiServer extends HeadlessNiFiServer
implements MiNiFiSe
private BootstrapListener bootstrapListener;
private C2NifiClientService c2NifiClientService;
+ private NarAutoUnloader narAutoUnloader;
@Override
public void start() {
@@ -55,6 +69,7 @@ public class StandardMiNiFiServer extends HeadlessNiFiServer
implements MiNiFiSe
initC2();
sendStartedStatus();
startHeartbeat();
+ startNarAutoUnloader();
}
@Override
@@ -84,6 +99,9 @@ public class StandardMiNiFiServer extends HeadlessNiFiServer
implements MiNiFiSe
if (c2NifiClientService != null) {
c2NifiClientService.stop();
}
+ if (narAutoUnloader != null) {
+ narAutoUnloader.stop();
+ }
}
public FlowStatusReport getStatusReport(String requestString) throws
StatusRequestException {
@@ -157,4 +175,25 @@ public class StandardMiNiFiServer extends
HeadlessNiFiServer implements MiNiFiSe
}
}
}
+
+ private void startNarAutoUnloader() {
+ try {
+ NiFiProperties properties = getNiFiProperties();
+ ExtensionManager extensionManager =
ExtensionManagerHolder.getExtensionManager();
+ NarUnpackMode unpackMode = properties.isUnpackNarsToUberJar() ?
UNPACK_TO_UBER_JAR : UNPACK_INDIVIDUAL_JARS;
+ NarLoader narLoader = new StandardNarLoader(
+ properties.getExtensionsWorkingDirectory(),
+ NarClassLoadersHolder.getInstance(),
+ (ExtensionDiscoveringManager) extensionManager,
+ new ExtensionMapping(),
+ null,
+ unpackMode);
+
+ NarAutoUnloaderTaskFactory narAutoUnLoaderTaskFactory = new
NarAutoUnloaderTaskFactory(properties, extensionManager, narLoader);
+ narAutoUnloader = new NarAutoUnloader(narAutoUnLoaderTaskFactory);
+ narAutoUnloader.start();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
diff --git
a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
index d11d3ec96e..af29968f67 100644
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
@@ -31,5 +31,6 @@ limitations under the License.
<module>minifi-resources</module>
<module>minifi-server</module>
<module>minifi-properties-loader</module>
+ <module>minifi-nar-unloader</module>
</modules>
</project>
\ No newline at end of file