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-api.git
The following commit(s) were added to refs/heads/main by this push:
new 66f45b0 NIFI-15155 Added ListenPort API (#26)
66f45b0 is described below
commit 66f45b0739748a48c65485a4dd0cc6c0e8756191
Author: Kevin Doran <[email protected]>
AuthorDate: Tue Nov 4 12:41:15 2025 -0500
NIFI-15155 Added ListenPort API (#26)
- Add ListenPortDefinition to PropertyDescriptor that will be serialized as
part of flow components
- Add ListenComponent interface that components can implement to provide
bridge to framework
- Update XML Manifest Writer to support PropertyDescriptors with
ListenPortDefinitions
- Update unit tests
Signed-off-by: David Handermann <[email protected]>
---
.../apache/nifi/components/PropertyDescriptor.java | 37 +++++++
.../nifi/components/listen/ListenComponent.java | 42 ++++++++
.../apache/nifi/components/listen/ListenPort.java | 61 +++++++++++
.../components/listen/ListenPortDefinition.java | 74 +++++++++++++
.../nifi/components/listen/StandardListenPort.java | 117 +++++++++++++++++++++
.../listen/StandardListenPortDefinition.java | 82 +++++++++++++++
.../nifi/components/listen/TransportProtocol.java | 25 +++++
.../documentation/xml/XmlDocumentationWriter.java | 15 +++
.../nifi/flow/VersionedListenPortDefinition.java | 52 +++++++++
.../nifi/flow/VersionedPropertyDescriptor.java | 10 ++
.../nifi/processor/util/StandardValidators.java | 3 +
.../nifi/components/TestPropertyDescriptor.java | 59 +++++++++++
.../xml/XmlDocumentationWriterTest.java | 32 ++++++
13 files changed, 609 insertions(+)
diff --git a/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
b/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
index 213584d..91dd576 100644
--- a/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
+++ b/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
@@ -16,6 +16,9 @@
*/
package org.apache.nifi.components;
+import org.apache.nifi.components.listen.ListenPortDefinition;
+import org.apache.nifi.components.listen.StandardListenPortDefinition;
+import org.apache.nifi.components.listen.TransportProtocol;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceDefinition;
import org.apache.nifi.components.resource.ResourceReference;
@@ -117,6 +120,11 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
*/
private final ResourceDefinition resourceDefinition;
+ /**
+ * Metadata about the listen port that this property specifies, if
applicable
+ */
+ private final ListenPortDefinition listenPortDefinition;
+
protected PropertyDescriptor(final Builder builder) {
this.displayName = builder.displayName == null ? builder.name :
builder.displayName;
this.name = builder.name;
@@ -132,6 +140,7 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
this.validators = List.copyOf(builder.validators);
this.dependencies = builder.dependencies == null ?
Collections.emptySet() : Set.copyOf(builder.dependencies);
this.resourceDefinition = builder.resourceDefinition;
+ this.listenPortDefinition = builder.listenPortDefinition;
}
@Override
@@ -217,6 +226,7 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
private boolean dynamicallyModifiesClasspath = false;
private Class<? extends ControllerService> controllerServiceDefinition;
private ResourceDefinition resourceDefinition;
+ private ListenPortDefinition listenPortDefinition;
private List<Validator> validators = new ArrayList<>();
public Builder fromPropertyDescriptor(final PropertyDescriptor
specDescriptor) {
@@ -234,6 +244,7 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
this.validators = new ArrayList<>(specDescriptor.validators);
this.dependencies = new HashSet<>(specDescriptor.dependencies);
this.resourceDefinition = specDescriptor.resourceDefinition;
+ this.listenPortDefinition = specDescriptor.listenPortDefinition;
return this;
}
@@ -577,6 +588,28 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
return this;
}
+ /**
+ * Specifies that this property defines a numbered host port that a
server will bind to and listen for client-initiated connections.
+ * <p>
+ * This enables discoverability of Listen Ports when deploying NiFi as
part of a system, which can simplify the dynamic creation of external network
components that need to facilitate
+ * inbound connections to NiFi, such as gateways, ingress controllers,
load balancers, and reverse proxies.
+ * <p>
+ * See {@link ListenPortDefinition} for guidance on how to specify
protocols.
+ * <p>
+ * Properties that identify Listen Ports should use the PORT_VALIDATOR
from {@link org.apache.nifi.processor.util.StandardValidators} to guarantee the
value is a valid port number.
+ *
+ * @param transportProtocol specifies the layer 4 protocol used at
the host operating system level for the port specified by this Property.
+ * @param applicationProtocols optionally specifies one or more layer
7 protocols supported by the NiFi component listening on the port specified by
this Property.
+ * @return the builder
+ */
+ public Builder identifiesListenPort(final TransportProtocol
transportProtocol, final String... applicationProtocols) {
+ Objects.requireNonNull(transportProtocol);
+ final List<String> appProtocols = applicationProtocols != null ?
Arrays.asList(applicationProtocols) : new ArrayList<>();
+
+ this.listenPortDefinition = new
StandardListenPortDefinition(transportProtocol, appProtocols);
+ return this;
+ }
+
/**
* Establishes a relationship between this Property and the given
property by declaring that this Property is only relevant if the given Property
has a non-null value.
* Furthermore, if one or more explicit Allowable Values are provided,
this Property will not be relevant unless the given Property's value is equal
to one of the given Allowable Values.
@@ -756,6 +789,10 @@ public final class PropertyDescriptor implements
Comparable<PropertyDescriptor>
return resourceDefinition;
}
+ public ListenPortDefinition getListenPortDefinition() {
+ return listenPortDefinition;
+ }
+
@Override
public boolean equals(final Object other) {
if (this == other) {
diff --git
a/src/main/java/org/apache/nifi/components/listen/ListenComponent.java
b/src/main/java/org/apache/nifi/components/listen/ListenComponent.java
new file mode 100644
index 0000000..96dee82
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/listen/ListenComponent.java
@@ -0,0 +1,42 @@
+/*
+ * 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.components.listen;
+
+import org.apache.nifi.controller.ConfigurationContext;
+
+import java.util.List;
+
+/**
+ * An extension component (e.g., Processor or ControllerService) that creates
one or more ingress network ports.
+ * <p>
+ * Implementing this interface allows {@link ListenPort}s provided by this
component to be dynamically discoverable by the framework.
+ * </p>
+ * <p>
+ * Typically, components implementing this interface should have at least
one property described using a {@link
org.apache.nifi.components.PropertyDescriptor}
+ * that identifies a {@link ListenPortDefinition}. The Property Descriptor
identifies a possible Listen Port that could be created.
+ * This interface provides actual the ports configured based on component
property values, along with additional ingress metadata.
+ * </p>
+ */
+public interface ListenComponent {
+
+ /**
+ * A list of listen ports provided by this component based on its current
configuration.
+ *
+ * @param context provides access to convenience methods for obtaining
property values
+ * @return a list of zero or more listen ports that are actively
configured to be provided by this component.
+ */
+ List<ListenPort> getListenPorts(final ConfigurationContext context);
+}
diff --git a/src/main/java/org/apache/nifi/components/listen/ListenPort.java
b/src/main/java/org/apache/nifi/components/listen/ListenPort.java
new file mode 100644
index 0000000..df52889
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/listen/ListenPort.java
@@ -0,0 +1,61 @@
+/*
+ * 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.components.listen;
+
+import java.util.List;
+
+/**
+ * Represents a dynamically discoverable ingress port provided by a {@link
ListenComponent}.
+ */
+public interface ListenPort {
+
+ /**
+ * Get the operating system numbered port that is listening for network
traffic.
+ *
+ * @return the port number
+ */
+ int getPortNumber();
+
+ /**
+ * Get the name of the listen port.
+ *
+ * @return A descriptive name of the listen port. Useful for {@link
ListenComponent}s that provide more than one port.
+ */
+ String getPortName();
+
+ /**
+ * Get the layer 4 transport protocol that is used at the OS networking
level for this port.
+ *
+ * @return the transport protocol
+ */
+ TransportProtocol getTransportProtocol();
+
+ /**
+ * Get the currently configured application protocols that this port
supports.
+ * <p>
+ * Note that this is not always the same as the application protocols
that could be supported. For example, if this port could support http/1.1 or h2
(HTTP 2),
+ * but is currently configured to require h2, then this method should
return [h2], not [http1.1, h2].
+ * </p>
+ * <p>
+ * See {@link ListenPortDefinition#getApplicationProtocols()} for
guidance on application protocol string values.
+ * This method should return a subset of application protocol values
specified by the corresponding PropertyDescriptor {@link ListenPortDefinition}.
+ * </p>
+ *
+ * @return the application protocols supported by this listen port, if
applicable; otherwise an empty list.
+ */
+ List<String> getApplicationProtocols();
+
+}
diff --git
a/src/main/java/org/apache/nifi/components/listen/ListenPortDefinition.java
b/src/main/java/org/apache/nifi/components/listen/ListenPortDefinition.java
new file mode 100644
index 0000000..3d32caa
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/listen/ListenPortDefinition.java
@@ -0,0 +1,74 @@
+/*
+ * 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.components.listen;
+
+import java.util.List;
+
+/**
+ * Defines the number and types of resources that allowed to be referenced by
a component property
+ */
+public interface ListenPortDefinition {
+
+ /**
+ * Specifies the transport protocol that is used for communication with
this listen port.
+ *
+ * @return the {@link TransportProtocol} enum value
+ */
+ TransportProtocol getTransportProtocol();
+
+ /**
+ * Specifies zero, one, or many application protocols that could be
supported on this listen port.
+ * <p>
+ * This is used as a hint for NiFi runtimes and environments to offer
richer behavior (such as configuration or validation) for application protocols
they understand.
+ * If more than one application protocol could be supported, but is
decided at runtime based on configuration, this method should return all
possible application protocols.
+ * Inspecting the component with a Listen Port at runtime can determine
more details about what has been configured.
+ * <p>
+ * General guidance for application protocol string values:
+ * <ol>
+ * <li>
+ * Use IANA names when possible. For example:
+ * <p>
+ * <a
href="https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml">
+ * IANA Service Name and Transport Protocol Port Number Registry
+ * </a>
+ * <p>
+ * <a
href="https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids">
+ * IANA TLS Application-Layer Protocol Negotiation (ALPN)
Protocol IDs
+ * </a>
+ * <p>
+ * <a
href="https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml">
+ * IANA Uniform Resource Identifier (URI) Schemes
+ * </a>
+ * </li>
+ * <li>
+ * Do not include TLS variants of protocols. NiFi Listen Processors
generally support TLS when possible, and the SSLContextProvider configuration
is enough information to infer if the app
+ * protocol is using TLS. For example, there is no need to include
wss for Websocket over TLS or h2c for HTTP/2 over TCP without TLS.
+ * </li>
+ * <li>
+ * For application protocols built on HTTP, such as gPRC, use or
include the foundational HTTP protocol(s) in the application protocol list for
the ListenPortDefinition.
+ * Protocols built on HTTP usually are just specifications for
structuring data payloads within HTTP requests, but the HTTP request semantics
are likely the most important aspect for system
+ * components that will be discovering NiFi Listen Ports, such as
ingress controllers, load balancers, gateways, proxies, etc. Data payload
structure is usually only important to a NiFi
+ * component, not networking components external to NiFi. You may
also include application protocol(s) layered atop HTTP that are relevant to the
Listen Port, if applicable.
+ * For example: ["http/1.1", "h2", "grpc"]
+ * </li>
+ * </ol>
+ *
+ * @return one or more application protocols that could be supported by
the processor,
+ * or an empty list if no application protocols are known to be supported.
+ */
+ List<String> getApplicationProtocols();
+
+}
diff --git
a/src/main/java/org/apache/nifi/components/listen/StandardListenPort.java
b/src/main/java/org/apache/nifi/components/listen/StandardListenPort.java
new file mode 100644
index 0000000..4fcc8e2
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/listen/StandardListenPort.java
@@ -0,0 +1,117 @@
+/*
+ * 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.components.listen;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class StandardListenPort implements ListenPort {
+
+ private final String portName;
+ private final int portNumber;
+ private final TransportProtocol transportProtocol;
+ private final List<String> applicationProtocols;
+
+ private StandardListenPort(final Builder builder) {
+ Objects.requireNonNull(builder.portName, "Port name is required");
+ Objects.requireNonNull(builder.transportProtocol, "Transport protocol
is required");
+ Objects.requireNonNull(builder.applicationProtocols, "Application
protocols is required. Use empty list if there are no application protocols.");
+
+ this.portName = builder.portName;
+ this.portNumber = builder.portNumber;
+ this.transportProtocol = builder.transportProtocol;
+ this.applicationProtocols = builder.applicationProtocols;
+ }
+
+ @Override
+ public int getPortNumber() {
+ return portNumber;
+ }
+
+ @Override
+ public String getPortName() {
+ return portName;
+ }
+
+ @Override
+ public TransportProtocol getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ @Override
+ public List<String> getApplicationProtocols() {
+ return applicationProtocols;
+ }
+
+ @Override
+ public String toString() {
+ return "StandardListenPort[portName=%s, portNumber=%s,
transportProtocol=%s, applicationProtocols=%s]".formatted(portName, portNumber,
transportProtocol, applicationProtocols);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final StandardListenPort that = (StandardListenPort) o;
+ return portNumber == that.portNumber
+ && Objects.equals(portName, that.portName)
+ && transportProtocol == that.transportProtocol
+ && Objects.equals(applicationProtocols, that.applicationProtocols);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(portName, portNumber, transportProtocol,
applicationProtocols);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String portName;
+ private int portNumber;
+ private TransportProtocol transportProtocol;
+ private List<String> applicationProtocols = Collections.emptyList();
+
+ public Builder portNumber(final int portNumber) {
+ this.portNumber = portNumber;
+ return this;
+ }
+
+ public Builder portName(final String portName) {
+ this.portName = portName;
+ return this;
+ }
+
+ public Builder transportProtocol(final TransportProtocol
transportProtocol) {
+ this.transportProtocol = transportProtocol;
+ return this;
+ }
+
+ public Builder applicationProtocols(final List<String>
applicationProtocols) {
+ this.applicationProtocols = applicationProtocols != null ?
applicationProtocols : Collections.emptyList();
+ return this;
+ }
+
+ public StandardListenPort build() {
+ return new StandardListenPort(this);
+ }
+ }
+
+}
diff --git
a/src/main/java/org/apache/nifi/components/listen/StandardListenPortDefinition.java
b/src/main/java/org/apache/nifi/components/listen/StandardListenPortDefinition.java
new file mode 100644
index 0000000..cf9f126
--- /dev/null
+++
b/src/main/java/org/apache/nifi/components/listen/StandardListenPortDefinition.java
@@ -0,0 +1,82 @@
+/*
+ * 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.components.listen;
+
+import org.apache.nifi.components.PropertyDescriptor;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a Listen Port definition for a {@link PropertyDescriptor} that
specifies a listen port
+ */
+public class StandardListenPortDefinition implements ListenPortDefinition {
+
+ private final TransportProtocol transportProtocol;
+ private final List<String> applicationProtocols;
+
+ /**
+ * Create a {@link ListenPortDefinition}.
+ *
+ * @param transportProtocol - the layer 4 transport protocol used by the
listen port
+ * @param applicationProtocols - application protocols supported by the
listen port or empty list
+ */
+ public StandardListenPortDefinition(TransportProtocol transportProtocol,
List<String> applicationProtocols) {
+ Objects.requireNonNull(transportProtocol, "Transport protocol is
required.");
+ Objects.requireNonNull(applicationProtocols, "Application protocols or
empty list is required.");
+
+ this.transportProtocol = transportProtocol;
+ this.applicationProtocols = applicationProtocols;
+ }
+
+ /**
+ * Create a {@link ListenPortDefinition} without any application protocols.
+ *
+ * @param transportProtocol - the layer 4 transport protocol used by the
listen port
+ */
+ public StandardListenPortDefinition(final TransportProtocol
transportProtocol) {
+ this(transportProtocol, Collections.emptyList());
+ }
+
+ @Override
+ public TransportProtocol getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ @Override
+ public List<String> getApplicationProtocols() {
+ return applicationProtocols;
+ }
+
+ @Override
+ public String toString() {
+ return "StandardListenPortDefinition[transportProtocol=%s,
applicationProtocols=%s]".formatted(transportProtocol, applicationProtocols);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final StandardListenPortDefinition that =
(StandardListenPortDefinition) o;
+ return transportProtocol == that.transportProtocol &&
Objects.equals(applicationProtocols, that.applicationProtocols);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(transportProtocol, applicationProtocols);
+ }
+}
diff --git
a/src/main/java/org/apache/nifi/components/listen/TransportProtocol.java
b/src/main/java/org/apache/nifi/components/listen/TransportProtocol.java
new file mode 100644
index 0000000..6d8a89d
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/listen/TransportProtocol.java
@@ -0,0 +1,25 @@
+/*
+ * 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.components.listen;
+
+/**
+ * Allowed values for ListenPortDefinition transportProtocol field.
+ * Identifies the layer 4 protocol used for the port number at the host
operating system level.
+ */
+public enum TransportProtocol {
+ TCP,
+ UDP
+}
diff --git
a/src/main/java/org/apache/nifi/documentation/xml/XmlDocumentationWriter.java
b/src/main/java/org/apache/nifi/documentation/xml/XmlDocumentationWriter.java
index dc3770b..4b6c244 100644
---
a/src/main/java/org/apache/nifi/documentation/xml/XmlDocumentationWriter.java
+++
b/src/main/java/org/apache/nifi/documentation/xml/XmlDocumentationWriter.java
@@ -56,6 +56,7 @@ import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.UseCase;
import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.components.listen.ListenPortDefinition;
import org.apache.nifi.components.PropertyDependency;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.RequiredPermission;
@@ -202,6 +203,7 @@ public class XmlDocumentationWriter extends
AbstractDocumentationWriter {
writeBooleanElement("dynamicallyModifiesClasspath",
property.isDynamicClasspathModifier());
writeBooleanElement("dynamic", property.isDynamic());
writeResourceDefinition(property.getResourceDefinition());
+ writeListenPortDefinition(property.getListenPortDefinition());
writeDependencies(property);
writeEndElement();
@@ -227,6 +229,19 @@ public class XmlDocumentationWriter extends
AbstractDocumentationWriter {
writeEndElement();
}
+ private void writeListenPortDefinition(final ListenPortDefinition
listenPortDefinition) throws IOException {
+ if (listenPortDefinition == null) {
+ return;
+ }
+
+ writeStartElement("listenPortDefinition");
+
+ writeTextElement("transportProtocol",
listenPortDefinition.getTransportProtocol().name());
+ writeTextArray("applicationProtocols", "applicationProtocol",
listenPortDefinition.getApplicationProtocols());
+
+ writeEndElement();
+ }
+
private void writeResourceType(final ResourceType resourceType) throws
IOException {
writeTextElement("resourceType", resourceType.name());
}
diff --git
a/src/main/java/org/apache/nifi/flow/VersionedListenPortDefinition.java
b/src/main/java/org/apache/nifi/flow/VersionedListenPortDefinition.java
new file mode 100644
index 0000000..ba0f395
--- /dev/null
+++ b/src/main/java/org/apache/nifi/flow/VersionedListenPortDefinition.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nifi.flow;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.util.List;
+
+public class VersionedListenPortDefinition {
+
+ private TransportProtocol transportProtocol;
+ private List<String> applicationProtocols;
+
+ @Schema(description = "The transport protocol used by the listen port")
+ public TransportProtocol getTransportProtocol() {
+ return transportProtocol;
+ }
+
+ public void setTransportProtocol(final TransportProtocol
transportProtocol) {
+ this.transportProtocol = transportProtocol;
+ }
+
+ @Schema(description = "The application protocol(s) that the 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/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
b/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
index 1540935..3df1d7e 100644
--- a/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
+++ b/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
@@ -26,6 +26,7 @@ public class VersionedPropertyDescriptor {
private boolean sensitive;
private boolean dynamic;
private VersionedResourceDefinition resourceDefinition;
+ private VersionedListenPortDefinition listenPortDefinition;
@Schema(description = "The name of the property")
public String getName() {
@@ -80,4 +81,13 @@ public class VersionedPropertyDescriptor {
public void setResourceDefinition(final VersionedResourceDefinition
resourceDefinition) {
this.resourceDefinition = resourceDefinition;
}
+
+ @Schema(description = "Returns the Listen Port Definition for the port
this property specifies, if applicable")
+ public VersionedListenPortDefinition getListenPortDefinition() {
+ return listenPortDefinition;
+ }
+
+ public void setListenPortDefinition(final VersionedListenPortDefinition
listenPortDefinition) {
+ this.listenPortDefinition = listenPortDefinition;
+ }
}
diff --git
a/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
b/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
index f24001a..26d88c7 100644
--- a/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
+++ b/src/main/java/org/apache/nifi/processor/util/StandardValidators.java
@@ -150,6 +150,9 @@ public class StandardValidators {
}
};
+ /**
+ * {@link Validator} that ensures the value is an integer between 0 and
65535
+ */
public static final Validator PORT_VALIDATOR = createLongValidator(0,
65535, true);
/**
diff --git
a/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
b/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
index c3288e8..7e13b26 100644
--- a/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
+++ b/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
@@ -16,6 +16,8 @@
*/
package org.apache.nifi.components;
+import org.apache.nifi.components.listen.ListenPortDefinition;
+import org.apache.nifi.components.listen.TransportProtocol;
import org.apache.nifi.components.PropertyDescriptor.Builder;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
@@ -25,6 +27,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
@@ -215,6 +218,62 @@ public class TestPropertyDescriptor {
}
}
+ @Nested
+ class RegardingListenPortDefinitions {
+
+ @Test
+ void testListenPortDefinitionNullByDefault() {
+ final PropertyDescriptor descriptor = new
PropertyDescriptor.Builder()
+ .name("My Property")
+ .required(false)
+ .build();
+
+ assertNull(descriptor.getListenPortDefinition());
+ }
+
+ @Test
+ void testTcpWithoutApplicationProtocols() {
+ final PropertyDescriptor descriptor = new
PropertyDescriptor.Builder()
+ .name("TCP Port")
+ .identifiesListenPort(TransportProtocol.TCP)
+ .required(true)
+ .build();
+
+ final ListenPortDefinition actualListenPortDefinition =
descriptor.getListenPortDefinition();
+
+ assertEquals(TransportProtocol.TCP,
actualListenPortDefinition.getTransportProtocol());
+ assertEquals(Collections.emptyList(),
actualListenPortDefinition.getApplicationProtocols());
+ }
+
+ @Test
+ void testTcpWithApplicationProtocols() {
+ final PropertyDescriptor descriptor = new
PropertyDescriptor.Builder()
+ .name("HTTP Port")
+ .identifiesListenPort(TransportProtocol.TCP, "http/1.1", "h2")
+ .required(true)
+ .build();
+
+ final ListenPortDefinition actualListenPortDefinition =
descriptor.getListenPortDefinition();
+
+ assertEquals(TransportProtocol.TCP,
actualListenPortDefinition.getTransportProtocol());
+ assertEquals(List.of("http/1.1", "h2"),
actualListenPortDefinition.getApplicationProtocols());
+ }
+
+ @Test
+ void testUdpWithApplicationProtocols() {
+ final PropertyDescriptor descriptor = new
PropertyDescriptor.Builder()
+ .name("Port")
+ .identifiesListenPort(TransportProtocol.UDP, "syslog")
+ .required(true)
+ .build();
+
+ final ListenPortDefinition actualListenPortDefinition =
descriptor.getListenPortDefinition();
+
+ assertEquals(TransportProtocol.UDP,
actualListenPortDefinition.getTransportProtocol());
+ assertEquals(List.of("syslog"),
actualListenPortDefinition.getApplicationProtocols());
+ }
+ }
+
@Test
void testDependsOnWithEnumValue() {
final PropertyDescriptor dependentPropertyDescriptor = new
PropertyDescriptor.Builder()
diff --git
a/src/test/java/org/apache/nifi/documentation/xml/XmlDocumentationWriterTest.java
b/src/test/java/org/apache/nifi/documentation/xml/XmlDocumentationWriterTest.java
index 71e0833..e2ea473 100644
---
a/src/test/java/org/apache/nifi/documentation/xml/XmlDocumentationWriterTest.java
+++
b/src/test/java/org/apache/nifi/documentation/xml/XmlDocumentationWriterTest.java
@@ -19,6 +19,7 @@ package org.apache.nifi.documentation.xml;
import org.apache.nifi.annotation.documentation.DeprecationNotice;
import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.listen.TransportProtocol;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.controller.AbstractControllerService;
@@ -103,6 +104,7 @@ class XmlDocumentationWriterTest {
private static final PropertyDescriptor THIRD_PROPERTY = new
PropertyDescriptor.Builder()
.name("Third Property")
+ .identifiesListenPort(TransportProtocol.TCP, "http/1.1", "h2")
.dependsOn(SECOND_PROPERTY)
.dependsOn(FIRST_PROPERTY)
.build();
@@ -137,6 +139,14 @@ class XmlDocumentationWriterTest {
ResourceType.URL.name()
);
+ private static final String EXPECTED_LISTEN_PORT_TRANSPORT_PROTOCOL =
TransportProtocol.TCP.name();
+
+ private static final List<String>
EXPECTED_LISTEN_PORT_APPLICATION_PROTOCOLS = List.of(
+ "http/1.1",
+ "h2"
+ );
+
+
@Test
void testWriteMinimalProcessor() throws Exception {
final Processor processor = new MinimalProcessor();
@@ -190,6 +200,7 @@ class XmlDocumentationWriterTest {
assertDependentPropertyNamesMatched(document);
assertDependentPropertyValuesMatched(document);
assertResourceTypesMatched(document);
+ assertListenPortPropertiesMatched(document);
}
private void assertRelationshipsMatched(final Document document) throws
XPathExpressionException {
@@ -283,6 +294,27 @@ class XmlDocumentationWriterTest {
assertEquals(EXPECTED_RESOURCE_TYPE_NAMES, resourceTypeNames);
}
+ private void assertListenPortPropertiesMatched(final Document document)
throws XPathExpressionException {
+ final Node listenPortTransportProtocol =
findNode("/extension/properties/property[name='Third
Property']/listenPortDefinition/transportProtocol", document);
+ assertNotNull(listenPortTransportProtocol);
+ final String transportProtocol =
listenPortTransportProtocol.getTextContent();
+ assertEquals(EXPECTED_LISTEN_PORT_TRANSPORT_PROTOCOL,
transportProtocol);
+
+ final Node listenPortApplicationProtocolsNode =
findNode("/extension/properties/property[name='Third
Property']/listenPortDefinition/applicationProtocols", document);
+ assertNotNull(listenPortApplicationProtocolsNode);
+
+ final NodeList listenPortApplicationProtocols =
listenPortApplicationProtocolsNode.getChildNodes();
+ final List<String> applicationProtocolNames = new ArrayList<>();
+ for (int i = 0; i < listenPortApplicationProtocols.getLength(); i++) {
+ final Node applicationProtocolNode =
listenPortApplicationProtocols.item(i);
+ assertEquals("applicationProtocol",
applicationProtocolNode.getNodeName());
+ final String applicationProtocol =
applicationProtocolNode.getTextContent();
+ applicationProtocolNames.add(applicationProtocol);
+ }
+
+ assertEquals(EXPECTED_LISTEN_PORT_APPLICATION_PROTOCOLS,
applicationProtocolNames);
+ }
+
private Node findNode(final String expression, final Node node) throws
XPathExpressionException {
final XPathFactory factory = XPathFactory.newInstance();