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 &lt;SSL 
Context Service&gt; 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;

Reply via email to