Skip to content

Commit 560f26e

Browse files
committed
NIFI-15155 Add ListenPort API
- 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
1 parent 6e2ed1e commit 560f26e

File tree

13 files changed

+593
-0
lines changed

13 files changed

+593
-0
lines changed

src/main/java/org/apache/nifi/components/PropertyDescriptor.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
*/
1717
package org.apache.nifi.components;
1818

19+
import org.apache.nifi.components.listen.ListenPortDefinition;
20+
import org.apache.nifi.components.listen.StandardListenPortDefinition;
21+
import org.apache.nifi.components.listen.TransportProtocol;
1922
import org.apache.nifi.components.resource.ResourceCardinality;
2023
import org.apache.nifi.components.resource.ResourceDefinition;
2124
import org.apache.nifi.components.resource.ResourceReference;
@@ -117,6 +120,11 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
117120
*/
118121
private final ResourceDefinition resourceDefinition;
119122

123+
/**
124+
* Metadata about the listen port that this property specifies, if applicable
125+
*/
126+
private final ListenPortDefinition listenPortDefinition;
127+
120128
protected PropertyDescriptor(final Builder builder) {
121129
this.displayName = builder.displayName == null ? builder.name : builder.displayName;
122130
this.name = builder.name;
@@ -132,6 +140,7 @@ protected PropertyDescriptor(final Builder builder) {
132140
this.validators = List.copyOf(builder.validators);
133141
this.dependencies = builder.dependencies == null ? Collections.emptySet() : Set.copyOf(builder.dependencies);
134142
this.resourceDefinition = builder.resourceDefinition;
143+
this.listenPortDefinition = builder.listenPortDefinition;
135144
}
136145

137146
@Override
@@ -217,6 +226,7 @@ public static final class Builder {
217226
private boolean dynamicallyModifiesClasspath = false;
218227
private Class<? extends ControllerService> controllerServiceDefinition;
219228
private ResourceDefinition resourceDefinition;
229+
private ListenPortDefinition listenPortDefinition;
220230
private List<Validator> validators = new ArrayList<>();
221231

222232
public Builder fromPropertyDescriptor(final PropertyDescriptor specDescriptor) {
@@ -234,6 +244,7 @@ public Builder fromPropertyDescriptor(final PropertyDescriptor specDescriptor) {
234244
this.validators = new ArrayList<>(specDescriptor.validators);
235245
this.dependencies = new HashSet<>(specDescriptor.dependencies);
236246
this.resourceDefinition = specDescriptor.resourceDefinition;
247+
this.listenPortDefinition = specDescriptor.listenPortDefinition;
237248
return this;
238249
}
239250

@@ -577,6 +588,28 @@ public Builder identifiesExternalResource(final ResourceCardinality cardinality,
577588
return this;
578589
}
579590

591+
/**
592+
* Specifies that this property defines a numbered host port that a server will bind to and listen for client-initiated connections.
593+
* <p>
594+
* 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
595+
* inbound connections to NiFi, such as gateways, ingress controllers, load balancers, and reverse proxies.
596+
* <p>
597+
* See {@link ListenPortDefinition} for guidance on how to specify protocols.
598+
* <p>
599+
* 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.
600+
*
601+
* @param transportProtocol specifies the layer 4 protocol used at the host operating system level for the port specified by this Property.
602+
* @param applicationProtocols optionally specifies one or more layer 7 protocols supported by the NiFi component listening on the port specified by this Property.
603+
* @return the builder
604+
*/
605+
public Builder identifiesListenPort(final TransportProtocol transportProtocol, final String... applicationProtocols) {
606+
Objects.requireNonNull(transportProtocol);
607+
final List<String> appProtocols = applicationProtocols != null ? Arrays.asList(applicationProtocols) : new ArrayList<>();
608+
609+
this.listenPortDefinition = new StandardListenPortDefinition(transportProtocol, appProtocols);
610+
return this;
611+
}
612+
580613
/**
581614
* 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.
582615
* 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 ResourceDefinition getResourceDefinition() {
756789
return resourceDefinition;
757790
}
758791

792+
public ListenPortDefinition getListenPortDefinition() {
793+
return listenPortDefinition;
794+
}
795+
759796
@Override
760797
public boolean equals(final Object other) {
761798
if (this == other) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.apache.nifi.components.listen;
17+
18+
import org.apache.nifi.controller.ConfigurationContext;
19+
20+
import java.util.List;
21+
22+
/**
23+
* An extension component (e.g., Processor or ControllerService) that creates one or more ingress network ports.
24+
* <p>
25+
* Implementing this interface allows {@link ListenPort}s provided by this component to be dynamically discoverable by the framework.
26+
* </p>
27+
* <p>
28+
* Typically, components implementing this interface should have at least one property described using a {@link org.apache.nifi.components.PropertyDescriptor}
29+
* that identifies a {@link ListenPortDefinition}. The Property Descriptor identifies a possible Listen Port that could be created.
30+
* This interface provides actual the port(s) configured based on component property values, along with additional ingress metadata.
31+
* </p>
32+
*/
33+
public interface ListenComponent {
34+
35+
/**
36+
* A list of listen ports provided by this component based on its current configuration.
37+
*
38+
* @param context provides access to convenience methods for obtaining property values
39+
* @return one or more listen ports that are actively configured to be provided by this component.
40+
*/
41+
List<ListenPort> getListenPorts(final ConfigurationContext context);
42+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.apache.nifi.components.listen;
17+
18+
import java.util.List;
19+
20+
/**
21+
* Represents a dynamically discoverable ingress port provided by a {@link ListenComponent}.
22+
*/
23+
public interface ListenPort {
24+
25+
/**
26+
* Get The operating system numbered port that is listening for network traffic.
27+
*
28+
* @return the port number
29+
*/
30+
int getPortNumber();
31+
32+
/**
33+
* Get the layer 4 transport protocol that is used at the OS networking level for this port.
34+
*
35+
* @return the transport protocol
36+
*/
37+
TransportProtocol getTransportProtocol();
38+
39+
/**
40+
* Get the currently configured application protocol(s) that this port supports.
41+
* <p>
42+
* 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),
43+
* but is currently configured to require h2, then this method should return [h2], not [http1.1, h2].
44+
* </p>
45+
* <p>
46+
* See {@link ListenPortDefinition#getApplicationProtocols()} for guidance on application protocol string values.
47+
* This method should return a subset of application protocol values specified by the corresponding PropertyDescriptor {@link ListenPortDefinition}.
48+
* </p>
49+
*
50+
* @return the application protocol(s) supported by this listen port, if applicable; otherwise an empty list.
51+
*/
52+
List<String> getApplicationProtocols();
53+
54+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.apache.nifi.components.listen;
17+
18+
import java.util.List;
19+
20+
/**
21+
* Defines the number and types of resources that allowed to be referenced by a component property
22+
*/
23+
public interface ListenPortDefinition {
24+
25+
/**
26+
* Specifies the transport protocol that is used for communication with this listen port.
27+
*
28+
* @return the {@link TransportProtocol} enum value
29+
*/
30+
TransportProtocol getTransportProtocol();
31+
32+
/**
33+
* Specifies zero, one, or many application protocols that could be supported on this listen port.
34+
* <p>
35+
* 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.
36+
* 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.
37+
* Inspecting the component with a Listen Port at runtime can determine more details about what has been configured.
38+
* <p>
39+
* General guidance for application protocol string values:
40+
* <ol>
41+
* <li>
42+
* Use IANA names when possible. For example:
43+
* <p>
44+
* <a href="https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml">
45+
* IANA Service Name and Transport Protocol Port Number Registry
46+
* </a>
47+
* <p>
48+
* <a href="https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids">
49+
* IANA TLS Application-Layer Protocol Negotiation (ALPN) Protocol IDs
50+
* </a>
51+
* <p>
52+
* <a href="https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml">
53+
* IANA Uniform Resource Identifier (URI) Schemes
54+
* </a>
55+
* </li>
56+
* <li>
57+
* 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
58+
* 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.
59+
* </li>
60+
* <li>
61+
* 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.
62+
* 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
63+
* 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
64+
* 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.
65+
* For example: ["http/1.1", "h2", "grpc"]
66+
* </li>
67+
* </ol>
68+
*
69+
* @return one or more application protocols that could be supported by the processor,
70+
* or an empty list if no application protocols are known to be supported.
71+
*/
72+
List<String> getApplicationProtocols();
73+
74+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
package org.apache.nifi.components.listen;
17+
18+
import java.util.Collections;
19+
import java.util.List;
20+
import java.util.Objects;
21+
22+
public class StandardListenPort implements ListenPort {
23+
24+
private final int portNumber;
25+
private final TransportProtocol transportProtocol;
26+
private final List<String> applicationProtocols;
27+
28+
private StandardListenPort(final Builder builder) {
29+
Objects.requireNonNull(builder.transportProtocol, "Transport protocol is required");
30+
Objects.requireNonNull(builder.applicationProtocols, "Application protocols is required. Use empty list if there are no application protocols.");
31+
32+
this.portNumber = builder.portNumber;
33+
this.transportProtocol = builder.transportProtocol;
34+
this.applicationProtocols = builder.applicationProtocols;
35+
}
36+
37+
@Override
38+
public int getPortNumber() {
39+
return portNumber;
40+
}
41+
42+
@Override
43+
public TransportProtocol getTransportProtocol() {
44+
return transportProtocol;
45+
}
46+
47+
@Override
48+
public List<String> getApplicationProtocols() {
49+
return applicationProtocols;
50+
}
51+
52+
@Override
53+
public String toString() {
54+
return "StandardListenPort{" +
55+
"portNumber=" + portNumber +
56+
", transportProtocol=" + transportProtocol +
57+
", applicationProtocols=" + applicationProtocols +
58+
'}';
59+
}
60+
61+
@Override
62+
public boolean equals(final Object o) {
63+
if (o == null || getClass() != o.getClass()) {
64+
return false;
65+
}
66+
final StandardListenPort that = (StandardListenPort) o;
67+
return portNumber == that.portNumber
68+
&& transportProtocol == that.transportProtocol
69+
&& Objects.equals(applicationProtocols, that.applicationProtocols);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return Objects.hash(portNumber, transportProtocol, applicationProtocols);
75+
}
76+
77+
public static Builder builder() {
78+
return new Builder();
79+
}
80+
81+
public static final class Builder {
82+
private int portNumber;
83+
private TransportProtocol transportProtocol;
84+
private List<String> applicationProtocols = Collections.emptyList();
85+
86+
public Builder portNumber(final int portNumber) {
87+
this.portNumber = portNumber;
88+
return this;
89+
}
90+
91+
public Builder transportProtocol(final TransportProtocol transportProtocol) {
92+
this.transportProtocol = transportProtocol;
93+
return this;
94+
}
95+
96+
public Builder applicationProtocols(final List<String> applicationProtocols) {
97+
this.applicationProtocols = applicationProtocols != null ? applicationProtocols : Collections.emptyList();
98+
return this;
99+
}
100+
101+
public StandardListenPort build() {
102+
return new StandardListenPort(this);
103+
}
104+
}
105+
106+
}

0 commit comments

Comments
 (0)