This is an automated email from the ASF dual-hosted git repository.
exceptionfactory 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 8064ad8083 NIFI-15156 Add support for discovering Listen Ports (#10476)
8064ad8083 is described below
commit 8064ad80833f9a57088ac99e7808c6d8c3e7111d
Author: Kevin Doran <[email protected]>
AuthorDate: Fri Nov 14 09:14:11 2025 -0500
NIFI-15156 Add support for discovering Listen Ports (#10476)
Signed-off-by: David Handermann <[email protected]>
---
.../protocol/component/api/PropertyDescriptor.java | 16 +-
.../api/PropertyListenPortDefinition.java | 53 +++
.../JacksonFlowSnapshotSerializerTest.java | 7 +
.../nifi/processors/standard/ListenHTTP.java | 54 ++-
.../org/apache/nifi/web/api/dto/ListenPortDTO.java | 136 ++++++
.../nifi/web/api/entity/ListenPortsEntity.java | 44 ++
.../nifi/controller/flow/AbstractFlowManager.java | 21 +
.../flow/mapping/NiFiRegistryFlowMapper.java | 18 +
.../apache/nifi/controller/flow/FlowManager.java | 8 +
.../org/apache/nifi/web/NiFiServiceFacade.java | 12 +
.../apache/nifi/web/StandardNiFiServiceFacade.java | 6 +
.../java/org/apache/nifi/web/api/FlowResource.java | 28 ++
.../nifi/web/controller/ControllerFacade.java | 61 +++
.../extension/manifest/ListenPortDefinition.java | 54 +++
.../apache/nifi/extension/manifest/Property.java | 11 +
.../nifi/extension/manifest/TransportProtocol.java | 22 +
.../jaxb/TestJAXBExtensionManifestParser.java | 37 ++
.../manifests/extension-manifest-listen-port.xml | 458 +++++++++++++++++++++
.../impl/StandardRuntimeManifestBuilder.java | 16 +
19 files changed, 1056 insertions(+), 6 deletions(-)
diff --git
a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDescriptor.java
b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDescriptor.java
index 6947663135..016f5137e2 100644
---
a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDescriptor.java
+++
b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDescriptor.java
@@ -16,13 +16,13 @@
*/
package org.apache.nifi.c2.protocol.component.api;
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
-import io.swagger.v3.oas.annotations.media.Schema;
-import org.apache.nifi.expression.ExpressionLanguageScope;
-
public class PropertyDescriptor implements Serializable {
private static final long serialVersionUID = 1L;
@@ -41,6 +41,7 @@ public class PropertyDescriptor implements Serializable {
private String validator;
private boolean dynamic;
private PropertyResourceDefinition resourceDefinition;
+ private PropertyListenPortDefinition listenPortDefinition;
private List<PropertyDependency> dependencies;
@Schema(description = "The name of the property key")
@@ -166,6 +167,15 @@ public class PropertyDescriptor implements Serializable {
this.resourceDefinition = resourceDefinition;
}
+ @Schema(description = "Indicates that this property defines a listen port")
+ public PropertyListenPortDefinition getListenPortDefinition() {
+ return listenPortDefinition;
+ }
+
+ public void setListenPortDefinition(final PropertyListenPortDefinition
listenPortDefinition) {
+ this.listenPortDefinition = listenPortDefinition;
+ }
+
@Schema(description = "The dependencies that this property has on other
properties")
public List<PropertyDependency> getDependencies() {
return dependencies;
diff --git
a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyListenPortDefinition.java
b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyListenPortDefinition.java
new file mode 100644
index 0000000000..a0af8dbcc6
--- /dev/null
+++
b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyListenPortDefinition.java
@@ -0,0 +1,53 @@
+/*
+ * 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.c2.protocol.component.api;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class PropertyListenPortDefinition implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private TransportProtocol transportProtocol;
+ private List<String> applicationProtocols;
+
+ @Schema(description = "The transport protocol used by this listen port")
+ public TransportProtocol getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ public void setTransportProtocol(final TransportProtocol
transportProtocol) {
+ this.transportProtocol = transportProtocol;
+ }
+
+ @Schema(description = "The application protocols that this listen port
could support (if any)")
+ public List<String> getApplicationProtocols() {
+ return applicationProtocols;
+ }
+
+ public void setApplicationProtocols(final List<String>
applicationProtocols) {
+ this.applicationProtocols = applicationProtocols;
+ }
+
+ public enum TransportProtocol {
+ TCP,
+ UDP
+ }
+
+}
diff --git
a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java
b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java
index a0bd0370e8..dc43d033e3 100644
---
a/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java
+++
b/nifi-extension-bundles/nifi-extension-utils/nifi-git-flow-registry/src/test/java/org/apache/nifi/registry/flow/git/serialize/JacksonFlowSnapshotSerializerTest.java
@@ -19,6 +19,7 @@ package org.apache.nifi.registry.flow.git.serialize;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.nifi.flow.VersionedListenPortDefinition;
import org.apache.nifi.flow.VersionedParameter;
import org.apache.nifi.flow.VersionedParameterContext;
import org.apache.nifi.flow.VersionedProcessGroup;
@@ -64,6 +65,10 @@ public class JacksonFlowSnapshotSerializerTest {
final VersionedResourceDefinition resourceDefinition = new
VersionedResourceDefinition();
resourceDefinition.setResourceTypes(Set.of(VersionedResourceType.TEXT,
VersionedResourceType.URL, VersionedResourceType.FILE));
descriptor.setResourceDefinition(resourceDefinition);
+ final VersionedListenPortDefinition listenPortDefinition = new
VersionedListenPortDefinition();
+
listenPortDefinition.setTransportProtocol(VersionedListenPortDefinition.TransportProtocol.TCP);
+ listenPortDefinition.setApplicationProtocols(List.of("http/1.1",
"h2"));
+ descriptor.setListenPortDefinition(listenPortDefinition);
final VersionedProcessor processor1 = new VersionedProcessor();
processor1.setIdentifier("proc1");
@@ -96,6 +101,8 @@ public class JacksonFlowSnapshotSerializerTest {
assertEquals("proc1", processors.get(0).get("identifier").asText());
assertEquals("[ \"failure\", \"success\" ]",
processors.get(0).get("autoTerminatedRelationships").toPrettyString());
assertEquals("[ \"FILE\", \"TEXT\", \"URL\" ]",
processors.get(0).get("propertyDescriptors").get("prop1").get("resourceDefinition").get("resourceTypes").toPrettyString());
+ assertEquals("TCP",
processors.get(0).get("propertyDescriptors").get("prop1").get("listenPortDefinition").get("transportProtocol").asText());
+ assertEquals("[ \"h2\", \"http/1.1\" ]",
processors.get(0).get("propertyDescriptors").get("prop1").get("listenPortDefinition").get("applicationProtocols").toPrettyString());
assertEquals("proc2", processors.get(1).get("identifier").asText());
assertEquals("proc3", processors.get(2).get("identifier").asText());
diff --git
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ListenHTTP.java
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ListenHTTP.java
index 4be58ad674..fd080a2430 100644
---
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ListenHTTP.java
+++
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ListenHTTP.java
@@ -37,6 +37,11 @@ import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.listen.ListenComponent;
+import org.apache.nifi.components.listen.ListenPort;
+import org.apache.nifi.components.listen.StandardListenPort;
+import org.apache.nifi.components.listen.TransportProtocol;
+import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.flowfile.FlowFile;
import
org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
@@ -140,7 +145,7 @@ import java.util.regex.Pattern;
""")
}
)
-public class ListenHTTP extends AbstractSessionFactoryProcessor {
+public class ListenHTTP extends AbstractSessionFactoryProcessor implements
ListenComponent {
private static final String MATCH_ALL = ".*";
private final AtomicBoolean initialized = new AtomicBoolean(false);
@@ -183,6 +188,7 @@ public class ListenHTTP extends
AbstractSessionFactoryProcessor {
.name("Listening Port")
.description("The Port to listen on for incoming connections")
.required(true)
+ .identifiesListenPort(TransportProtocol.TCP, "http/1.1", "h2")
.expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
.addValidator(StandardValidators.PORT_VALIDATOR)
.build();
@@ -197,6 +203,7 @@ public class ListenHTTP extends
AbstractSessionFactoryProcessor {
"If the processor is set to use one-way SSL, one-way SSL
will be used on this port. " +
"If the processor is set to use two-way SSL, one-way SSL
will be used on this port (client authentication not required).")
.required(false)
+ .identifiesListenPort(TransportProtocol.TCP, "http/1.1", "h2")
.expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
.addValidator(StandardValidators.PORT_VALIDATOR)
.build();
@@ -422,6 +429,47 @@ public class ListenHTTP extends
AbstractSessionFactoryProcessor {
shutdownHttpServer(toShutdown);
}
+ @Override
+ public List<ListenPort> getListenPorts(final ConfigurationContext context)
{
+
+ final List<ListenPort> ports = new ArrayList<>();
+
+ final Integer primaryPortNumber =
context.getProperty(PORT).evaluateAttributeExpressions().asInteger();
+ final Integer healthCheckPortNumber =
context.getProperty(HEALTH_CHECK_PORT).evaluateAttributeExpressions().asInteger();
+ final SSLContextProvider sslContextProvider =
context.getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextProvider.class);
+ final HttpProtocolStrategy httpProtocolStrategy = sslContextProvider
== null
+ ?
HttpProtocolStrategy.valueOf(HTTP_PROTOCOL_STRATEGY.getDefaultValue())
+ :
context.getProperty(HTTP_PROTOCOL_STRATEGY).asAllowableValue(HttpProtocolStrategy.class);
+ final List<String> applicationProtocols = switch
(httpProtocolStrategy) {
+ case H2 -> List.of("h2");
+ case HTTP_1_1 -> List.of("http/1.1");
+ case H2_HTTP_1_1 -> List.of("h2", "http/1.1");
+ case null -> List.of("h2", "http/1.1");
+ };
+
+ if (primaryPortNumber != null) {
+ final ListenPort primaryPort = StandardListenPort.builder()
+ .portNumber(primaryPortNumber)
+ .portName(PORT.getDisplayName())
+ .transportProtocol(TransportProtocol.TCP)
+ .applicationProtocols(applicationProtocols)
+ .build();
+ ports.add(primaryPort);
+ }
+
+ if (healthCheckPortNumber != null) {
+ final ListenPort healthCheckPort = StandardListenPort.builder()
+ .portNumber(healthCheckPortNumber)
+ .portName(HEALTH_CHECK_PORT.getDisplayName())
+ .transportProtocol(TransportProtocol.TCP)
+ .applicationProtocols(applicationProtocols)
+ .build();
+ ports.add(healthCheckPort);
+ }
+
+ return ports;
+ }
+
Server getServer() {
return this.server;
}
@@ -462,8 +510,8 @@ public class ListenHTTP extends
AbstractSessionFactoryProcessor {
// get the configured port
final int port =
context.getProperty(PORT).evaluateAttributeExpressions().asInteger();
final HttpProtocolStrategy httpProtocolStrategy = sslContextProvider
== null
- ?
HttpProtocolStrategy.valueOf(HTTP_PROTOCOL_STRATEGY.getDefaultValue())
- :
context.getProperty(HTTP_PROTOCOL_STRATEGY).asAllowableValue(HttpProtocolStrategy.class);
+ ?
HttpProtocolStrategy.valueOf(HTTP_PROTOCOL_STRATEGY.getDefaultValue())
+ :
context.getProperty(HTTP_PROTOCOL_STRATEGY).asAllowableValue(HttpProtocolStrategy.class);
final ServerConnector connector = createServerConnector(server,
port,
requestHeaderSize,
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ListenPortDTO.java
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ListenPortDTO.java
new file mode 100644
index 0000000000..be711369e3
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ListenPortDTO.java
@@ -0,0 +1,136 @@
+/*
+ * 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.web.api.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.xml.bind.annotation.XmlType;
+
+import java.util.List;
+
+@XmlType(name = "listenPort")
+public class ListenPortDTO {
+
+ // Port definition
+ private String portName;
+ private int portNumber;
+ private String transportProtocol;
+ private List<String> applicationProtocols;
+
+ // Contextual information about the component providing the port, and the
PG containing the component
+ private String componentType;
+ private String componentId;
+ private String componentName;
+ private String componentClass;
+ private String parentGroupId;
+ private String parentGroupName;
+
+ @Schema(description = "The name of the the listen port. Useful context for
components that provide multiple ports.")
+ public String getPortName() {
+ return portName;
+ }
+
+ public void setPortName(final String portName) {
+ this.portName = portName;
+ }
+
+ @Schema(description = "The ingress port number")
+ public int getPortNumber() {
+ return portNumber;
+ }
+
+ public void setPortNumber(final int portNumber) {
+ this.portNumber = portNumber;
+ }
+
+ @Schema(description = "The ingress transport protocol (TCP or UDP)")
+ public String getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ public void setTransportProtocol(final String transportProtocol) {
+ this.transportProtocol = transportProtocol;
+ }
+
+ @Schema(description = "Supported application protocols, if applicable")
+ public List<String> getApplicationProtocols() {
+ return applicationProtocols;
+ }
+
+ public void setApplicationProtocols(final List<String>
applicationProtocols) {
+ this.applicationProtocols = applicationProtocols;
+ }
+
+ @Schema(description = "The type of component providing the listen port
(e.g., Processor, ControllerService)")
+ public String getComponentType() {
+ return componentType;
+ }
+
+ public void setComponentType(final String componentType) {
+ this.componentType = componentType;
+ }
+
+ @Schema(description = "The id of the component providing the listen port")
+ public String getComponentId() {
+ return componentId;
+ }
+
+ public void setComponentId(final String componentId) {
+ this.componentId = componentId;
+ }
+
+ @Schema(description = "The name of the component providing the listen
port")
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public void setComponentName(final String componentName) {
+ this.componentName = componentName;
+ }
+
+ @Schema(description = "The class type of the component providing the
listen port")
+ public String getComponentClass() {
+ return componentClass;
+ }
+
+ public void setComponentClass(final String componentClass) {
+ this.componentClass = componentClass;
+ }
+
+ @Schema(description = "The id of the process group containing the
component providing the listen port, if applicable")
+ public String getParentGroupId() {
+ return parentGroupId;
+ }
+
+ public void setParentGroupId(final String parentGroupId) {
+ this.parentGroupId = parentGroupId;
+ }
+
+ @Schema(description = "The name of the process group containing the
component providing the listen port, if applicable")
+ public String getParentGroupName() {
+ return parentGroupName;
+ }
+
+ public void setParentGroupName(final String parentGroupName) {
+ this.parentGroupName = parentGroupName;
+ }
+
+ @Override
+ public String toString() {
+ return ("ListenPortDTO[portName= %s, portNumber=%s,
transportProtocol=%s, applicationProtocols=%s, " +
+ "componentType=%s, componentId=%s, componentName=%s,
componentClass=%s, parentGroupId=%s, parentGroupName=%s]").formatted(
+ portName, portNumber, transportProtocol, applicationProtocols,
componentType, componentId, componentName, componentClass, parentGroupId,
parentGroupName);
+ }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ListenPortsEntity.java
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ListenPortsEntity.java
new file mode 100644
index 0000000000..63da467b3c
--- /dev/null
+++
b/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ListenPortsEntity.java
@@ -0,0 +1,44 @@
+/*
+ * 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.web.api.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import org.apache.nifi.web.api.dto.ListenPortDTO;
+
+import java.util.List;
+
+@XmlRootElement(name = "listenPortsEntity")
+public class ListenPortsEntity {
+
+ private List<ListenPortDTO> listenPorts;
+
+ public ListenPortsEntity() {
+ }
+
+ public ListenPortsEntity(final List<ListenPortDTO> listenPorts) {
+ this.listenPorts = listenPorts;
+ }
+
+ @Schema(description = "A list of ingress ports that are currently
configured")
+ public List<ListenPortDTO> getListenPorts() {
+ return listenPorts;
+ }
+
+ public void setListenPorts(final List<ListenPortDTO> listenPorts) {
+ this.listenPorts = listenPorts;
+ }
+}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
index 6d36f7917e..50308465d2 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/flow/AbstractFlowManager.java
@@ -21,10 +21,12 @@ import org.apache.nifi.annotation.lifecycle.OnRemoved;
import org.apache.nifi.authorization.resource.Authorizable;
import org.apache.nifi.bundle.BundleCoordinate;
import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.listen.ListenComponent;
import org.apache.nifi.connectable.Connectable;
import org.apache.nifi.connectable.Connection;
import org.apache.nifi.connectable.Funnel;
import org.apache.nifi.connectable.Port;
+import org.apache.nifi.controller.ComponentNode;
import org.apache.nifi.controller.FlowAnalysisRuleNode;
import org.apache.nifi.controller.ParameterProviderNode;
import org.apache.nifi.controller.ProcessScheduler;
@@ -57,6 +59,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -732,4 +735,22 @@ public abstract class AbstractFlowManager implements
FlowManager {
ruleViolationsManager.removeRuleViolationsForSubject(identifier);
}
}
+
+ @Override
+ public Set<ComponentNode> getAllListenComponents() {
+
+ final Set<ComponentNode> allListenComponents = new LinkedHashSet<>();
+
+ // Search Processors
+ allProcessors.values().stream()
+ .filter(processorNode -> processorNode.getComponent() instanceof
ListenComponent)
+ .forEach(allListenComponents::add);
+
+ // Search Controller Services
+ getAllControllerServices().stream()
+ .filter(csNode -> csNode.getComponent() instanceof ListenComponent)
+ .forEach(allListenComponents::add);
+
+ return allListenComponents;
+ }
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
index dba641a9e8..d4818e6bbc 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
@@ -21,6 +21,7 @@ import org.apache.commons.lang3.ClassUtils;
import org.apache.nifi.asset.Asset;
import org.apache.nifi.bundle.BundleCoordinate;
import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.listen.ListenPortDefinition;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceDefinition;
import org.apache.nifi.connectable.Connectable;
@@ -59,6 +60,7 @@ import org.apache.nifi.flow.VersionedFlowCoordinates;
import org.apache.nifi.flow.VersionedFlowRegistryClient;
import org.apache.nifi.flow.VersionedFunnel;
import org.apache.nifi.flow.VersionedLabel;
+import org.apache.nifi.flow.VersionedListenPortDefinition;
import org.apache.nifi.flow.VersionedParameter;
import org.apache.nifi.flow.VersionedParameterContext;
import org.apache.nifi.flow.VersionedParameterProvider;
@@ -590,6 +592,9 @@ public class NiFiRegistryFlowMapper {
final VersionedResourceDefinition versionedResourceDefinition =
mapResourceDefinition(descriptor.getResourceDefinition());
versionedDescriptor.setResourceDefinition(versionedResourceDefinition);
+ final VersionedListenPortDefinition versionedListenPortDefinition
= mapListenPortDefinition(descriptor.getListenPortDefinition());
+
versionedDescriptor.setListenPortDefinition(versionedListenPortDefinition);
+
final Class<?> referencedServiceType =
descriptor.getControllerServiceDefinition();
versionedDescriptor.setIdentifiesControllerService(referencedServiceType !=
null);
@@ -637,6 +642,19 @@ public class NiFiRegistryFlowMapper {
return versionedResourceDefinition;
}
+ private VersionedListenPortDefinition mapListenPortDefinition(final
ListenPortDefinition listenPortDefinition) {
+ if (listenPortDefinition == null) {
+ return null;
+ }
+
+ final VersionedListenPortDefinition.TransportProtocol
transportProtocol =
VersionedListenPortDefinition.TransportProtocol.valueOf(listenPortDefinition.getTransportProtocol().name());
+
+ final VersionedListenPortDefinition versionedListenPortDefinition =
new VersionedListenPortDefinition();
+ versionedListenPortDefinition.setTransportProtocol(transportProtocol);
+
versionedListenPortDefinition.setApplicationProtocols(listenPortDefinition.getApplicationProtocols());
+ return versionedListenPortDefinition;
+ }
+
private Bundle mapBundle(final BundleCoordinate coordinate) {
final Bundle versionedBundle = new Bundle();
versionedBundle.setGroup(coordinate.getGroup());
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
index c0543ef719..c8927bb871 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java
@@ -21,6 +21,7 @@ import org.apache.nifi.connectable.Connectable;
import org.apache.nifi.connectable.Connection;
import org.apache.nifi.connectable.Funnel;
import org.apache.nifi.connectable.Port;
+import org.apache.nifi.controller.ComponentNode;
import org.apache.nifi.controller.FlowAnalysisRuleNode;
import org.apache.nifi.controller.ParameterProviderNode;
import org.apache.nifi.controller.ProcessorNode;
@@ -439,4 +440,11 @@ public interface FlowManager extends
ParameterProviderLookup {
Optional<FlowAnalyzer> getFlowAnalyzer();
Optional<RuleViolationsManager> getRuleViolationsManager();
+
+ /**
+ * Returns all components (processors, controller services, etc.) that are
a {@link org.apache.nifi.components.listen.ListenComponent}
+ *
+ * @return A set of listen components.
+ */
+ Set<ComponentNode> getAllListenComponents();
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
index 0cddedbfec..e42712ca56 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java
@@ -70,6 +70,7 @@ import org.apache.nifi.web.api.dto.FlowFileDTO;
import org.apache.nifi.web.api.dto.FlowRegistryClientDTO;
import org.apache.nifi.web.api.dto.FunnelDTO;
import org.apache.nifi.web.api.dto.LabelDTO;
+import org.apache.nifi.web.api.dto.ListenPortDTO;
import org.apache.nifi.web.api.dto.ListingRequestDTO;
import org.apache.nifi.web.api.dto.NodeDTO;
import org.apache.nifi.web.api.dto.ParameterContextDTO;
@@ -3126,4 +3127,15 @@ public interface NiFiServiceFacade {
*/
Set<String> filterComponents(String groupId, Function<ProcessGroup,
Set<String>> getComponents);
+ // ----------------------------------------
+ // Listen Port methods
+ // ----------------------------------------
+
+ /**
+ * Get all dynamically defined data ingress ports provided by Listen
Components (e.g., Processors and Controller Services)
+ *
+ * @param user the user performing the lookup
+ * @return the list of listen Ports accessible to the current user
+ */
+ Set<ListenPortDTO> getListenPorts(NiFiUser user);
}
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index 97b43df0f8..33a0d9e971 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -262,6 +262,7 @@ import org.apache.nifi.web.api.dto.FlowRegistryClientDTO;
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
import org.apache.nifi.web.api.dto.FunnelDTO;
import org.apache.nifi.web.api.dto.LabelDTO;
+import org.apache.nifi.web.api.dto.ListenPortDTO;
import org.apache.nifi.web.api.dto.ListingRequestDTO;
import org.apache.nifi.web.api.dto.NarCoordinateDTO;
import org.apache.nifi.web.api.dto.NarSummaryDTO;
@@ -7249,6 +7250,11 @@ public class StandardNiFiServiceFacade implements
NiFiServiceFacade {
return new NiFiRegistryFlowMapper(extensionManager, options);
}
+ @Override
+ public Set<ListenPortDTO> getListenPorts(final NiFiUser user) {
+ return controllerFacade.getListenPorts(user);
+ }
+
@Autowired
public void setProperties(final NiFiProperties properties) {
this.properties = properties;
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
index 71994f9289..117eacb92d 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java
@@ -88,6 +88,7 @@ import org.apache.nifi.web.api.dto.ClusterSummaryDTO;
import org.apache.nifi.web.api.dto.ComponentDifferenceDTO;
import org.apache.nifi.web.api.dto.ContentViewerDTO;
import org.apache.nifi.web.api.dto.DifferenceDTO;
+import org.apache.nifi.web.api.dto.ListenPortDTO;
import org.apache.nifi.web.api.dto.NodeDTO;
import org.apache.nifi.web.api.dto.ProcessGroupDTO;
import org.apache.nifi.web.api.dto.RevisionDTO;
@@ -131,6 +132,7 @@ import
org.apache.nifi.web.api.entity.FlowRegistryBucketsEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientsEntity;
import org.apache.nifi.web.api.entity.HistoryEntity;
+import org.apache.nifi.web.api.entity.ListenPortsEntity;
import org.apache.nifi.web.api.entity.ParameterContextEntity;
import org.apache.nifi.web.api.entity.ParameterContextsEntity;
import org.apache.nifi.web.api.entity.ParameterProviderEntity;
@@ -1394,6 +1396,32 @@ public class FlowResource extends ApplicationResource {
return noCache(Response.ok(entity)).build();
}
+ @GET
+ @Path("listen-ports")
+ @Consumes(MediaType.WILDCARD)
+ @Produces(MediaType.APPLICATION_JSON)
+ @Operation(
+ summary = "Gets all listen ports configured on this NiFi that the
current user has access to",
+ responses = {
+ @ApiResponse(responseCode = "200", content = @Content(schema =
@Schema(implementation = ListenPortsEntity.class))),
+ @ApiResponse(responseCode = "400", description = "NiFi was unable
to complete the request because it was invalid. The request should not be
retried without modification."),
+ @ApiResponse(responseCode = "401", description = "Client could not
be authenticated."),
+ @ApiResponse(responseCode = "403", description = "Client is not
authorized to make this request."),
+ @ApiResponse(responseCode = "409", description = "The request was
valid but NiFi was not in the appropriate state to process it.")
+ },
+ security = {
+ @SecurityRequirement(name = "Read - /flow")
+ }
+ )
+ public Response getListenPorts() {
+ authorizeFlow();
+
+ final Set<ListenPortDTO> listenPorts =
serviceFacade.getListenPorts(NiFiUserUtils.getNiFiUser());
+ final ListenPortsEntity listenPortsEntity = new ListenPortsEntity(new
ArrayList<>(listenPorts));
+
+ return generateOkResponse(listenPortsEntity).build();
+ }
+
/**
* Retrieves the status for this NiFi.
*
diff --git
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
index ae1d9e2a88..c2fbae3f27 100644
---
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
+++
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/controller/ControllerFacade.java
@@ -43,9 +43,12 @@ import
org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.cluster.protocol.NodeIdentifier;
import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.components.RequiredPermission;
+import org.apache.nifi.components.listen.ListenComponent;
import org.apache.nifi.connectable.Connectable;
import org.apache.nifi.connectable.Connection;
import org.apache.nifi.connectable.Port;
+import org.apache.nifi.controller.ComponentNode;
+import org.apache.nifi.controller.ConfigurationContext;
import org.apache.nifi.controller.ContentAvailability;
import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.controller.Counter;
@@ -64,6 +67,7 @@ import
org.apache.nifi.controller.serialization.VersionedReportingTaskImporter;
import org.apache.nifi.controller.service.ControllerServiceNode;
import org.apache.nifi.controller.service.ControllerServiceProvider;
import org.apache.nifi.controller.service.ControllerServiceResolver;
+import org.apache.nifi.controller.service.StandardConfigurationContext;
import org.apache.nifi.controller.status.ConnectionStatus;
import org.apache.nifi.controller.status.PortStatus;
import org.apache.nifi.controller.status.ProcessGroupStatus;
@@ -113,6 +117,7 @@ import org.apache.nifi.web.ResourceNotFoundException;
import org.apache.nifi.web.api.dto.BundleDTO;
import org.apache.nifi.web.api.dto.DocumentedTypeDTO;
import org.apache.nifi.web.api.dto.DtoFactory;
+import org.apache.nifi.web.api.dto.ListenPortDTO;
import org.apache.nifi.web.api.dto.diagnostics.ProcessorDiagnosticsDTO;
import org.apache.nifi.web.api.dto.provenance.AttributeDTO;
import org.apache.nifi.web.api.dto.provenance.LatestProvenanceEventsDTO;
@@ -143,6 +148,7 @@ import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
@@ -1853,6 +1859,61 @@ public class ControllerFacade implements Authorizable {
return results;
}
+ /**
+ * Get all user-defined data ingress ports provided by Listen Components
(e.g., Processors and Controller Services)
+ *
+ * @param user the user performing the lookup
+ * @return the set of listen Ports accessible to the current user
+ */
+ public Set<ListenPortDTO> getListenPorts(final NiFiUser user) {
+
+ // Get all listen components for which the requesting user is
authorized
+ final Set<ComponentNode> listenComponentNodes =
flowController.getFlowManager().getAllListenComponents().stream()
+ .filter(componentNode -> componentNode.isAuthorized(authorizer,
RequestAction.READ, user))
+ .collect(Collectors.toSet());
+
+ // If the current user doesn't have access to any listen components,
return an empty result for ports
+ if (listenComponentNodes.isEmpty()) {
+ return Collections.emptySet();
+ }
+
+ // Now find all Listen Ports provided by the Listen Components. A
listen component can provide multiple Listen Ports (e.g., ListenHTTP can have a
data port and a health check port).
+ // The current Listen Ports for a component depend on configuration
(e.g., port property value), so create a configuration context to provide
ListenComponent.getListenPorts(context).
+ final Set<ListenPortDTO> listenPorts = new HashSet<>();
+ final ControllerServiceProvider controllerServiceProvider =
flowController.getControllerServiceProvider();
+ for (final ComponentNode componentNode : listenComponentNodes) {
+ final ConfigurationContext configurationContext = new
StandardConfigurationContext(componentNode, controllerServiceProvider, null);
+ final ConfigurableComponent component =
componentNode.getComponent();
+ // All components are expected to be ListenComponents, so this
check is just for safe casting
+ if (component instanceof ListenComponent listenComponent) {
+
listenComponent.getListenPorts(configurationContext).forEach(listenPort -> {
+ final ListenPortDTO listenPortDTO = new ListenPortDTO();
+ listenPortDTO.setPortName(listenPort.getPortName());
+ listenPortDTO.setPortNumber(listenPort.getPortNumber());
+
listenPortDTO.setTransportProtocol(listenPort.getTransportProtocol().name());
+
listenPortDTO.setApplicationProtocols(listenPort.getApplicationProtocols());
+
listenPortDTO.setComponentClass(componentNode.getCanonicalClassName());
+
listenPortDTO.setComponentId(componentNode.getIdentifier());
+ listenPortDTO.setComponentName(componentNode.getName());
+
listenPortDTO.setParentGroupId(componentNode.getParentProcessGroup().map(ProcessGroup::getIdentifier).orElse(null));
+
listenPortDTO.setParentGroupName(componentNode.getParentProcessGroup().map(ProcessGroup::getName).orElse(null));
+
+ if (componentNode instanceof ProcessorNode) {
+ listenPortDTO.setComponentType("Processor");
+ } else if (componentNode instanceof ControllerServiceNode)
{
+ listenPortDTO.setComponentType("ControllerService");
+ } else {
+ logger.warn("Unexpected listen component type {}",
componentNode.getClass().getCanonicalName());
+ listenPortDTO.setComponentType(null);
+ }
+ listenPorts.add(listenPortDTO);
+ });
+ }
+ }
+
+ return listenPorts;
+ }
+
public void verifyComponentTypes(VersionedProcessGroup versionedFlow) {
flowController.verifyComponentTypesInSnippet(versionedFlow);
}
diff --git
a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ListenPortDefinition.java
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ListenPortDefinition.java
new file mode 100644
index 0000000000..8eba124651
--- /dev/null
+++
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ListenPortDefinition.java
@@ -0,0 +1,54 @@
+/*
+ * 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.extension.manifest;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlElementWrapper;
+
+import java.util.List;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ListenPortDefinition {
+
+ private TransportProtocol transportProtocol;
+
+ @XmlElementWrapper
+ @XmlElement(name = "applicationProtocol")
+ private List<String> applicationProtocols;
+
+ @Schema(description = "The transport protocol used by this listen port")
+ public TransportProtocol getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ public void setTransportProtocol(final TransportProtocol
transportProtocol) {
+ this.transportProtocol = transportProtocol;
+ }
+
+ @Schema(description = "The application protocols that this listen port
could support (if any)")
+ public List<String> getApplicationProtocols() {
+ return applicationProtocols;
+ }
+
+ public void setApplicationProtocols(final List<String>
applicationProtocols) {
+ this.applicationProtocols = applicationProtocols;
+ }
+
+}
diff --git
a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Property.java
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Property.java
index acd5739c96..66212e3938 100644
---
a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Property.java
+++
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Property.java
@@ -52,6 +52,8 @@ public class Property {
private ResourceDefinition resourceDefinition;
+ private ListenPortDefinition listenPortDefinition;
+
@Schema(description = "The name of the property")
public String getName() {
return name;
@@ -177,4 +179,13 @@ public class Property {
public void setResourceDefinition(ResourceDefinition resourceDefinition) {
this.resourceDefinition = resourceDefinition;
}
+
+ @Schema(description = "The optional listen port definition")
+ public ListenPortDefinition getListenPortDefinition() {
+ return listenPortDefinition;
+ }
+
+ public void setListenPortDefinition(final ListenPortDefinition
listenPortDefinition) {
+ this.listenPortDefinition = listenPortDefinition;
+ }
}
diff --git
a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/TransportProtocol.java
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/TransportProtocol.java
new file mode 100644
index 0000000000..b8109d36be
--- /dev/null
+++
b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/TransportProtocol.java
@@ -0,0 +1,22 @@
+/*
+ * 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.extension.manifest;
+
+public enum TransportProtocol {
+ TCP,
+ UDP
+}
diff --git
a/nifi-manifest/nifi-extension-manifest-parser/src/test/java/org/apache/nifi/extension/manifest/parser/jaxb/TestJAXBExtensionManifestParser.java
b/nifi-manifest/nifi-extension-manifest-parser/src/test/java/org/apache/nifi/extension/manifest/parser/jaxb/TestJAXBExtensionManifestParser.java
index 48c080d51e..721fad6706 100644
---
a/nifi-manifest/nifi-extension-manifest-parser/src/test/java/org/apache/nifi/extension/manifest/parser/jaxb/TestJAXBExtensionManifestParser.java
+++
b/nifi-manifest/nifi-extension-manifest-parser/src/test/java/org/apache/nifi/extension/manifest/parser/jaxb/TestJAXBExtensionManifestParser.java
@@ -24,11 +24,13 @@ import org.apache.nifi.extension.manifest.DependentValues;
import org.apache.nifi.extension.manifest.Extension;
import org.apache.nifi.extension.manifest.ExtensionManifest;
import org.apache.nifi.extension.manifest.ExtensionType;
+import org.apache.nifi.extension.manifest.ListenPortDefinition;
import org.apache.nifi.extension.manifest.Property;
import org.apache.nifi.extension.manifest.ProvidedServiceAPI;
import org.apache.nifi.extension.manifest.ResourceDefinition;
import org.apache.nifi.extension.manifest.ResourceType;
import org.apache.nifi.extension.manifest.Restriction;
+import org.apache.nifi.extension.manifest.TransportProtocol;
import org.apache.nifi.extension.manifest.parser.ExtensionManifestParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -289,6 +291,41 @@ public class TestJAXBExtensionManifestParser {
assertEquals(ResourceType.FILE, resourceTypes.get(0));
}
+ @Test
+ public void testDocsWithListenPortDefinitions() throws IOException {
+ final ExtensionManifest extensionManifest =
parse("src/test/resources/manifests/extension-manifest-listen-port.xml");
+ assertNotNull(extensionManifest);
+ assertEquals("2.5.0-SNAPSHOT",
extensionManifest.getSystemApiVersion());
+
+ final List<Extension> extensionDetails =
extensionManifest.getExtensions();
+
+ final Extension jettyWebSocketServer = extensionDetails.stream()
+ .filter(e ->
e.getName().equals("org.apache.nifi.websocket.jetty.JettyWebSocketServer"))
+ .findFirst()
+ .orElse(null);
+
+ assertNotNull(jettyWebSocketServer);
+ assertEquals(ExtensionType.CONTROLLER_SERVICE,
jettyWebSocketServer.getType());
+
+ final List<Property> properties = jettyWebSocketServer.getProperties();
+ assertNotNull(properties);
+
+ final Property portProperty = properties.stream()
+ .filter(p -> p.getName().equals("Port"))
+ .findFirst()
+ .orElse(null);
+ assertNotNull(portProperty);
+
+ final ListenPortDefinition listenPortDefinition =
portProperty.getListenPortDefinition();
+ assertNotNull(listenPortDefinition);
+ assertEquals(TransportProtocol.TCP,
listenPortDefinition.getTransportProtocol());
+
+ final List<String> applicationProtocols =
listenPortDefinition.getApplicationProtocols();
+ assertNotNull(applicationProtocols);
+ assertEquals(1, applicationProtocols.size());
+ assertEquals("ws", applicationProtocols.getFirst());
+ }
+
@Test
public void testBundleAndBuildInfo() throws IOException {
final ExtensionManifest extensionManifest =
parse("src/test/resources/manifests/extension-manifest-kafka-2-6-nar.xml");
diff --git
a/nifi-manifest/nifi-extension-manifest-parser/src/test/resources/manifests/extension-manifest-listen-port.xml
b/nifi-manifest/nifi-extension-manifest-parser/src/test/resources/manifests/extension-manifest-listen-port.xml
new file mode 100644
index 0000000000..928cc3e621
--- /dev/null
+++
b/nifi-manifest/nifi-extension-manifest-parser/src/test/resources/manifests/extension-manifest-listen-port.xml
@@ -0,0 +1,458 @@
+<!--
+ 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.
+-->
+<extensionManifest>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-jetty-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ <parentNar>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </parentNar>
+ <systemApiVersion>2.5.0-SNAPSHOT</systemApiVersion>
+ <buildInfo>
+ <tag>HEAD</tag>
+ <branch>listen-ports</branch>
+ <revision>5ac640d</revision>
+ </buildInfo>
+ <extensions>
+ <extension>
+ <name>org.apache.nifi.websocket.jetty.JettyWebSocketClient</name>
+ <type>CONTROLLER_SERVICE</type>
+ <description>Implementation of WebSocketClientService. This
service uses Jetty WebSocket client module to provide WebSocket session
management throughout the application.</description>
+ <tags>
+ <tag>WebSocket</tag>
+ <tag>Jetty</tag>
+ <tag>client</tag>
+ </tags>
+ <properties>
+ <property>
+ <name>Input Buffer Size</name>
+ <displayName>Input Buffer Size</displayName>
+ <description>The size of the input (read from network
layer) buffer size.</description>
+ <defaultValue>4 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Max Text Message Size</name>
+ <displayName>Max Text Message Size</displayName>
+ <description>The maximum size of a text message during
parsing/generating.</description>
+ <defaultValue>64 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Max Binary Message Size</name>
+ <displayName>Max Binary Message Size</displayName>
+ <description>The maximum size of a binary message during
parsing/generating.</description>
+ <defaultValue>64 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Idle Timeout</name>
+ <displayName>Idle Timeout</displayName>
+ <description>The maximum amount of time that a WebSocket
connection may remain idle before it is closed. A value of 0 sec disables the
timeout.</description>
+ <defaultValue>0 sec</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>WebSocket URI</name>
+ <displayName>WebSocket URI</displayName>
+ <description>The WebSocket URI this client connects
to.</description>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>FLOWFILE_ATTRIBUTES</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>SSL Context Service</name>
+ <displayName>SSL Context Service</displayName>
+ <description>The SSL Context Service to use in order to
secure the server. If specified, the server will accept only WSS requests;
otherwise, the server will accept only WS requests</description>
+ <controllerServiceDefinition>
+
<className>org.apache.nifi.ssl.SSLContextProvider</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-standard-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </controllerServiceDefinition>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Connection Timeout</name>
+ <displayName>Connection Timeout</displayName>
+ <description>The timeout to connect the WebSocket
URI.</description>
+ <defaultValue>3 sec</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Connection Attempt Count</name>
+ <displayName>Connection Attempt Count</displayName>
+ <description>The number of times to try and establish a
connection.</description>
+ <defaultValue>3</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Session Maintenance Interval</name>
+ <displayName>Session Maintenance Interval</displayName>
+ <description>The interval between session maintenance
activities. A WebSocket session established with a WebSocket server can be
terminated due to different reasons including restarting the WebSocket server
or timing out inactive sessions. This session maintenance activity is
periodically executed in order to reconnect those lost sessions, so that a
WebSocket client can reuse the same session id transparently after it
reconnects successfully. The maintenance activity [...]
+ <defaultValue>10 sec</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Username</name>
+ <displayName>Username</displayName>
+ <description>The user name for Basic
Authentication.</description>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Password</name>
+ <displayName>Password</displayName>
+ <description>The user password for Basic
Authentication.</description>
+ <required>false</required>
+ <sensitive>true</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Authentication Header Charset</name>
+ <displayName>Authentication Header Charset</displayName>
+ <description>The charset for Basic Authentication header
base64 string.</description>
+ <defaultValue>US-ASCII</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Custom Authorization</name>
+ <displayName>Custom Authorization</displayName>
+ <description>Configures a custom HTTP Authorization Header
as described in RFC 7235 Section 4.2. Setting a custom Authorization Header
excludes configuring the User Name and User Password properties for Basic
Authentication.</description>
+ <required>false</required>
+ <sensitive>true</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>HTTP Proxy Host</name>
+ <displayName>HTTP Proxy Host</displayName>
+ <description>The host name of the HTTP Proxy.</description>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>HTTP Proxy Port</name>
+ <displayName>HTTP Proxy Port</displayName>
+ <description>The port number of the HTTP
Proxy.</description>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ </properties>
+ <providedServiceAPIs>
+ <providedServiceAPI>
+
<className>org.apache.nifi.websocket.WebSocketClientService</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </providedServiceAPI>
+ <providedServiceAPI>
+
<className>org.apache.nifi.websocket.WebSocketService</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </providedServiceAPI>
+ </providedServiceAPIs>
+ </extension>
+ <extension>
+ <name>org.apache.nifi.websocket.jetty.JettyWebSocketServer</name>
+ <type>CONTROLLER_SERVICE</type>
+ <description>Implementation of WebSocketServerService. This
service uses Jetty WebSocket server module to provide WebSocket session
management throughout the application.</description>
+ <tags>
+ <tag>WebSocket</tag>
+ <tag>Jetty</tag>
+ <tag>server</tag>
+ </tags>
+ <properties>
+ <property>
+ <name>Input Buffer Size</name>
+ <displayName>Input Buffer Size</displayName>
+ <description>The size of the input (read from network
layer) buffer size.</description>
+ <defaultValue>4 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Max Text Message Size</name>
+ <displayName>Max Text Message Size</displayName>
+ <description>The maximum size of a text message during
parsing/generating.</description>
+ <defaultValue>64 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Max Binary Message Size</name>
+ <displayName>Max Binary Message Size</displayName>
+ <description>The maximum size of a binary message during
parsing/generating.</description>
+ <defaultValue>64 kb</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Idle Timeout</name>
+ <displayName>Idle Timeout</displayName>
+ <description>The maximum amount of time that a WebSocket
connection may remain idle before it is closed. A value of 0 sec disables the
timeout.</description>
+ <defaultValue>0 sec</defaultValue>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Port</name>
+ <displayName>Port</displayName>
+ <description>The port number on which this WebSocketServer
listens to.</description>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ <listenPortDefinition>
+ <transportProtocol>TCP</transportProtocol>
+ <applicationProtocols>
+ <applicationProtocol>ws</applicationProtocol>
+ </applicationProtocols>
+ </listenPortDefinition>
+ </property>
+ <property>
+ <name>SSL Context Service</name>
+ <displayName>SSL Context Service</displayName>
+ <description>The SSL Context Service to use in order to
secure the server. If specified, the server will accept only WSS requests;
otherwise, the server will accept only WS requests</description>
+ <controllerServiceDefinition>
+
<className>org.apache.nifi.ssl.SSLContextProvider</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-standard-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </controllerServiceDefinition>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Client Authentication</name>
+ <displayName>Client Authentication</displayName>
+ <description>Specifies whether or not the Processor should
authenticate client by its certificate. This value is ignored if the <SSL
Context Service> Property is not specified or the SSL Context provided uses
only a KeyStore and not a TrustStore.</description>
+ <defaultValue>no</defaultValue>
+ <allowableValues>
+ <allowableValue>
+ <displayName>No Authentication</displayName>
+ <value>no</value>
+ <description>Processor will not authenticate
clients. Anyone can communicate with this Processor anonymously</description>
+ </allowableValue>
+ <allowableValue>
+ <displayName>Want Authentication</displayName>
+ <value>want</value>
+ <description>Processor will try to verify the
client but if unable to verify will allow the client to communicate
anonymously</description>
+ </allowableValue>
+ <allowableValue>
+ <displayName>Need Authentication</displayName>
+ <value>need</value>
+ <description>Processor will reject communications
from any client unless the client provides a certificate that is trusted by the
TrustStore specified in the SSL Context Service</description>
+ </allowableValue>
+ </allowableValues>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Basic Authentication Enabled</name>
+ <displayName>Basic Authentication Enabled</displayName>
+ <description>If enabled, client connection requests are
authenticated with Basic authentication using the specified Login
Provider.</description>
+ <defaultValue>false</defaultValue>
+ <allowableValues>
+ <allowableValue>
+ <displayName>true</displayName>
+ <value>true</value>
+ <description/>
+ </allowableValue>
+ <allowableValue>
+ <displayName>false</displayName>
+ <value>false</value>
+ <description/>
+ </allowableValue>
+ </allowableValues>
+ <required>true</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Basic Authentication Path Spec</name>
+ <displayName>Basic Authentication Path Spec</displayName>
+ <description>Specify a Path Spec to apply Basic
Authentication.</description>
+ <defaultValue>/*</defaultValue>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Basic Authentication Roles</name>
+ <displayName>Basic Authentication Roles</displayName>
+ <description>The authenticated user must have one of
specified role. Multiple roles can be set as comma separated string. '*'
represents any role and so does '**' any role including no role.</description>
+ <defaultValue>**</defaultValue>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>Login Service</name>
+ <displayName>Login Service</displayName>
+ <description>Specify which Login Service to use for Basic
Authentication.</description>
+ <defaultValue>hash</defaultValue>
+ <allowableValues>
+ <allowableValue>
+ <displayName>HashLoginService</displayName>
+ <value>hash</value>
+ <description>See
http://www.eclipse.org/jetty/javadoc/current/org/eclipse/jetty/security/HashLoginService.html
for detail.</description>
+ </allowableValue>
+ </allowableValues>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>false</expressionLanguageSupported>
+ <expressionLanguageScope>NONE</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ </property>
+ <property>
+ <name>users-properties-file</name>
+ <displayName>Users Properties File</displayName>
+ <description>Specify a property file containing users for
Basic Authentication using HashLoginService. See
http://www.eclipse.org/jetty/documentation/current/configuring-security.html
for detail.</description>
+ <required>false</required>
+ <sensitive>false</sensitive>
+
<expressionLanguageSupported>true</expressionLanguageSupported>
+
<expressionLanguageScope>ENVIRONMENT</expressionLanguageScope>
+
<dynamicallyModifiesClasspath>false</dynamicallyModifiesClasspath>
+ <dynamic>false</dynamic>
+ <resourceDefinition>
+ <cardinality>SINGLE</cardinality>
+ <resourceTypes>
+ <resourceType>FILE</resourceType>
+ </resourceTypes>
+ </resourceDefinition>
+ </property>
+ </properties>
+ <providedServiceAPIs>
+ <providedServiceAPI>
+
<className>org.apache.nifi.websocket.WebSocketServerService</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </providedServiceAPI>
+ <providedServiceAPI>
+
<className>org.apache.nifi.websocket.WebSocketService</className>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-websocket-services-api-nar</artifactId>
+ <version>2.7.0-SNAPSHOT</version>
+ </providedServiceAPI>
+ </providedServiceAPIs>
+ </extension>
+ </extensions>
+</extensionManifest>
\ No newline at end of file
diff --git
a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
index 9e5fce9306..a080378e5e 100644
---
a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
+++
b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/StandardRuntimeManifestBuilder.java
@@ -32,6 +32,7 @@ import
org.apache.nifi.c2.protocol.component.api.ProcessorDefinition;
import org.apache.nifi.c2.protocol.component.api.PropertyAllowableValue;
import org.apache.nifi.c2.protocol.component.api.PropertyDependency;
import org.apache.nifi.c2.protocol.component.api.PropertyDescriptor;
+import org.apache.nifi.c2.protocol.component.api.PropertyListenPortDefinition;
import org.apache.nifi.c2.protocol.component.api.PropertyResourceDefinition;
import org.apache.nifi.c2.protocol.component.api.Relationship;
import org.apache.nifi.c2.protocol.component.api.ReportingTaskDefinition;
@@ -55,6 +56,7 @@ import org.apache.nifi.extension.manifest.DynamicRelationship;
import org.apache.nifi.extension.manifest.Extension;
import org.apache.nifi.extension.manifest.ExtensionManifest;
import org.apache.nifi.extension.manifest.ExtensionType;
+import org.apache.nifi.extension.manifest.ListenPortDefinition;
import org.apache.nifi.extension.manifest.Property;
import org.apache.nifi.extension.manifest.ProvidedServiceAPI;
import org.apache.nifi.extension.manifest.ResourceDefinition;
@@ -611,6 +613,7 @@ public class StandardRuntimeManifestBuilder implements
RuntimeManifestBuilder {
descriptor.setAllowableValues(getPropertyAllowableValues(property.getAllowableValues()));
descriptor.setTypeProvidedByValue(getControllerServiceDefinedType(property.getControllerServiceDefinition()));
descriptor.setResourceDefinition(getPropertyResourceDefinition(property.getResourceDefinition()));
+
descriptor.setListenPortDefinition(getPropertyListenPortDefinition(property.getListenPortDefinition()));
descriptor.setDependencies(getPropertyDependencies(property.getDependencies()));
return descriptor;
}
@@ -666,6 +669,19 @@ public class StandardRuntimeManifestBuilder implements
RuntimeManifestBuilder {
};
}
+ private PropertyListenPortDefinition getPropertyListenPortDefinition(final
ListenPortDefinition listenPortDefinition) {
+ if (listenPortDefinition == null ||
listenPortDefinition.getTransportProtocol() == null) {
+ return null;
+ }
+
+ final PropertyListenPortDefinition propertyListenPortDefinition = new
PropertyListenPortDefinition();
+ final PropertyListenPortDefinition.TransportProtocol transportProtocol
=
PropertyListenPortDefinition.TransportProtocol.valueOf(listenPortDefinition.getTransportProtocol().name());
+ propertyListenPortDefinition.setTransportProtocol(transportProtocol);
+
propertyListenPortDefinition.setApplicationProtocols(listenPortDefinition.getApplicationProtocols());
+
+ return propertyListenPortDefinition;
+ }
+
private ExpressionLanguageScope getELScope(final
org.apache.nifi.extension.manifest.ExpressionLanguageScope elScope) {
if (elScope == null) {
return null;