This is an automated email from the ASF dual-hosted git repository.
mcgilman pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/NIFI-15258 by this push:
new 8ff7ded679 NIFI-15606: Connector Mock War implementation. (#10907)
8ff7ded679 is described below
commit 8ff7ded679fb36d0ff7551494dab967a822bb3d4
Author: Bob Paulin <[email protected]>
AuthorDate: Tue Feb 17 13:02:20 2026 -0600
NIFI-15606: Connector Mock War implementation. (#10907)
* NIFI-15606: Connector Mock War implementation.
* Load war files from Connector Nar
* Mock Connector Web Context
* Allow test runner to set http port
* NIFI-15606: Connector Mock War implementation.
* Code Review Feedback
---
.../mock/connector/server/ConnectorTestRunner.java | 9 +
.../nifi-connector-mock-server/pom.xml | 9 +-
.../server/MockNiFiConnectorWebContext.java | 52 ++++++
.../server/StandardConnectorMockServer.java | 130 +++++++++++++-
.../server/MockNiFiConnectorWebContextTest.java | 80 +++++++++
.../StandardConnectorMockServerJettyTest.java | 186 +++++++++++++++++++++
.../connector/StandardConnectorTestRunner.java | 21 ++-
7 files changed, 484 insertions(+), 3 deletions(-)
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock-api/src/main/java/org/apache/nifi/mock/connector/server/ConnectorTestRunner.java
b/nifi-connector-mock-bundle/nifi-connector-mock-api/src/main/java/org/apache/nifi/mock/connector/server/ConnectorTestRunner.java
index 25a5215db3..22b122402d 100644
---
a/nifi-connector-mock-bundle/nifi-connector-mock-api/src/main/java/org/apache/nifi/mock/connector/server/ConnectorTestRunner.java
+++
b/nifi-connector-mock-bundle/nifi-connector-mock-api/src/main/java/org/apache/nifi/mock/connector/server/ConnectorTestRunner.java
@@ -68,4 +68,13 @@ public interface ConnectorTestRunner extends Closeable {
void waitForIdle(Duration minimumIdleTime, Duration maxWaitTime);
List<ValidationResult> validate();
+
+ /**
+ * Returns the HTTP port on which the embedded Jetty server is listening,
or -1 if no server is running.
+ *
+ * @return the HTTP port, or -1 if not applicable
+ */
+ default int getHttpPort() {
+ return -1;
+ }
}
diff --git a/nifi-connector-mock-bundle/nifi-connector-mock-server/pom.xml
b/nifi-connector-mock-bundle/nifi-connector-mock-server/pom.xml
index 3b5704bf2a..bcfb88f3e5 100644
--- a/nifi-connector-mock-bundle/nifi-connector-mock-server/pom.xml
+++ b/nifi-connector-mock-bundle/nifi-connector-mock-server/pom.xml
@@ -60,7 +60,14 @@
<artifactId>nifi-server-api</artifactId>
<version>2.9.0-SNAPSHOT</version>
</dependency>
-
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.ee11</groupId>
+ <artifactId>jetty-ee11-webapp</artifactId>
+ </dependency>
<!-- Test Dependencies -->
<dependency>
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContext.java
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContext.java
new file mode 100644
index 0000000000..9e8891ee52
--- /dev/null
+++
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContext.java
@@ -0,0 +1,52 @@
+/*
+ * 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.mock.connector.server;
+
+import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.ConnectorRepository;
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.web.NiFiConnectorWebContext;
+
+/**
+ * Mock implementation of {@link NiFiConnectorWebContext} for the connector
test runner.
+ * Provides direct access to the connector instance and its flow contexts
without
+ * authorization proxying, since the mock server uses a permit-all authorizer.
+ */
+public class MockNiFiConnectorWebContext implements NiFiConnectorWebContext {
+
+ private final ConnectorRepository connectorRepository;
+
+ public MockNiFiConnectorWebContext(final ConnectorRepository
connectorRepository) {
+ this.connectorRepository = connectorRepository;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> ConnectorWebContext<T> getConnectorWebContext(final String
connectorId) throws IllegalArgumentException {
+ final ConnectorNode connectorNode =
connectorRepository.getConnector(connectorId);
+ if (connectorNode == null) {
+ throw new IllegalArgumentException("Unable to find connector with
id: " + connectorId);
+ }
+
+ final T connector = (T) connectorNode.getConnector();
+ final FlowContext workingFlowContext =
connectorNode.getWorkingFlowContext();
+ final FlowContext activeFlowContext =
connectorNode.getActiveFlowContext();
+
+ return new ConnectorWebContext<>(connector, workingFlowContext,
activeFlowContext);
+ }
+}
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServer.java
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServer.java
index a4cbf8b5d9..f71ab45d51 100644
---
a/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServer.java
+++
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/main/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServer.java
@@ -17,6 +17,7 @@
package org.apache.nifi.mock.connector.server;
+import jakarta.servlet.ServletContext;
import org.apache.nifi.admin.service.AuditService;
import org.apache.nifi.asset.Asset;
import org.apache.nifi.asset.AssetManager;
@@ -44,6 +45,7 @@ import org.apache.nifi.components.state.StateManagerProvider;
import org.apache.nifi.components.validation.DisabledServiceValidationResult;
import org.apache.nifi.components.validation.ValidationState;
import org.apache.nifi.connectable.FlowFileTransferCounts;
+import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.controller.DecommissionTask;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.metrics.DefaultComponentMetricReporter;
@@ -56,19 +58,29 @@ import org.apache.nifi.diagnostics.DiagnosticsFactory;
import org.apache.nifi.encrypt.PropertyEncryptor;
import org.apache.nifi.engine.FlowEngine;
import org.apache.nifi.events.VolatileBulletinRepository;
-import org.apache.nifi.controller.ControllerService;
import
org.apache.nifi.mock.connector.server.secrets.ConnectorTestRunnerSecretsManager;
import org.apache.nifi.nar.ExtensionMapping;
import org.apache.nifi.processor.Processor;
import org.apache.nifi.reporting.BulletinRepository;
import org.apache.nifi.util.NiFiProperties;
import org.apache.nifi.validation.RuleViolationsManager;
+import org.apache.nifi.web.NiFiConnectorWebContext;
+import org.eclipse.jetty.ee.webapp.WebAppClassLoader;
+import org.eclipse.jetty.ee11.webapp.WebAppContext;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -76,9 +88,16 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.jar.JarFile;
+import java.util.stream.Stream;
public class StandardConnectorMockServer implements ConnectorMockServer {
+ private static final Logger logger =
LoggerFactory.getLogger(StandardConnectorMockServer.class);
+
private static final String CONNECTOR_ID = "test-connector";
+ private static final String NAR_DEPENDENCIES_PATH =
"NAR-INF/bundled-dependencies";
+ private static final String CONNECTOR_WAR_MANIFEST_PATH =
"META-INF/nifi-connector";
+ private static final String WAR_EXTENSION = ".war";
private Bundle systemBundle;
private Set<Bundle> bundles;
@@ -90,6 +109,7 @@ public class StandardConnectorMockServer implements
ConnectorMockServer {
private FlowEngine flowEngine;
private MockExtensionMapper mockExtensionMapper;
private FlowFileTransferCounts initialFlowFileTransferCounts = new
FlowFileTransferCounts(0L, 0L, 0L, 0L);
+ private Server jettyServer;
@Override
public void start() {
@@ -136,6 +156,8 @@ public class StandardConnectorMockServer implements
ConnectorMockServer {
((MockConnectorRepository)
connectorRepository).setMockExtensionMapper(mockExtensionMapper);
flowEngine = new FlowEngine(4, "Connector Threads");
+
+ startJettyServer();
}
@Override
@@ -147,6 +169,14 @@ public class StandardConnectorMockServer implements
ConnectorMockServer {
@Override
public void stop() {
+ if (jettyServer != null) {
+ try {
+ jettyServer.stop();
+ logger.info("Jetty server stopped");
+ } catch (final Exception e) {
+ logger.warn("Failed to stop Jetty server", e);
+ }
+ }
if (flowEngine != null) {
flowEngine.shutdown();
}
@@ -401,8 +431,106 @@ public class StandardConnectorMockServer implements
ConnectorMockServer {
extensionManager.addControllerService(mockControllerServiceClass);
}
+ @Override
+ public int getHttpPort() {
+ if (jettyServer == null) {
+ return -1;
+ }
+
+ final ServerConnector connector = (ServerConnector)
jettyServer.getConnectors()[0];
+ return connector.getLocalPort();
+ }
+
@Override
public void close() {
stop();
}
+
+ private void startJettyServer() {
+ final String httpPortValue =
nifiProperties.getProperty(NiFiProperties.WEB_HTTP_PORT);
+ if (httpPortValue == null || httpPortValue.isBlank()) {
+ logger.debug("No HTTP port configured; skipping Jetty server
startup");
+ return;
+ }
+
+ final int httpPort = Integer.parseInt(httpPortValue);
+ final Map<File, Bundle> wars = findWars(bundles);
+ if (wars.isEmpty()) {
+ logger.debug("No WAR files found in NAR bundles; skipping Jetty
server startup");
+ return;
+ }
+
+ jettyServer = new Server();
+
+ final ServerConnector serverConnector = new
ServerConnector(jettyServer);
+ serverConnector.setPort(httpPort);
+ jettyServer.addConnector(serverConnector);
+
+ final List<WebAppContext> webAppContexts = new ArrayList<>();
+ final ContextHandlerCollection handlers = new
ContextHandlerCollection();
+ for (final Map.Entry<File, Bundle> entry : wars.entrySet()) {
+ final File warFile = entry.getKey();
+ final Bundle bundle = entry.getValue();
+
+ final String warName = warFile.getName();
+ final String contextPath = "/" + warName.substring(0,
warName.length() - WAR_EXTENSION.length());
+
+ final WebAppContext webAppContext = new
WebAppContext(warFile.getPath(), contextPath);
+ webAppContext.setClassLoader(new
WebAppClassLoader(bundle.getClassLoader(), webAppContext));
+
+ handlers.addHandler(webAppContext);
+ webAppContexts.add(webAppContext);
+ logger.info("Deploying WAR [{}] at context path [{}]",
warFile.getAbsolutePath(), contextPath);
+ }
+
+ jettyServer.setHandler(handlers);
+
+ try {
+ jettyServer.start();
+ logger.info("Jetty server started on port [{}]", getHttpPort());
+ } catch (final Exception e) {
+ throw new RuntimeException("Failed to start Jetty server", e);
+ }
+
+ performInjectionForConnectorUis(webAppContexts);
+ }
+
+ private void performInjectionForConnectorUis(final List<WebAppContext>
webAppContexts) {
+ final NiFiConnectorWebContext connectorWebContext = new
MockNiFiConnectorWebContext(connectorRepository);
+ for (final WebAppContext webAppContext : webAppContexts) {
+ final ServletContext servletContext =
webAppContext.getServletHandler().getServletContext();
+ servletContext.setAttribute("nifi-connector-web-context",
connectorWebContext);
+ logger.info("Injected NiFiConnectorWebContext into WAR context
[{}]", webAppContext.getContextPath());
+ }
+ }
+
+ public Map<File, Bundle> findWars(final Set<Bundle> bundles) {
+ final Map<File, Bundle> wars = new HashMap<>();
+
+ bundles.forEach(bundle -> {
+ final BundleDetails details = bundle.getBundleDetails();
+ final Path bundledDependencies = new
File(details.getWorkingDirectory(), NAR_DEPENDENCIES_PATH).toPath();
+ if (Files.isDirectory(bundledDependencies)) {
+ try (final Stream<Path> dependencies =
Files.list(bundledDependencies)) {
+ dependencies.filter(dependency ->
dependency.getFileName().toString().endsWith(WAR_EXTENSION))
+ .map(Path::toFile)
+ .filter(this::isConnectorWar)
+ .forEach(dependency -> wars.put(dependency,
bundle));
+ } catch (final IOException e) {
+ logger.warn("Failed to find WAR files in
bundled-dependencies [{}]", bundledDependencies, e);
+ }
+ }
+ });
+
+ return wars;
+ }
+
+ private boolean isConnectorWar(final File warFile) {
+ try (final JarFile jarFile = new JarFile(warFile)) {
+ return jarFile.getJarEntry(CONNECTOR_WAR_MANIFEST_PATH) != null;
+ } catch (final IOException e) {
+ logger.warn("Unable to inspect WAR file [{}] for connector
manifest", warFile, e);
+ return false;
+ }
+ }
}
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContextTest.java
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContextTest.java
new file mode 100644
index 0000000000..659fe2e8fb
--- /dev/null
+++
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/MockNiFiConnectorWebContextTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.mock.connector.server;
+
+import org.apache.nifi.components.connector.Connector;
+import org.apache.nifi.components.connector.ConnectorNode;
+import org.apache.nifi.components.connector.ConnectorRepository;
+import org.apache.nifi.components.connector.FrameworkFlowContext;
+import org.apache.nifi.web.NiFiConnectorWebContext;
+import org.apache.nifi.web.NiFiConnectorWebContext.ConnectorWebContext;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class MockNiFiConnectorWebContextTest {
+
+ private static final String CONNECTOR_ID = "test-connector";
+
+ @Mock
+ private ConnectorRepository connectorRepository;
+
+ @Mock
+ private ConnectorNode connectorNode;
+
+ @Mock
+ private Connector connector;
+
+ @Mock
+ private FrameworkFlowContext workingFlowContext;
+
+ @Mock
+ private FrameworkFlowContext activeFlowContext;
+
+ @Test
+ void testGetConnectorWebContextReturnsConnectorAndFlowContexts() {
+
when(connectorRepository.getConnector(CONNECTOR_ID)).thenReturn(connectorNode);
+ when(connectorNode.getConnector()).thenReturn(connector);
+
when(connectorNode.getWorkingFlowContext()).thenReturn(workingFlowContext);
+
when(connectorNode.getActiveFlowContext()).thenReturn(activeFlowContext);
+
+ final NiFiConnectorWebContext context = new
MockNiFiConnectorWebContext(connectorRepository);
+ final ConnectorWebContext<Connector> result =
context.getConnectorWebContext(CONNECTOR_ID);
+
+ assertNotNull(result);
+ assertEquals(connector, result.connector());
+ assertEquals(workingFlowContext, result.workingFlowContext());
+ assertEquals(activeFlowContext, result.activeFlowContext());
+ }
+
+ @Test
+ void testGetConnectorWebContextThrowsForUnknownConnector() {
+ when(connectorRepository.getConnector("unknown-id")).thenReturn(null);
+
+ final NiFiConnectorWebContext context = new
MockNiFiConnectorWebContext(connectorRepository);
+
+ assertThrows(IllegalArgumentException.class, () ->
context.getConnectorWebContext("unknown-id"));
+ }
+}
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServerJettyTest.java
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServerJettyTest.java
new file mode 100644
index 0000000000..980d8341c9
--- /dev/null
+++
b/nifi-connector-mock-bundle/nifi-connector-mock-server/src/test/java/org/apache/nifi/mock/connector/server/StandardConnectorMockServerJettyTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.mock.connector.server;
+
+import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleCoordinate;
+import org.apache.nifi.bundle.BundleDetails;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class StandardConnectorMockServerJettyTest {
+
+ private static final String NAR_DEPENDENCIES_PATH =
"NAR-INF/bundled-dependencies";
+
+ @TempDir
+ private Path tempDir;
+
+ @Test
+ void testFindWarsDiscoversSingleWarFile() throws Exception {
+ final Path bundleWorkingDir = tempDir.resolve("test-bundle");
+ final Path depsDir = bundleWorkingDir.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir);
+
+ final Path warFile = depsDir.resolve("my-app.war");
+ createConnectorWar(warFile);
+
+ final Bundle bundle = createBundle(bundleWorkingDir);
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle));
+
+ assertEquals(1, wars.size());
+ assertTrue(wars.containsKey(warFile.toFile()));
+ assertEquals(bundle, wars.get(warFile.toFile()));
+ }
+
+ @Test
+ void testFindWarsIgnoresNonWarFiles() throws Exception {
+ final Path bundleWorkingDir = tempDir.resolve("test-bundle");
+ final Path depsDir = bundleWorkingDir.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir);
+
+ Files.createFile(depsDir.resolve("some-lib.jar"));
+ Files.createFile(depsDir.resolve("config.xml"));
+
+ final Bundle bundle = createBundle(bundleWorkingDir);
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle));
+
+ assertTrue(wars.isEmpty());
+ }
+
+ @Test
+ void testFindWarsIgnoresWarWithoutConnectorManifest() throws Exception {
+ final Path bundleWorkingDir = tempDir.resolve("test-bundle");
+ final Path depsDir = bundleWorkingDir.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir);
+
+ final Path warFile = depsDir.resolve("non-connector.war");
+ createWarWithoutConnectorManifest(warFile);
+
+ final Bundle bundle = createBundle(bundleWorkingDir);
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle));
+
+ assertTrue(wars.isEmpty());
+ }
+
+ @Test
+ void testFindWarsHandlesMissingDependenciesDirectory() throws Exception {
+ final Path bundleWorkingDir = tempDir.resolve("empty-bundle");
+ Files.createDirectories(bundleWorkingDir);
+
+ final Bundle bundle = createBundle(bundleWorkingDir);
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle));
+
+ assertTrue(wars.isEmpty());
+ }
+
+ @Test
+ void testFindWarsDiscoversMultipleWarFiles() throws Exception {
+ final Path bundleWorkingDir = tempDir.resolve("multi-war-bundle");
+ final Path depsDir = bundleWorkingDir.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir);
+
+ createConnectorWar(depsDir.resolve("app-one.war"));
+ createConnectorWar(depsDir.resolve("app-two.war"));
+ Files.createFile(depsDir.resolve("some-lib.jar"));
+
+ final Bundle bundle = createBundle(bundleWorkingDir);
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle));
+
+ assertEquals(2, wars.size());
+ }
+
+ @Test
+ void testFindWarsFromMultipleBundles() throws Exception {
+ final Path bundleDir1 = tempDir.resolve("bundle-1");
+ final Path depsDir1 = bundleDir1.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir1);
+ createConnectorWar(depsDir1.resolve("first-app.war"));
+
+ final Path bundleDir2 = tempDir.resolve("bundle-2");
+ final Path depsDir2 = bundleDir2.resolve(NAR_DEPENDENCIES_PATH);
+ Files.createDirectories(depsDir2);
+ createConnectorWar(depsDir2.resolve("second-app.war"));
+
+ final Bundle bundle1 = createBundle(bundleDir1, "test-bundle-1");
+ final Bundle bundle2 = createBundle(bundleDir2, "test-bundle-2");
+
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of(bundle1,
bundle2));
+
+ assertEquals(2, wars.size());
+ }
+
+ @Test
+ void testGetHttpPortReturnsNegativeOneWhenNoServer() {
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ assertEquals(-1, server.getHttpPort());
+ }
+
+ @Test
+ void testFindWarsReturnsEmptyForEmptyBundleSet() throws Exception {
+ final StandardConnectorMockServer server = new
StandardConnectorMockServer();
+ final Map<File, Bundle> wars = server.findWars(Set.of());
+ assertTrue(wars.isEmpty());
+ }
+
+ private static void createConnectorWar(final Path warPath) throws
IOException {
+ try (final JarOutputStream jarOut = new JarOutputStream(new
FileOutputStream(warPath.toFile()))) {
+ jarOut.putNextEntry(new JarEntry("META-INF/nifi-connector"));
+ jarOut.closeEntry();
+ }
+ }
+
+ private static void createWarWithoutConnectorManifest(final Path warPath)
throws IOException {
+ try (final JarOutputStream jarOut = new JarOutputStream(new
FileOutputStream(warPath.toFile()))) {
+ jarOut.putNextEntry(new JarEntry("WEB-INF/web.xml"));
+ jarOut.closeEntry();
+ }
+ }
+
+ private Bundle createBundle(final Path workingDir) {
+ return createBundle(workingDir, "test-bundle");
+ }
+
+ private Bundle createBundle(final Path workingDir, final String
artifactId) {
+ final BundleDetails details = new BundleDetails.Builder()
+ .workingDir(workingDir.toFile())
+ .coordinate(new BundleCoordinate("org.test", artifactId,
"1.0.0"))
+ .build();
+
+ return new Bundle(details, ClassLoader.getSystemClassLoader());
+ }
+
+}
diff --git
a/nifi-connector-mock-bundle/nifi-connector-mock/src/main/java/org/apache/nifi/mock/connector/StandardConnectorTestRunner.java
b/nifi-connector-mock-bundle/nifi-connector-mock/src/main/java/org/apache/nifi/mock/connector/StandardConnectorTestRunner.java
index e04708fdf8..294fadec94 100644
---
a/nifi-connector-mock-bundle/nifi-connector-mock/src/main/java/org/apache/nifi/mock/connector/StandardConnectorTestRunner.java
+++
b/nifi-connector-mock-bundle/nifi-connector-mock/src/main/java/org/apache/nifi/mock/connector/StandardConnectorTestRunner.java
@@ -46,15 +46,18 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
public class StandardConnectorTestRunner implements ConnectorTestRunner,
Closeable {
private final File narLibraryDirectory;
+ private final int httpPort;
private ConnectorMockServer mockServer;
private StandardConnectorTestRunner(final Builder builder) {
this.narLibraryDirectory = builder.narLibraryDirectory;
+ this.httpPort = builder.httpPort;
try {
bootstrapInstance();
@@ -102,9 +105,14 @@ public class StandardConnectorTestRunner implements
ConnectorTestRunner, Closeab
final Set<Bundle> narBundles = narClassLoaders.getBundles();
+ final Properties additionalProperties = new Properties();
+ if (httpPort >= 0) {
+ additionalProperties.setProperty(NiFiProperties.WEB_HTTP_PORT,
String.valueOf(httpPort));
+ }
+
final NiFiProperties properties;
try (final InputStream propertiesIn =
getClass().getClassLoader().getResourceAsStream("nifi.properties")) {
- properties =
NiFiProperties.createBasicNiFiProperties(propertiesIn);
+ properties =
NiFiProperties.createBasicNiFiProperties(propertiesIn, additionalProperties);
}
nifiServer.initialize(properties, systemBundle, narBundles,
extensionMapping);
@@ -208,10 +216,16 @@ public class StandardConnectorTestRunner implements
ConnectorTestRunner, Closeab
return mockServer.validate();
}
+ @Override
+ public int getHttpPort() {
+ return mockServer.getHttpPort();
+ }
+
public static class Builder {
private String connectorClassName;
private File narLibraryDirectory;
+ private int httpPort = -1;
private final Map<String, Class<? extends Processor>> processorMocks =
new HashMap<>();
private final Map<String, Class<? extends ControllerService>>
controllerServiceMocks = new HashMap<>();
@@ -225,6 +239,11 @@ public class StandardConnectorTestRunner implements
ConnectorTestRunner, Closeab
return this;
}
+ public Builder httpPort(final int httpPort) {
+ this.httpPort = httpPort;
+ return this;
+ }
+
public Builder mockProcessor(final String processorType, final Class<?
extends Processor> mockProcessorClass) {
processorMocks.put(processorType, mockProcessorClass);
return this;