Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/main/java/org/apache/nifi/components/PropertyDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -132,6 +140,7 @@ protected PropertyDescriptor(final Builder builder) {
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
Expand Down Expand Up @@ -217,6 +226,7 @@ public static final class Builder {
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) {
Expand All @@ -234,6 +244,7 @@ public Builder fromPropertyDescriptor(final PropertyDescriptor specDescriptor) {
this.validators = new ArrayList<>(specDescriptor.validators);
this.dependencies = new HashSet<>(specDescriptor.dependencies);
this.resourceDefinition = specDescriptor.resourceDefinition;
this.listenPortDefinition = specDescriptor.listenPortDefinition;
return this;
}

Expand Down Expand Up @@ -577,6 +588,28 @@ public Builder identifiesExternalResource(final ResourceCardinality cardinality,
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.
Expand Down Expand Up @@ -756,6 +789,10 @@ public ResourceDefinition getResourceDefinition() {
return resourceDefinition;
}

public ListenPortDefinition getListenPortDefinition() {
return listenPortDefinition;
}

@Override
public boolean equals(final Object other) {
if (this == other) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
61 changes: 61 additions & 0 deletions src/main/java/org/apache/nifi/components/listen/ListenPort.java
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
@@ -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();

}
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Loading