diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml
index 1c3b97fef0f11..9aacfdf0a8efa 100644
--- a/.github/workflows/ci-workflow.yml
+++ b/.github/workflows/ci-workflow.yml
@@ -170,6 +170,7 @@ jobs:
${{ env.MAVEN_VERIFY_COMMAND }}
${{ env.MAVEN_BUILD_PROFILES }}
-P report-code-coverage
+ -P python-unit-tests
${{ env.MAVEN_PROJECTS }}
- name: Codecov
uses: codecov/codecov-action@v3
@@ -238,6 +239,7 @@ jobs:
${{ env.MAVEN_COMMAND }}
${{ env.MAVEN_VERIFY_COMMAND }}
${{ env.MAVEN_BUILD_PROFILES }}
+ -P python-unit-tests
${{ env.MAVEN_PROJECTS }}
- name: Upload Test Reports
uses: actions/upload-artifact@v3
diff --git a/.gitignore b/.gitignore
index 2e9685e07b608..f45e08f9fcff5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ nb-configuration.xml
.vscode/
.java-version
/nifi-nar-bundles/nifi-py4j-bundle/nifi-python-extension-api/src/main/python/dist/
+__pycache__
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index eacdc9ed17a17..346d645fd06fd 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentInfo.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentInfo.java
index 5660b8c2eb77c..24c02a7c6430d 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentInfo.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentInfo.java
@@ -17,13 +17,11 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import java.io.Serializable;
-@ApiModel
public class AgentInfo implements Serializable {
private static final long serialVersionUID = 1L;
@@ -33,10 +31,7 @@ public class AgentInfo implements Serializable {
private RuntimeManifest agentManifest;
private AgentStatus status;
- @ApiModelProperty(
- value = "A unique identifier for the Agent",
- notes = "Usually set when the agent is provisioned and deployed",
- required = true)
+ @Schema(description = "A unique identifier for the Agent. Usually set when the agent is provisioned and deployed")
public String getIdentifier() {
return identifier;
}
@@ -45,9 +40,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty(
- value = "The class or category label of the agent, e.g., 'sensor-collector'",
- notes = "Usually set when the agent is provisioned and deployed")
+ @Schema(description = "The class or category label of the agent, e.g., 'sensor-collector'. Usually set when the agent is provisioned and deployed")
public String getAgentClass() {
return agentClass;
}
@@ -56,7 +49,7 @@ public void setAgentClass(String agentClass) {
this.agentClass = agentClass;
}
- @ApiModelProperty("The hash code of the manifest definition generated by the agent.")
+ @Schema(description = "The hash code of the manifest definition generated by the agent.")
public String getAgentManifestHash() {
return this.agentManifestHash;
}
@@ -65,7 +58,7 @@ public void setAgentManifestHash(String agentManifestHash) {
this.agentManifestHash = agentManifestHash;
}
- @ApiModelProperty("The specification of the agent's capabilities")
+ @Schema(description = "The specification of the agent's capabilities")
public RuntimeManifest getAgentManifest() {
return agentManifest;
}
@@ -74,7 +67,7 @@ public void setAgentManifest(RuntimeManifest runtimeManifest) {
this.agentManifest = runtimeManifest;
}
- @ApiModelProperty("A summary of the runtime status of the agent")
+ @Schema(description = "A summary of the runtime status of the agent")
public AgentStatus getStatus() {
return status;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java
index 3263fd38896b9..e8d527394b313 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentManifest.java
@@ -17,17 +17,16 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import java.util.Objects;
import java.util.Set;
+
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
-@ApiModel
public class AgentManifest extends RuntimeManifest {
private static final long serialVersionUID = 1L;
- @ApiModelProperty("All supported operations by agent")
+ @Schema(description = "All supported operations by agent")
private Set supportedOperations;
public AgentManifest() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositories.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositories.java
index aa6eb5e37e9d0..cdabb182272fc 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositories.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositories.java
@@ -17,18 +17,14 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
-@ApiModel
public class AgentRepositories implements Serializable {
private static final long serialVersionUID = 1L;
private AgentRepositoryStatus flowFile;
private AgentRepositoryStatus provenance;
- @ApiModelProperty
public AgentRepositoryStatus getFlowFile() {
return flowFile;
}
@@ -37,7 +33,6 @@ public void setFlowFile(AgentRepositoryStatus flowFile) {
this.flowFile = flowFile;
}
- @ApiModelProperty
public AgentRepositoryStatus getProvenance() {
return provenance;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositoryStatus.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositoryStatus.java
index 2b91b0ae475aa..b4e8fa2460a12 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositoryStatus.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentRepositoryStatus.java
@@ -17,12 +17,10 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
-@ApiModel
public class AgentRepositoryStatus implements Serializable {
private static final long serialVersionUID = 1L;
@@ -31,7 +29,7 @@ public class AgentRepositoryStatus implements Serializable {
private Long dataSize;
private Long dataSizeMax;
- @ApiModelProperty(value = "The number of items in the repository", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The number of items in the repository", allowableValues = "range[0, 9223372036854775807]")
public Long getSize() {
return size;
}
@@ -40,7 +38,7 @@ public void setSize(Long size) {
this.size = size;
}
- @ApiModelProperty(value = "The maximum number of items the repository is capable of storing", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The maximum number of items the repository is capable of storing", allowableValues = "range[0, 9223372036854775807]")
public Long getSizeMax() {
return sizeMax;
}
@@ -49,7 +47,7 @@ public void setSizeMax(Long sizeMax) {
this.sizeMax = sizeMax;
}
- @ApiModelProperty(value = "The data size (in Bytes) of all items in the repository", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The data size (in Bytes) of all items in the repository", allowableValues = "range[0, 9223372036854775807]")
public Long getDataSize() {
return dataSize;
}
@@ -58,7 +56,7 @@ public void setDataSize(Long dataSize) {
this.dataSize = dataSize;
}
- @ApiModelProperty(value = "The maximum data size (in Bytes) that the repository is capable of storing", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The maximum data size (in Bytes) that the repository is capable of storing", allowableValues = "range[0, 9223372036854775807]")
public Long getDataSizeMax() {
return dataSizeMax;
}
@@ -75,7 +73,7 @@ public void setDataSizeMax(Long dataSizeMax) {
*
* @return a decimal between [0, 1] representing the sizeMax utilization percentage
*/
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public Double getSizeUtilization() {
return size != null && sizeMax != null && sizeMax > 0 ? (double) size / (double) sizeMax : null;
}
@@ -86,7 +84,7 @@ public Double getSizeUtilization() {
*
* @return a decimal between [0, 1] representing the dataSizeMax utilization percentage
*/
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public Double getDataSizeUtilization() {
return dataSize != null && dataSizeMax != null && dataSizeMax > 0 ? (double) dataSize / (double) dataSizeMax : null;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentResourceConsumption.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentResourceConsumption.java
index 2ceb170564808..47e72bd7e373e 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentResourceConsumption.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentResourceConsumption.java
@@ -17,21 +17,20 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
/**
* Resource consumption of the given agent
*/
-@ApiModel
public class AgentResourceConsumption implements Serializable {
private static final long serialVersionUID = 1L;
private Long memoryUsage;
private Double cpuUtilization;
- @ApiModelProperty("The memory footprint of the agent in bytes.")
+ @Schema(description = "The memory footprint of the agent in bytes.")
public Long getMemoryUsage() {
return memoryUsage;
}
@@ -40,7 +39,7 @@ public void setMemoryUsage(Long memoryUsage) {
this.memoryUsage = memoryUsage;
}
- @ApiModelProperty("The CPU utilisation of the agent [0.0 - 1.0] and -1.0 in case of errors.")
+ @Schema(description = "The CPU utilisation of the agent [0.0 - 1.0] and -1.0 in case of errors.")
public Double getCpuUtilization() {
return cpuUtilization;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentStatus.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentStatus.java
index bb249617140c0..1d40294d5d5aa 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentStatus.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/AgentStatus.java
@@ -17,8 +17,8 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Map;
@@ -26,7 +26,6 @@
* Status of the aspects of the agent, including any agent components that are controllable by the C2 server, ie:
* - Repositories that can be cleared and their current state
*/
-@ApiModel
public class AgentStatus implements Serializable {
private static final long serialVersionUID = 1L;
@@ -35,7 +34,7 @@ public class AgentStatus implements Serializable {
private Map components;
private AgentResourceConsumption resourceConsumption;
- @ApiModelProperty("The number of milliseconds since the agent started.")
+ @Schema(description = "The number of milliseconds since the agent started.")
public Long getUptime() {
return uptime;
}
@@ -44,7 +43,7 @@ public void setUptime(Long uptime) {
this.uptime = uptime;
}
- @ApiModelProperty("Status and metrics for the agent repositories")
+ @Schema(description = "Status and metrics for the agent repositories")
public AgentRepositories getRepositories() {
return repositories;
}
@@ -53,7 +52,7 @@ public void setRepositories(AgentRepositories repositories) {
this.repositories = repositories;
}
- @ApiModelProperty("Status for shared agent components (that is, components that exist outside the context of a specific flow).")
+ @Schema(description = "Status for shared agent components (that is, components that exist outside the context of a specific flow).")
public Map getComponents() {
return components;
}
@@ -62,7 +61,7 @@ public void setComponents(Map components) {
this.components = components;
}
- @ApiModelProperty("Resource consumption details of the agent.")
+ @Schema(description = "Resource consumption details of the agent.")
public AgentResourceConsumption getResourceConsumption() {
return resourceConsumption;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Heartbeat.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Heartbeat.java
index 4c69c51f7e467..e09760fe640b7 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Heartbeat.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Heartbeat.java
@@ -17,8 +17,7 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
@@ -26,7 +25,6 @@
/**
* An object representation of a Heartbeat in the C2 protocol
*/
-@ApiModel
public class C2Heartbeat implements Serializable {
private static final long serialVersionUID = 1L;
@@ -37,7 +35,7 @@ public class C2Heartbeat implements Serializable {
private AgentInfo agentInfo;
private FlowInfo flowInfo;
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getIdentifier() {
return identifier;
}
@@ -46,7 +44,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public Long getCreated() {
return created;
}
@@ -55,7 +53,7 @@ public void setCreated(Long created) {
this.created = created;
}
- @ApiModelProperty("Metadata for the device")
+ @Schema(description = "Metadata for the device")
public DeviceInfo getDeviceInfo() {
return deviceInfo;
}
@@ -64,7 +62,7 @@ public void setDeviceInfo(DeviceInfo deviceInfo) {
this.deviceInfo = deviceInfo;
}
- @ApiModelProperty("Metadata for the agent installed on the device")
+ @Schema(description = "Metadata for the agent installed on the device")
public AgentInfo getAgentInfo() {
return agentInfo;
}
@@ -73,7 +71,7 @@ public void setAgentInfo(AgentInfo agentInfo) {
this.agentInfo = agentInfo;
}
- @ApiModelProperty("Metadata for the flow currently deployed to the agent")
+ @Schema(description = "Metadata for the flow currently deployed to the agent")
public FlowInfo getFlowInfo() {
return flowInfo;
}
@@ -83,22 +81,22 @@ public void setFlowInfo(FlowInfo flowInfo) {
}
// Convenience getters
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getDeviceId() {
return deviceInfo != null ? deviceInfo.getIdentifier() : null;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getAgentId() {
return agentInfo != null ? agentInfo.getIdentifier() : null;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getAgentClass() {
return agentInfo != null ? agentInfo.getAgentClass() : null;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getAgentManifestId() {
if (agentInfo != null && agentInfo.getAgentManifest() != null) {
return agentInfo.getAgentManifest().getIdentifier();
@@ -106,7 +104,7 @@ public String getAgentManifestId() {
return null;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getFlowId() {
return flowInfo != null ? flowInfo.getFlowId() : null;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2HeartbeatResponse.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2HeartbeatResponse.java
index 210504756e68b..4803b714fc548 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2HeartbeatResponse.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2HeartbeatResponse.java
@@ -17,18 +17,14 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
-@ApiModel
public class C2HeartbeatResponse implements Serializable {
private static final long serialVersionUID = 1L;
private List requestedOperations;
- @ApiModelProperty
public List getRequestedOperations() {
return requestedOperations;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Operation.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Operation.java
index a8c5edd7421d9..6dca210258909 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Operation.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2Operation.java
@@ -17,8 +17,8 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
@@ -26,7 +26,6 @@
import static java.lang.String.format;
-@ApiModel
public class C2Operation implements Serializable {
private static final long serialVersionUID = 1L;
@@ -36,7 +35,7 @@ public class C2Operation implements Serializable {
private Map args;
private Set dependencies;
- @ApiModelProperty(value = "A unique identifier for the operation", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+ @Schema(description = "A unique identifier for the operation", accessMode = Schema.AccessMode.READ_ONLY)
public String getIdentifier() {
return identifier;
}
@@ -45,7 +44,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty(value = "The type of operation", required = true)
+ @Schema(description = "The type of operation")
public OperationType getOperation() {
return operation;
}
@@ -58,13 +57,7 @@ public void setOperation(OperationType operation) {
this.operation = operation;
}
- @ApiModelProperty(
- value = "The primary operand of the operation",
- notes = "This is an optional field which contains the name of the entity that is target of the operation. " +
- "Most operations can be fully specified with zero or one operands." +
- "If no operand is needed, this field will be absent." +
- "If one operand is insufficient, the operation will contain an args map" +
- "with additional keyword parameters and values (see 'args').")
+ @Schema(description = "The primary operand of the operation")
public OperandType getOperand() {
return operand;
}
@@ -77,8 +70,7 @@ public void setOperand(OperandType operand) {
this.operand = operand;
}
- @ApiModelProperty(value = "If the operation requires arguments ",
- notes = "This is an optional field and only provided when an operation has arguments " +
+ @Schema(description = "This is an optional field and only provided when an operation has arguments " +
"in additional to the primary operand or optional parameters. Arguments are " +
"arbitrary key-value pairs whose interpretation is subject to the context" +
"of the operation and operand. For example, given:" +
@@ -95,7 +87,7 @@ public void setArgs(Map args) {
this.args = args;
}
- @ApiModelProperty("Optional set of operation ids that this operation depends on. " +
+ @Schema(description = "Optional set of operation ids that this operation depends on. " +
"Executing this operation is conditional on the success of all dependency operations.")
public Set getDependencies() {
return dependencies;
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationAck.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationAck.java
index d74967e34fe7f..08729e1b1f460 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationAck.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationAck.java
@@ -17,29 +17,28 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Objects;
-@ApiModel
public class C2OperationAck implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty("The id of the requested operation that is being acknowledged")
+ @Schema(description = "The id of the requested operation that is being acknowledged")
private String operationId;
- @ApiModelProperty("The agent's status response for this operation ID")
+ @Schema(description = "The agent's status response for this operation ID")
private C2OperationState operationState;
// Optional, additional details that can be included in an ACK
- @ApiModelProperty("Optionally, an ack can include device info that is relevant to the operation being acknowledged")
+ @Schema(description = "Optionally, an ack can include device info that is relevant to the operation being acknowledged")
private DeviceInfo deviceInfo;
- @ApiModelProperty("Optionally, an ack can include agent info that is relevant to the operation being acknowledged")
+ @Schema(description = "Optionally, an ack can include agent info that is relevant to the operation being acknowledged")
private AgentInfo agentInfo;
- @ApiModelProperty("Optionally, an ack can include flow info that is relevant to the operation being acknowledged")
+ @Schema(description = "Optionally, an ack can include flow info that is relevant to the operation being acknowledged")
private FlowInfo flowInfo;
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationState.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationState.java
index dc4e303e431dc..1e9c1e5bf24a5 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationState.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/C2OperationState.java
@@ -17,8 +17,8 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Objects;
@@ -33,16 +33,13 @@
* some insight, but a pre-condition and post-condition failure may better indicate how to arrive at operational
* success.
*/
-@ApiModel
public class C2OperationState implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty(value = "State of the operation performed", required = true, example = "FULLY_APPLIED")
+ @Schema(description = "State of the operation performed", example = "FULLY_APPLIED")
private OperationState state;
- @ApiModelProperty(
- value = "Additional details about the state",
- example = "Operation failed due to missing processor(s)")
+ @Schema(description = "Additional details about the state")
private String details;
public String getDetails() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/ComponentStatus.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/ComponentStatus.java
index 172ea7f9f732c..e9303a718a505 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/ComponentStatus.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/ComponentStatus.java
@@ -17,17 +17,13 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
-@ApiModel
public class ComponentStatus implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean running;
- @ApiModelProperty
public Boolean getRunning() {
return running;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/DeviceInfo.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/DeviceInfo.java
index 88d8be30f1097..85aa3fa471e14 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/DeviceInfo.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/DeviceInfo.java
@@ -17,24 +17,21 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Objects;
-@ApiModel
public class DeviceInfo implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty(
- value = "A unique, long-lived identifier for the device",
- required = true)
+ @Schema(description = "A unique, long-lived identifier for the device")
private String identifier;
- @ApiModelProperty("Metadata for the device hardware and operating system")
+ @Schema(description = "Metadata for the device hardware and operating system")
private SystemInfo systemInfo;
- @ApiModelProperty("Metadata for the network interface of this device")
+ @Schema(description = "Metadata for the network interface of this device")
private NetworkInfo networkInfo;
public String getIdentifier() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowInfo.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowInfo.java
index caf2626afc509..b6c9cc93ec1d6 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowInfo.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowInfo.java
@@ -17,12 +17,11 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Map;
-@ApiModel
public class FlowInfo implements Serializable {
private static final long serialVersionUID = 1L;
@@ -31,7 +30,7 @@ public class FlowInfo implements Serializable {
private Map components;
private Map queues;
- @ApiModelProperty(value = "A unique identifier of the flow currently deployed on the agent", required = true)
+ @Schema(description = "A unique identifier of the flow currently deployed on the agent")
public String getFlowId() {
return flowId;
}
@@ -40,7 +39,7 @@ public void setFlowId(String flowId) {
this.flowId = flowId;
}
- @ApiModelProperty("The Uniform Resource Identifier (URI) for the flow")
+ @Schema(description = "The Uniform Resource Identifier (URI) for the flow")
public FlowUri getFlowUri() {
return flowUri;
}
@@ -49,7 +48,7 @@ public void setFlowUri(FlowUri flowUri) {
this.flowUri = flowUri;
}
- @ApiModelProperty("Status and for each component that is part of the flow (e.g., processors)")
+ @Schema(description = "Status and for each component that is part of the flow (e.g., processors)")
public Map getComponents() {
return components;
}
@@ -58,7 +57,7 @@ public void setComponents(Map components) {
this.components = components;
}
- @ApiModelProperty("Status and metrics for each flow connection queue")
+ @Schema(description = "Status and metrics for each flow connection queue")
public Map getQueues() {
return queues;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowQueueStatus.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowQueueStatus.java
index 0d59267f510d1..5173185c076c0 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowQueueStatus.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowQueueStatus.java
@@ -17,12 +17,10 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
-@ApiModel
public class FlowQueueStatus implements Serializable {
private static final long serialVersionUID = 1L;
@@ -31,7 +29,7 @@ public class FlowQueueStatus implements Serializable {
private Long dataSize;
private Long dataSizeMax;
- @ApiModelProperty(value = "The number of flow files in the queue", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The number of flow files in the queue", allowableValues = "range[0, 9223372036854775807]")
public Long getSize() {
return size;
}
@@ -40,7 +38,7 @@ public void setSize(Long size) {
this.size = size;
}
- @ApiModelProperty(value = "The maximum number of flow files that the queue is configured to hold", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The maximum number of flow files that the queue is configured to hold", allowableValues = "range[0, 9223372036854775807]")
public Long getSizeMax() {
return sizeMax;
}
@@ -49,7 +47,7 @@ public void setSizeMax(Long sizeMax) {
this.sizeMax = sizeMax;
}
- @ApiModelProperty(value = "The size (in Bytes) of all flow files in the queue", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The size (in Bytes) of all flow files in the queue", allowableValues = "range[0, 9223372036854775807]")
public Long getDataSize() {
return dataSize;
}
@@ -58,7 +56,7 @@ public void setDataSize(Long dataSize) {
this.dataSize = dataSize;
}
- @ApiModelProperty(value = "The maximum size (in Bytes) that the queue is configured to hold", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "The maximum size (in Bytes) that the queue is configured to hold", allowableValues = "range[0, 9223372036854775807]")
public Long getDataSizeMax() {
return dataSizeMax;
}
@@ -75,7 +73,7 @@ public void setDataSizeMax(Long dataSizeMax) {
*
* @return a decimal between [0, 1] representing the sizeMax utilization percentage
*/
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public Double getSizeUtilization() {
return size != null && sizeMax != null && sizeMax > 0 ? (double) size / (double) sizeMax : null;
}
@@ -86,7 +84,7 @@ public Double getSizeUtilization() {
*
* @return a decimal between [0, 1] representing the dataSizeMax utilization percentage
*/
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public Double getDataSizeUtilization() {
return dataSize != null && dataSizeMax != null && dataSizeMax > 0 ? (double) dataSize / (double) dataSizeMax : null;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowUri.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowUri.java
index 9c3d7cd584daf..e668161db7881 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowUri.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/FlowUri.java
@@ -17,14 +17,12 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
-@ApiModel(
- value = "FlowUri",
- description = "Uniform Resource Identifier for flows, used to uniquely identify a flow version ")
+@Schema(description = "Uniform Resource Identifier for flows, used to uniquely identify a flow version")
public class FlowUri implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Location.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Location.java
index da02e2fe5d144..b1f8f412b642c 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Location.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Location.java
@@ -17,20 +17,14 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-
import java.io.Serializable;
import java.util.Objects;
-@ApiModel
public class Location implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty
private Double latitude;
- @ApiModelProperty
private Double longitude;
public Double getLatitude() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/NetworkInfo.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/NetworkInfo.java
index 17ea0b21652ef..f9a64d7bf1400 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/NetworkInfo.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/NetworkInfo.java
@@ -17,21 +17,20 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
-@ApiModel
public class NetworkInfo implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty("The device network interface ID")
+ @Schema(description = "The device network interface ID")
private String deviceId;
- @ApiModelProperty("The device network hostname")
+ @Schema(description = "The device network hostname")
private String hostname;
- @ApiModelProperty("The device network interface IP Address (v4 or v6)")
+ @Schema(description = "The device network interface IP Address (v4 or v6)")
private String ipAddress;
public String getDeviceId() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Operation.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Operation.java
index 89f2c0e6028bf..3a1edd2909f6a 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Operation.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/Operation.java
@@ -17,10 +17,8 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
public class Operation extends C2Operation {
private static final long serialVersionUID = 1L;
@@ -32,7 +30,7 @@ public class Operation extends C2Operation {
private Long created;
private Long updated;
- @ApiModelProperty("The identifier of the agent to which the operation applies")
+ @Schema(description = "The identifier of the agent to which the operation applies")
public String getTargetAgentId() {
return targetAgentId;
}
@@ -41,9 +39,7 @@ public void setTargetAgentId(String targetAgentId) {
this.targetAgentId = targetAgentId;
}
- @ApiModelProperty(
- value = "The current state of the operation",
- accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+ @Schema(description = "The current state of the operation", accessMode = Schema.AccessMode.READ_ONLY)
public OperationState getState() {
return state;
}
@@ -52,7 +48,7 @@ public void setState(OperationState state) {
this.state = state;
}
- @ApiModelProperty(hidden = true)
+ @Schema(hidden = true)
public String getBulkOperationId() {
return bulkOperationId;
}
@@ -61,11 +57,8 @@ public void setBulkOperationId(String bulkOperationId) {
this.bulkOperationId = bulkOperationId;
}
- @ApiModelProperty(value = "The verified identity of the C2 client that created the operation",
- accessMode = ApiModelProperty.AccessMode.READ_ONLY,
- notes = "This field is set by the server when an operation request is submitted to identify the origin. " +
- "When the C2 instance is secured, this is the client principal identity (e.g., certificate DN). " +
- "When the C2 instances is unsecured, this will be 'anonymous' as client identity can not be authenticated.")
+ @Schema(description = "The verified identity of the C2 client that created the operation",
+ accessMode = Schema.AccessMode.READ_ONLY)
public String getCreatedBy() {
return createdBy;
}
@@ -74,7 +67,7 @@ public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
- @ApiModelProperty("The time (in milliseconds since Epoch) that this operation was created")
+ @Schema(description = "The time (in milliseconds since Epoch) that this operation was created")
public Long getCreated() {
return created;
}
@@ -83,7 +76,7 @@ public void setCreated(Long created) {
this.created = created;
}
- @ApiModelProperty("The time (in milliseconds since Epoch) that this operation was last updated")
+ @Schema(description = "The time (in milliseconds since Epoch) that this operation was last updated")
public Long getUpdated() {
return updated;
}
@@ -92,7 +85,7 @@ public void setUpdated(Long updated) {
this.updated = updated;
}
- @ApiModelProperty(value = "Additional details about the state of this operation (such as an error message).")
+ @Schema(description = "Additional details about the state of this operation (such as an error message).")
public String getDetails() {
return details;
}
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java
index ede22079a6cf4..b90e981b00a01 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SupportedOperation.java
@@ -17,20 +17,19 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
-@ApiModel
public class SupportedOperation implements Serializable {
private static final long serialVersionUID = 1;
- @ApiModelProperty("The type of the operation supported by the agent")
+ @Schema(description = "The type of the operation supported by the agent")
private OperationType type;
- @ApiModelProperty("Operand specific properties defined by the agent")
+ @Schema(description = "Operand specific properties defined by the agent")
private Map> properties;
public OperationType getType() {
diff --git a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SystemInfo.java b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SystemInfo.java
index cd07ef19b76e6..8371c6fcb20ca 100644
--- a/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SystemInfo.java
+++ b/c2/c2-protocol/c2-protocol-api/src/main/java/org/apache/nifi/c2/protocol/api/SystemInfo.java
@@ -17,34 +17,32 @@
package org.apache.nifi.c2.protocol.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
-@ApiModel
public class SystemInfo implements Serializable {
private static final long serialVersionUID = 1L;
- @ApiModelProperty("Machine architecture of the device, e.g., ARM, x86")
+ @Schema(description = "Machine architecture of the device, e.g., ARM, x86")
private String machineArch;
private String operatingSystem;
- @ApiModelProperty(value = "Size of physical memory of the device in bytes", allowableValues = "range[0, 9223372036854775807]")
+ @Schema(description = "Size of physical memory of the device in bytes", allowableValues = "range[0, 9223372036854775807]")
private Long physicalMem;
- @ApiModelProperty(
- value = "Number of virtual cores on the device",
+ @Schema(description = "Number of virtual cores on the device",
name = "vCores",
allowableValues = "range[0, 2147483647]")
private Integer vCores;
- @ApiModelProperty
+ @Schema(description = "Memory usage")
private Long memoryUsage;
- @ApiModelProperty
+ @Schema(description = "CPU utilization")
private Double cpuUtilization;
+
private Double cpuLoadAverage;
public String getMachineArch() {
diff --git a/c2/c2-protocol/c2-protocol-component-api/pom.xml b/c2/c2-protocol/c2-protocol-component-api/pom.xml
index 36b982462828b..c281fd78d3683 100644
--- a/c2/c2-protocol/c2-protocol-component-api/pom.xml
+++ b/c2/c2-protocol/c2-protocol-component-api/pom.xml
@@ -31,7 +31,7 @@ limitations under the License.
nifi-api
- io.swagger
+ io.swagger.core.v3swagger-annotationscompile
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Attribute.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Attribute.java
index d6155fdea4c1f..457162dfa8d6e 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Attribute.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Attribute.java
@@ -16,16 +16,14 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
public class Attribute {
private String name;
private String description;
- @ApiModelProperty(value = "The name of the attribute")
+ @Schema(description = "The name of the attribute")
public String getName() {
return name;
}
@@ -34,7 +32,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the attribute")
+ @Schema(description = "The description of the attribute")
public String getDescription() {
return description;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/BuildInfo.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/BuildInfo.java
index fe1e75f8316eb..4e25a46e62488 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/BuildInfo.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/BuildInfo.java
@@ -17,11 +17,10 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
-@ApiModel
public class BuildInfo implements Serializable {
private static final long serialVersionUID = 1L;
@@ -32,7 +31,7 @@ public class BuildInfo implements Serializable {
private String compiler;
private String compilerFlags;
- @ApiModelProperty("The version number of the built component.")
+ @Schema(description = "The version number of the built component.")
public String getVersion() {
return version;
}
@@ -41,7 +40,7 @@ public void setVersion(String version) {
this.version = version;
}
- @ApiModelProperty("The SCM revision id of the source code used for this build.")
+ @Schema(description = "The SCM revision id of the source code used for this build.")
public String getRevision() {
return revision;
}
@@ -50,7 +49,7 @@ public void setRevision(String revision) {
this.revision = revision;
}
- @ApiModelProperty("The timestamp (milliseconds since Epoch) of the build.")
+ @Schema(description = "The timestamp (milliseconds since Epoch) of the build.")
public Long getTimestamp() {
return timestamp;
}
@@ -59,7 +58,7 @@ public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
- @ApiModelProperty("The target architecture of the built component.")
+ @Schema(description = "The target architecture of the built component.")
public String getTargetArch() {
return targetArch;
}
@@ -68,7 +67,7 @@ public void setTargetArch(String targetArch) {
this.targetArch = targetArch;
}
- @ApiModelProperty("The compiler used for the build")
+ @Schema(description = "The compiler used for the build")
public String getCompiler() {
return compiler;
}
@@ -77,7 +76,7 @@ public void setCompiler(String compiler) {
this.compiler = compiler;
}
- @ApiModelProperty("The compiler flags used for the build.")
+ @Schema(description = "The compiler flags used for the build.")
public String getCompilerFlags() {
return compilerFlags;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Bundle.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Bundle.java
index 6c009e7c9b47e..559e10fb506ad 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Bundle.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Bundle.java
@@ -17,13 +17,11 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
-@ApiModel
public class Bundle implements Serializable {
private static final long serialVersionUID = 1L;
@@ -49,10 +47,7 @@ public static Bundle defaultBundle() {
return new Bundle(DEFAULT_GROUP, DEFAULT_ARTIFACT, DEFAULT_VERSION);
}
- @ApiModelProperty(
- value = "The group id of the bundle",
- notes = "A globally unique group namespace, e.g., org.apache.nifi",
- required = true)
+ @Schema(description = "The group id of the bundle")
public String getGroup() {
return group;
}
@@ -61,10 +56,7 @@ public void setGroup(String group) {
this.group = group;
}
- @ApiModelProperty(
- value = "The artifact id of the bundle",
- notes = "Unique within the group",
- required = true)
+ @Schema(description = "The artifact id of the bundle")
public String getArtifact() {
return artifact;
}
@@ -73,7 +65,7 @@ public void setArtifact(String artifact) {
this.artifact = artifact;
}
- @ApiModelProperty("The version of the bundle artifact")
+ @Schema(description = "The version of the bundle artifact")
public String getVersion() {
return version;
}
@@ -82,10 +74,7 @@ public void setVersion(String version) {
this.version = version;
}
- @ApiModelProperty(value = "The full specification of the bundle contents",
- notes = "This is optional, as the group, artifact, and version are " +
- "also enough to reference a bundle in the case the bundle " +
- "specification has been published to a registry.")
+ @Schema(description = "The full specification of the bundle contents")
public ComponentManifest getComponentManifest() {
return componentManifest;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ComponentManifest.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ComponentManifest.java
index 0be677931867e..d7199d5467939 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ComponentManifest.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ComponentManifest.java
@@ -17,13 +17,12 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
-@ApiModel
public class ComponentManifest implements Serializable {
private static final long serialVersionUID = 1L;
@@ -32,7 +31,7 @@ public class ComponentManifest implements Serializable {
private List processors;
private List reportingTasks;
- @ApiModelProperty("Public interfaces defined in this bundle")
+ @Schema(description = "Public interfaces defined in this bundle")
public List getApis() {
return (apis != null ? Collections.unmodifiableList(apis) : null);
}
@@ -41,7 +40,7 @@ public void setApis(List apis) {
this.apis = apis;
}
- @ApiModelProperty("Controller Services provided in this bundle")
+ @Schema(description = "Controller Services provided in this bundle")
public List getControllerServices() {
return (controllerServices != null ? Collections.unmodifiableList(controllerServices) : null);
}
@@ -50,7 +49,7 @@ public void setControllerServices(List controllerSe
this.controllerServices = controllerServices;
}
- @ApiModelProperty("Processors provided in this bundle")
+ @Schema(description = "Processors provided in this bundle")
public List getProcessors() {
return (processors != null ? Collections.unmodifiableList(processors) : null);
}
@@ -59,7 +58,7 @@ public void setProcessors(List processors) {
this.processors = processors;
}
- @ApiModelProperty("Reporting Tasks provided in this bundle")
+ @Schema(description = "Reporting Tasks provided in this bundle")
public List getReportingTasks() {
return (reportingTasks != null ? Collections.unmodifiableList(reportingTasks) : null);
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ConfigurableExtensionDefinition.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ConfigurableExtensionDefinition.java
index 99216c7cf9cd7..b7321ac9dfa15 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ConfigurableExtensionDefinition.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ConfigurableExtensionDefinition.java
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -33,7 +33,7 @@ public abstract class ConfigurableExtensionDefinition extends ExtensionComponent
private List dynamicProperties;
@Override
- @ApiModelProperty("Descriptions of configuration properties applicable to this component.")
+ @Schema(description = "Descriptions of configuration properties applicable to this component.")
public Map getPropertyDescriptors() {
return (propertyDescriptors != null ? Collections.unmodifiableMap(propertyDescriptors) : null);
}
@@ -44,7 +44,7 @@ public void setPropertyDescriptors(LinkedHashMap pro
}
@Override
- @ApiModelProperty("Whether or not this component makes use of dynamic (user-set) properties.")
+ @Schema(description = "Whether or not this component makes use of dynamic (user-set) properties.")
public boolean getSupportsDynamicProperties() {
return supportsDynamicProperties;
}
@@ -55,7 +55,7 @@ public void setSupportsDynamicProperties(boolean supportsDynamicProperties) {
}
@Override
- @ApiModelProperty("Whether or not this component makes use of sensitive dynamic (user-set) properties.")
+ @Schema(description = "Whether or not this component makes use of sensitive dynamic (user-set) properties.")
public boolean getSupportsSensitiveDynamicProperties() {
return supportsSensitiveDynamicProperties;
}
@@ -66,7 +66,7 @@ public void setSupportsSensitiveDynamicProperties(boolean supportsSensitiveDynam
}
@Override
- @ApiModelProperty("Describes the dynamic properties supported by this component")
+ @Schema(description = "Describes the dynamic properties supported by this component")
public List getDynamicProperties() {
return dynamicProperties;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ControllerServiceDefinition.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ControllerServiceDefinition.java
index 89279e04d7213..0457b11c34001 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ControllerServiceDefinition.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ControllerServiceDefinition.java
@@ -17,9 +17,6 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public class ControllerServiceDefinition extends ConfigurableExtensionDefinition {
private static final long serialVersionUID = 1L;
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DefinedType.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DefinedType.java
index b59f00cb6222a..91acb08d62da8 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DefinedType.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DefinedType.java
@@ -17,8 +17,7 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
@@ -26,7 +25,6 @@
/**
* A reference to a defined type identified by bundle and fully qualified class type identifiers
*/
-@ApiModel
public class DefinedType implements Serializable {
private static final long serialVersionUID = 1L;
@@ -36,7 +34,7 @@ public class DefinedType implements Serializable {
private String type;
private String typeDescription;
- @ApiModelProperty("The group name of the bundle that provides the referenced type.")
+ @Schema(description = "The group name of the bundle that provides the referenced type.")
public String getGroup() {
return group;
}
@@ -45,7 +43,7 @@ public void setGroup(String group) {
this.group = group;
}
- @ApiModelProperty("The artifact name of the bundle that provides the referenced type.")
+ @Schema(description = "The artifact name of the bundle that provides the referenced type.")
public String getArtifact() {
return artifact;
}
@@ -54,7 +52,7 @@ public void setArtifact(String artifact) {
this.artifact = artifact;
}
- @ApiModelProperty("The version of the bundle that provides the referenced type.")
+ @Schema(description = "The version of the bundle that provides the referenced type.")
public String getVersion() {
return version;
}
@@ -63,10 +61,7 @@ public void setVersion(String version) {
this.version = version;
}
- @ApiModelProperty(
- value = "The fully-qualified class type",
- required = true,
- notes = "For example, 'org.apache.nifi.GetFile' or 'org::apache:nifi::minifi::GetFile'")
+ @Schema(description = "The fully-qualified class type")
public String getType() {
return type;
}
@@ -75,7 +70,7 @@ public void setType(String type) {
this.type = type;
}
- @ApiModelProperty("The description of the type.")
+ @Schema(description = "The description of the type.")
public String getTypeDescription() {
return typeDescription;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicProperty.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicProperty.java
index 25e247dbec799..a219b94282842 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicProperty.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicProperty.java
@@ -16,11 +16,9 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.expression.ExpressionLanguageScope;
-@ApiModel
public class DynamicProperty {
private String name;
@@ -28,7 +26,7 @@ public class DynamicProperty {
private String description;
private ExpressionLanguageScope expressionLanguageScope;
- @ApiModelProperty(value = "The description of the dynamic property name")
+ @Schema(description = "The description of the dynamic property name")
public String getName() {
return name;
}
@@ -37,7 +35,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the dynamic property value")
+ @Schema(description = "The description of the dynamic property value")
public String getValue() {
return value;
}
@@ -46,7 +44,7 @@ public void setValue(String value) {
this.value = value;
}
- @ApiModelProperty(value = "The description of the dynamic property")
+ @Schema(description = "The description of the dynamic property")
public String getDescription() {
return description;
}
@@ -55,7 +53,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "The scope of the expression language support")
+ @Schema(description = "The scope of the expression language support")
public ExpressionLanguageScope getExpressionLanguageScope() {
return expressionLanguageScope;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicRelationship.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicRelationship.java
index 0934490ee31d4..5b6efff25d858 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicRelationship.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/DynamicRelationship.java
@@ -16,16 +16,14 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
public class DynamicRelationship {
private String name;
private String description;
- @ApiModelProperty(value = "The description of the dynamic relationship name")
+ @Schema(description = "The description of the dynamic relationship name")
public String getName() {
return name;
}
@@ -34,7 +32,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the dynamic relationship")
+ @Schema(description = "The description of the dynamic relationship")
public String getDescription() {
return description;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
index 485ee1563c9ef..f3e94a246c83d 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ExtensionComponent.java
@@ -17,8 +17,7 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Collections;
import java.util.List;
@@ -28,7 +27,6 @@
/**
* A component provided by an extension bundle
*/
-@ApiModel
public class ExtensionComponent extends DefinedType {
private static final long serialVersionUID = 1L;
@@ -52,7 +50,7 @@ public class ExtensionComponent extends DefinedType {
private boolean additionalDetails;
- @ApiModelProperty("The build metadata for this component")
+ @Schema(description = "The build metadata for this component")
public BuildInfo getBuildInfo() {
return buildInfo;
}
@@ -61,7 +59,7 @@ public void setBuildInfo(BuildInfo buildInfo) {
this.buildInfo = buildInfo;
}
- @ApiModelProperty("If this type represents a provider for an interface, this lists the APIs it implements")
+ @Schema(description = "If this type represents a provider for an interface, this lists the APIs it implements")
public List getProvidedApiImplementations() {
return (providedApiImplementations != null ? Collections.unmodifiableList(providedApiImplementations) : null);
@@ -71,7 +69,7 @@ public void setProvidedApiImplementations(List providedApiImplement
this.providedApiImplementations = providedApiImplementations;
}
- @ApiModelProperty("The tags associated with this type")
+ @Schema(description = "The tags associated with this type")
public Set getTags() {
return (tags != null ? Collections.unmodifiableSet(tags) : null);
}
@@ -80,7 +78,7 @@ public void setTags(Set tags) {
this.tags = tags;
}
- @ApiModelProperty("The names of other component types that may be related")
+ @Schema(description = "The names of other component types that may be related")
public Set getSeeAlso() {
return seeAlso;
}
@@ -89,7 +87,7 @@ public void setSeeAlso(Set seeAlso) {
this.seeAlso = seeAlso;
}
- @ApiModelProperty("Whether or not the component has been deprecated")
+ @Schema(description = "Whether or not the component has been deprecated")
public Boolean getDeprecated() {
return deprecated;
}
@@ -98,7 +96,7 @@ public void setDeprecated(Boolean deprecated) {
this.deprecated = deprecated;
}
- @ApiModelProperty("If this component has been deprecated, this optional field can be used to provide an explanation")
+ @Schema(description = "If this component has been deprecated, this optional field can be used to provide an explanation")
public String getDeprecationReason() {
return deprecationReason;
}
@@ -107,7 +105,7 @@ public void setDeprecationReason(String deprecationReason) {
this.deprecationReason = deprecationReason;
}
- @ApiModelProperty("If this component has been deprecated, this optional field provides alternatives to use")
+ @Schema(description = "If this component has been deprecated, this optional field provides alternatives to use")
public Set getDeprecationAlternatives() {
return deprecationAlternatives;
}
@@ -116,7 +114,7 @@ public void setDeprecationAlternatives(Set deprecationAlternatives) {
this.deprecationAlternatives = deprecationAlternatives;
}
- @ApiModelProperty("Whether or not the component has a general restriction")
+ @Schema(description = "Whether or not the component has a general restriction")
public Boolean isRestricted() {
return restricted;
}
@@ -129,7 +127,7 @@ public void setRestricted(Boolean restricted) {
this.restricted = restricted;
}
- @ApiModelProperty("An optional description of the general restriction")
+ @Schema(description = "An optional description of the general restriction")
public String getRestrictedExplanation() {
return restrictedExplanation;
}
@@ -138,7 +136,7 @@ public void setRestrictedExplanation(String restrictedExplanation) {
this.restrictedExplanation = restrictedExplanation;
}
- @ApiModelProperty("Explicit restrictions that indicate a require permission to use the component")
+ @Schema(description = "Explicit restrictions that indicate a require permission to use the component")
public Set getExplicitRestrictions() {
return explicitRestrictions;
}
@@ -147,7 +145,7 @@ public void setExplicitRestrictions(Set explicitRestrictions) {
this.explicitRestrictions = explicitRestrictions;
}
- @ApiModelProperty("Indicates if the component stores state")
+ @Schema(description = "Indicates if the component stores state")
public Stateful getStateful() {
return stateful;
}
@@ -156,7 +154,7 @@ public void setStateful(Stateful stateful) {
this.stateful = stateful;
}
- @ApiModelProperty("The system resource considerations for the given component")
+ @Schema(description = "The system resource considerations for the given component")
public List getSystemResourceConsiderations() {
return systemResourceConsiderations;
}
@@ -165,7 +163,7 @@ public void setSystemResourceConsiderations(List sy
this.systemResourceConsiderations = systemResourceConsiderations;
}
- @ApiModelProperty("Indicates if the component has additional details documentation")
+ @Schema(description = "Indicates if the component has additional details documentation")
public boolean isAdditionalDetails() {
return additionalDetails;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/MultiProcessorUseCase.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/MultiProcessorUseCase.java
new file mode 100644
index 0000000000000..ccc238bdee237
--- /dev/null
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/MultiProcessorUseCase.java
@@ -0,0 +1,67 @@
+/*
+ * 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 MultiProcessorUseCase implements Serializable {
+ private String description;
+ private String notes;
+ private List keywords;
+ private List configurations;
+
+ @Schema(description="A description of the use case")
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ @Schema(description="Any pertinent notes about the use case")
+ public String getNotes() {
+ return notes;
+ }
+
+ public void setNotes(final String notes) {
+ this.notes = notes;
+ }
+
+ @Schema(description="Keywords that pertain to the use csae")
+ public List getKeywords() {
+ return keywords;
+ }
+
+ public void setKeywords(final List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Schema(description="A description of how to configure the Processor to perform the task described in the use case")
+ public List getConfigurations() {
+ return configurations;
+ }
+
+ public void setConfigurations(final List configurations) {
+ this.configurations = configurations;
+ }
+
+}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorConfiguration.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorConfiguration.java
new file mode 100644
index 0000000000000..0416e19e47cad
--- /dev/null
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+public class ProcessorConfiguration implements Serializable {
+ private String processorClassName;
+ private String configuration;
+
+ @Schema(description="The fully qualified classname of the Processor that should be used to accomplish the use case")
+ public String getProcessorClassName() {
+ return processorClassName;
+ }
+
+ public void setProcessorClassName(final String processorClassName) {
+ this.processorClassName = processorClassName;
+ }
+
+ @Schema(description="A description of how the Processor should be configured in order to accomplish the use case")
+ public String getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(final String configuration) {
+ this.configuration = configuration;
+ }
+}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorDefinition.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorDefinition.java
index 99ea2429b570a..f35a6add63298 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorDefinition.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ProcessorDefinition.java
@@ -17,15 +17,13 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.annotation.behavior.InputRequirement;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-@ApiModel
public class ProcessorDefinition extends ConfigurableExtensionDefinition {
private static final long serialVersionUID = 1L;
@@ -53,7 +51,11 @@ public class ProcessorDefinition extends ConfigurableExtensionDefinition {
private List readsAttributes;
private List writesAttributes;
- @ApiModelProperty("Any input requirements this processor has.")
+ private List useCases;
+ private List multiProcessorUseCases;
+
+
+ @Schema(description="Any input requirements this processor has.")
public InputRequirement.Requirement getInputRequirement() {
return inputRequirement;
}
@@ -62,7 +64,7 @@ public void setInputRequirement(InputRequirement.Requirement inputRequirement) {
this.inputRequirement = inputRequirement;
}
- @ApiModelProperty("The supported relationships for this processor.")
+ @Schema(description = "The supported relationships for this processor.")
public List getSupportedRelationships() {
return (supportedRelationships == null ? Collections.emptyList() : Collections.unmodifiableList(supportedRelationships));
}
@@ -71,7 +73,7 @@ public void setSupportedRelationships(List supportedRelationships)
this.supportedRelationships = supportedRelationships;
}
- @ApiModelProperty("Whether or not this processor supports dynamic relationships.")
+ @Schema(description = "Whether or not this processor supports dynamic relationships.")
public boolean getSupportsDynamicRelationships() {
return supportsDynamicRelationships;
}
@@ -80,7 +82,7 @@ public void setSupportsDynamicRelationships(boolean supportsDynamicRelationships
this.supportsDynamicRelationships = supportsDynamicRelationships;
}
- @ApiModelProperty("If the processor supports dynamic relationships, this describes the dynamic relationship")
+ @Schema(description = "If the processor supports dynamic relationships, this describes the dynamic relationship")
public DynamicRelationship getDynamicRelationship() {
return dynamicRelationship;
}
@@ -89,7 +91,7 @@ public void setDynamicRelationship(DynamicRelationship dynamicRelationship) {
this.dynamicRelationship = dynamicRelationship;
}
- @ApiModelProperty("Whether or not this processor should be triggered serially (i.e. no concurrent execution).")
+ @Schema(description = "Whether or not this processor should be triggered serially (i.e. no concurrent execution).")
public boolean getTriggerSerially() {
return triggerSerially;
}
@@ -98,7 +100,7 @@ public void setTriggerSerially(boolean triggerSerially) {
this.triggerSerially = triggerSerially;
}
- @ApiModelProperty("Whether or not this processor should be triggered when incoming queues are empty.")
+ @Schema(description = "Whether or not this processor should be triggered when incoming queues are empty.")
public boolean getTriggerWhenEmpty() {
return triggerWhenEmpty;
}
@@ -107,7 +109,7 @@ public void setTriggerWhenEmpty(boolean triggerWhenEmpty) {
this.triggerWhenEmpty = triggerWhenEmpty;
}
- @ApiModelProperty("Whether or not this processor should be triggered when any destination queue has room.")
+ @Schema(description = "Whether or not this processor should be triggered when any destination queue has room.")
public boolean getTriggerWhenAnyDestinationAvailable() {
return triggerWhenAnyDestinationAvailable;
}
@@ -116,7 +118,7 @@ public void setTriggerWhenAnyDestinationAvailable(boolean triggerWhenAnyDestinat
this.triggerWhenAnyDestinationAvailable = triggerWhenAnyDestinationAvailable;
}
- @ApiModelProperty("Whether or not this processor supports batching. If a Processor uses this annotation, " +
+ @Schema(description = "Whether or not this processor supports batching. If a Processor uses this annotation, " +
"it allows the Framework to batch calls to session commits, as well as allowing the Framework to return " +
"the same session multiple times.")
public boolean getSupportsBatching() {
@@ -127,7 +129,7 @@ public void setSupportsBatching(boolean supportsBatching) {
this.supportsBatching = supportsBatching;
}
- @ApiModelProperty("Whether or not this processor should be scheduled only on the primary node in a cluster.")
+ @Schema(description = "Whether or not this processor should be scheduled only on the primary node in a cluster.")
public boolean getPrimaryNodeOnly() {
return primaryNodeOnly;
}
@@ -136,7 +138,7 @@ public void setPrimaryNodeOnly(boolean primaryNodeOnly) {
this.primaryNodeOnly = primaryNodeOnly;
}
- @ApiModelProperty("Whether or not this processor is considered side-effect free. Side-effect free indicate that the " +
+ @Schema(description = "Whether or not this processor is considered side-effect free. Side-effect free indicate that the " +
"processor's operations on FlowFiles can be safely repeated across process sessions.")
public boolean getSideEffectFree() {
return sideEffectFree;
@@ -146,7 +148,7 @@ public void setSideEffectFree(boolean sideEffectFree) {
this.sideEffectFree = sideEffectFree;
}
- @ApiModelProperty("The supported scheduling strategies, such as TIME_DRIVER, CRON, or EVENT_DRIVEN.")
+ @Schema(description = "The supported scheduling strategies, such as TIME_DRIVER, CRON, or EVENT_DRIVEN.")
public List getSupportedSchedulingStrategies() {
return supportedSchedulingStrategies;
}
@@ -155,7 +157,7 @@ public void setSupportedSchedulingStrategies(List supportedSchedulingStr
this.supportedSchedulingStrategies = supportedSchedulingStrategies;
}
- @ApiModelProperty("The default scheduling strategy for the processor.")
+ @Schema(description = "The default scheduling strategy for the processor.")
public String getDefaultSchedulingStrategy() {
return defaultSchedulingStrategy;
}
@@ -164,7 +166,7 @@ public void setDefaultSchedulingStrategy(String defaultSchedulingStrategy) {
this.defaultSchedulingStrategy = defaultSchedulingStrategy;
}
- @ApiModelProperty("The default concurrent tasks for each scheduling strategy.")
+ @Schema(description = "The default concurrent tasks for each scheduling strategy.")
public Map getDefaultConcurrentTasksBySchedulingStrategy() {
return defaultConcurrentTasksBySchedulingStrategy != null ? Collections.unmodifiableMap(defaultConcurrentTasksBySchedulingStrategy) : null;
}
@@ -173,7 +175,7 @@ public void setDefaultConcurrentTasksBySchedulingStrategy(Map d
this.defaultConcurrentTasksBySchedulingStrategy = defaultConcurrentTasksBySchedulingStrategy;
}
- @ApiModelProperty("The default scheduling period for each scheduling strategy. " +
+ @Schema(description = "The default scheduling period for each scheduling strategy. " +
"The scheduling period is expected to be a time period, such as \"30 sec\".")
public Map getDefaultSchedulingPeriodBySchedulingStrategy() {
return defaultSchedulingPeriodBySchedulingStrategy != null ? Collections.unmodifiableMap(defaultSchedulingPeriodBySchedulingStrategy) : null;
@@ -183,7 +185,7 @@ public void setDefaultSchedulingPeriodBySchedulingStrategy(Map d
this.defaultSchedulingPeriodBySchedulingStrategy = defaultSchedulingPeriodBySchedulingStrategy;
}
- @ApiModelProperty("The default penalty duration as a time period, such as \"30 sec\".")
+ @Schema(description = "The default penalty duration as a time period, such as \"30 sec\".")
public String getDefaultPenaltyDuration() {
return defaultPenaltyDuration;
}
@@ -192,7 +194,7 @@ public void setDefaultPenaltyDuration(String defaultPenaltyDuration) {
this.defaultPenaltyDuration = defaultPenaltyDuration;
}
- @ApiModelProperty("The default yield duration as a time period, such as \"1 sec\".")
+ @Schema(description = "The default yield duration as a time period, such as \"1 sec\".")
public String getDefaultYieldDuration() {
return defaultYieldDuration;
}
@@ -201,7 +203,7 @@ public void setDefaultYieldDuration(String defaultYieldDuration) {
this.defaultYieldDuration = defaultYieldDuration;
}
- @ApiModelProperty("The default bulletin level, such as WARN, INFO, DEBUG, etc.")
+ @Schema(description = "The default bulletin level, such as WARN, INFO, DEBUG, etc.")
public String getDefaultBulletinLevel() {
return defaultBulletinLevel;
}
@@ -210,7 +212,7 @@ public void setDefaultBulletinLevel(String defaultBulletinLevel) {
this.defaultBulletinLevel = defaultBulletinLevel;
}
- @ApiModelProperty("The FlowFile attributes this processor reads")
+ @Schema(description = "The FlowFile attributes this processor reads")
public List getReadsAttributes() {
return readsAttributes;
}
@@ -219,7 +221,7 @@ public void setReadsAttributes(List readsAttributes) {
this.readsAttributes = readsAttributes;
}
- @ApiModelProperty("The FlowFile attributes this processor writes/updates")
+ @Schema(description = "The FlowFile attributes this processor writes/updates")
public List getWritesAttributes() {
return writesAttributes;
}
@@ -227,4 +229,22 @@ public List getWritesAttributes() {
public void setWritesAttributes(List writesAttributes) {
this.writesAttributes = writesAttributes;
}
+
+ @Schema(description="A list of use cases that have been documented for this Processor")
+ public List getUseCases() {
+ return useCases;
+ }
+
+ public void setUseCases(final List useCases) {
+ this.useCases = useCases;
+ }
+
+ @Schema(description="A list of use cases that have been documented that involve this Processor in conjunction with other Processors")
+ public List getMultiProcessorUseCases() {
+ return multiProcessorUseCases;
+ }
+
+ public void setMultiProcessorUseCases(final List multiProcessorUseCases) {
+ this.multiProcessorUseCases = multiProcessorUseCases;
+ }
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyAllowableValue.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyAllowableValue.java
index e70e80a5ace9e..7e33631e7d92f 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyAllowableValue.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyAllowableValue.java
@@ -17,13 +17,11 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Objects;
-@ApiModel
public class PropertyAllowableValue implements Serializable {
private static final long serialVersionUID = 1L;
@@ -31,7 +29,7 @@ public class PropertyAllowableValue implements Serializable {
private String displayName;
private String description;
- @ApiModelProperty(value = "The internal value", required = true)
+ @Schema(description = "The internal value")
public String getValue() {
return value;
}
@@ -40,7 +38,7 @@ public void setValue(String value) {
this.value = value;
}
- @ApiModelProperty("The display name of the value, if different from the internal value")
+ @Schema(description = "The display name of the value, if different from the internal value")
public String getDisplayName() {
return displayName;
}
@@ -49,7 +47,7 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- @ApiModelProperty("The description of the value, e.g., the behavior it produces.")
+ @Schema(description = "The description of the value, e.g., the behavior it produces.")
public String getDescription() {
return description;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDependency.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDependency.java
index dbac2d16dea8f..c49193c09d385 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDependency.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyDependency.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.List;
-@ApiModel
public class PropertyDependency implements Serializable {
private static final long serialVersionUID = 1L;
@@ -30,7 +28,7 @@ public class PropertyDependency implements Serializable {
private String propertyDisplayName;
private List dependentValues;
- @ApiModelProperty("The name of the property that is depended upon")
+ @Schema(description = "The name of the property that is depended upon")
public String getPropertyName() {
return propertyName;
}
@@ -39,7 +37,7 @@ public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
- @ApiModelProperty("The name of the property that is depended upon")
+ @Schema(description = "The name of the property that is depended upon")
public String getPropertyDisplayName() {
return propertyDisplayName;
}
@@ -48,7 +46,7 @@ public void setPropertyDisplayName(String propertyDisplayName) {
this.propertyDisplayName = propertyDisplayName;
}
- @ApiModelProperty("The values that satisfy the dependency")
+ @Schema(description = "The values that satisfy the dependency")
public List getDependentValues() {
return dependentValues;
}
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 2ebd040bc0026..c0fe2ce527fd3 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,15 +16,13 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
-import org.apache.nifi.expression.ExpressionLanguageScope;
+import io.swagger.v3.oas.annotations.media.Schema;
+import org.apache.nifi.expression.ExpressionLanguageScope;
-@ApiModel
public class PropertyDescriptor implements Serializable {
private static final long serialVersionUID = 1L;
@@ -44,7 +42,7 @@ public class PropertyDescriptor implements Serializable {
private PropertyResourceDefinition resourceDefinition;
private List dependencies;
- @ApiModelProperty(value = "The name of the property key", required = true)
+ @Schema(description = "The name of the property key")
public String getName() {
return name;
}
@@ -53,7 +51,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The display name of the property key, if different from the name")
+ @Schema(description = "The display name of the property key, if different from the name")
public String getDisplayName() {
return displayName;
}
@@ -62,7 +60,7 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- @ApiModelProperty("The description of what the property does")
+ @Schema(description = "The description of what the property does")
public String getDescription() {
return description;
}
@@ -71,7 +69,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty("A list of the allowable values for the property")
+ @Schema(description = "A list of the allowable values for the property")
public List getAllowableValues() {
return (allowableValues != null ? Collections.unmodifiableList(allowableValues) : null);
}
@@ -80,7 +78,7 @@ public void setAllowableValues(List allowableValues) {
this.allowableValues = allowableValues;
}
- @ApiModelProperty("The default value if a user-set value is not specified")
+ @Schema(description = "The default value if a user-set value is not specified")
public String getDefaultValue() {
return defaultValue;
}
@@ -89,7 +87,7 @@ public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
- @ApiModelProperty("Whether or not the property is required for the component")
+ @Schema(description = "Whether or not the property is required for the component")
public boolean getRequired() {
return required;
}
@@ -98,7 +96,7 @@ public void setRequired(boolean required) {
this.required = required;
}
- @ApiModelProperty("Whether or not the value of the property is considered sensitive (e.g., passwords and keys)")
+ @Schema(description = "Whether or not the value of the property is considered sensitive (e.g., passwords and keys)")
public boolean getSensitive() {
return sensitive;
}
@@ -107,7 +105,7 @@ public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
- @ApiModelProperty("The scope of expression language supported by this property")
+ @Schema(description = "The scope of expression language supported by this property")
public ExpressionLanguageScope getExpressionLanguageScope() {
return expressionLanguageScope;
}
@@ -117,12 +115,12 @@ public void setExpressionLanguageScope(ExpressionLanguageScope expressionLanguag
this.expressionLanguageScopeDescription = expressionLanguageScope == null ? null : expressionLanguageScope.getDescription();
}
- @ApiModelProperty(value = "The description of the expression language scope supported by this property", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+ @Schema(description = "The description of the expression language scope supported by this property", accessMode = Schema.AccessMode.READ_ONLY)
public String getExpressionLanguageScopeDescription() {
return expressionLanguageScope == null ? null : expressionLanguageScope.getDescription();
}
- @ApiModelProperty("Indicates that this property is for selecting a controller service of the specified type")
+ @Schema(description = "Indicates that this property is for selecting a controller service of the specified type")
public DefinedType getTypeProvidedByValue() {
return typeProvidedByValue;
}
@@ -131,7 +129,7 @@ public void setTypeProvidedByValue(DefinedType typeProvidedByValue) {
this.typeProvidedByValue = typeProvidedByValue;
}
- @ApiModelProperty("A regular expression that can be used to validate the value of this property")
+ @Schema(description = "A regular expression that can be used to validate the value of this property")
public String getValidRegex() {
return validRegex;
}
@@ -140,7 +138,7 @@ public void setValidRegex(String validRegex) {
this.validRegex = validRegex;
}
- @ApiModelProperty("Name of the validator used for this property descriptor")
+ @Schema(description = "Name of the validator used for this property descriptor")
public String getValidator() {
return validator;
}
@@ -149,7 +147,7 @@ public void setValidator(final String validator) {
this.validator = validator;
}
- @ApiModelProperty("Whether or not the descriptor is for a dynamically added property")
+ @Schema(description = "Whether or not the descriptor is for a dynamically added property")
public boolean isDynamic() {
return dynamic;
}
@@ -158,7 +156,7 @@ public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
- @ApiModelProperty("Indicates that this property references external resources")
+ @Schema(description = "Indicates that this property references external resources")
public PropertyResourceDefinition getResourceDefinition() {
return resourceDefinition;
}
@@ -167,7 +165,7 @@ public void setResourceDefinition(PropertyResourceDefinition resourceDefinition)
this.resourceDefinition = resourceDefinition;
}
- @ApiModelProperty("The dependencies that this property has on other properties")
+ @Schema(description = "The dependencies that this property has on other properties")
public List getDependencies() {
return dependencies;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyResourceDefinition.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyResourceDefinition.java
index ebc6d2bdc96e2..0e8d26fef5d34 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyResourceDefinition.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/PropertyResourceDefinition.java
@@ -16,22 +16,20 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
import java.io.Serializable;
import java.util.Set;
-@ApiModel
public class PropertyResourceDefinition implements Serializable {
private static final long serialVersionUID = 1L;
private ResourceCardinality cardinality;
private Set resourceTypes;
- @ApiModelProperty("The cardinality of the resource definition (i.e. single or multiple)")
+ @Schema(description = "The cardinality of the resource definition (i.e. single or multiple)")
public ResourceCardinality getCardinality() {
return cardinality;
}
@@ -40,7 +38,7 @@ public void setCardinality(ResourceCardinality cardinality) {
this.cardinality = cardinality;
}
- @ApiModelProperty("The types of resources that can be referenced")
+ @Schema(description = "The types of resources that can be referenced")
public Set getResourceTypes() {
return resourceTypes;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Relationship.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Relationship.java
index 84135a896db9f..e7066b9b01726 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Relationship.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Relationship.java
@@ -17,18 +17,17 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.io.Serializable;
-@ApiModel
public class Relationship implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String description;
- @ApiModelProperty("The name of the relationship")
+ @Schema(description = "The name of the relationship")
public String getName() {
return name;
}
@@ -37,7 +36,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The description of the relationship")
+ @Schema(description = "The description of the relationship")
public String getDescription() {
return description;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ReportingTaskDefinition.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ReportingTaskDefinition.java
index aa51e36c76594..19bbf25d96dbb 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ReportingTaskDefinition.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/ReportingTaskDefinition.java
@@ -17,14 +17,12 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-@ApiModel
public class ReportingTaskDefinition extends ConfigurableExtensionDefinition {
private static final long serialVersionUID = 1L;
@@ -32,7 +30,7 @@ public class ReportingTaskDefinition extends ConfigurableExtensionDefinition {
private String defaultSchedulingStrategy;
private Map defaultSchedulingPeriodBySchedulingStrategy;
- @ApiModelProperty("The supported scheduling strategies, such as TIME_DRIVER or CRON.")
+ @Schema(description = "The supported scheduling strategies, such as TIME_DRIVER or CRON.")
public List getSupportedSchedulingStrategies() {
return (supportedSchedulingStrategies != null ? Collections.unmodifiableList(supportedSchedulingStrategies) : null);
}
@@ -41,7 +39,7 @@ public void setSupportedSchedulingStrategies(List supportedSchedulingStr
this.supportedSchedulingStrategies = supportedSchedulingStrategies;
}
- @ApiModelProperty("The default scheduling strategy for the reporting task.")
+ @Schema(description = "The default scheduling strategy for the reporting task.")
public String getDefaultSchedulingStrategy() {
return defaultSchedulingStrategy;
}
@@ -50,7 +48,7 @@ public void setDefaultSchedulingStrategy(String defaultSchedulingStrategy) {
this.defaultSchedulingStrategy = defaultSchedulingStrategy;
}
- @ApiModelProperty("The default scheduling period for each scheduling strategy. " +
+ @Schema(description = "The default scheduling period for each scheduling strategy. " +
"The scheduling period is expected to be a time period, such as \"30 sec\".")
public Map getDefaultSchedulingPeriodBySchedulingStrategy() {
return defaultSchedulingPeriodBySchedulingStrategy != null ? Collections.unmodifiableMap(defaultSchedulingPeriodBySchedulingStrategy) : null;
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Restriction.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Restriction.java
index 9b0f24dbab75e..b44eee01cdf6c 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Restriction.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Restriction.java
@@ -16,18 +16,16 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
-@ApiModel
public class Restriction {
private String requiredPermission;
private String explanation;
- @ApiModelProperty(value = "The permission required for this restriction")
+ @Schema(description = "The permission required for this restriction")
public String getRequiredPermission() {
return requiredPermission;
}
@@ -36,7 +34,7 @@ public void setRequiredPermission(String requiredPermission) {
this.requiredPermission = requiredPermission;
}
- @ApiModelProperty(value = "The explanation of this restriction")
+ @Schema(description = "The explanation of this restriction")
public String getExplanation() {
return explanation;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/RuntimeManifest.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/RuntimeManifest.java
index 8b12fa9009d34..f6b6f28f6c647 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/RuntimeManifest.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/RuntimeManifest.java
@@ -17,15 +17,13 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-@ApiModel
public class RuntimeManifest implements Serializable {
private static final long serialVersionUID = 1L;
@@ -36,7 +34,7 @@ public class RuntimeManifest implements Serializable {
private List bundles;
private SchedulingDefaults schedulingDefaults;
- @ApiModelProperty("A unique identifier for the manifest")
+ @Schema(description = "A unique identifier for the manifest")
public String getIdentifier() {
return identifier;
}
@@ -45,9 +43,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty(
- value = "The type of the runtime binary, e.g., 'minifi-java' or 'minifi-cpp'",
- notes = "Usually set when the runtime is built.")
+ @Schema(description = "The type of the runtime binary, e.g., 'minifi-java' or 'minifi-cpp'")
public String getAgentType() {
return agentType;
}
@@ -56,9 +52,7 @@ public void setAgentType(String agentType) {
this.agentType = agentType;
}
- @ApiModelProperty(
- value = "The version of the runtime binary, e.g., '1.0.1'",
- notes = "Usually set when the runtime is built.")
+ @Schema(description = "The version of the runtime binary, e.g., '1.0.1'")
public String getVersion() {
return version;
}
@@ -67,7 +61,7 @@ public void setVersion(String version) {
this.version = version;
}
- @ApiModelProperty("Build summary for this runtime binary")
+ @Schema(description = "Build summary for this runtime binary")
public BuildInfo getBuildInfo() {
return buildInfo;
}
@@ -76,7 +70,7 @@ public void setBuildInfo(BuildInfo buildInfo) {
this.buildInfo = buildInfo;
}
- @ApiModelProperty("All extension bundles included with this runtime")
+ @Schema(description = "All extension bundles included with this runtime")
public List getBundles() {
return (bundles != null ? Collections.unmodifiableList(bundles) : null);
}
@@ -85,7 +79,7 @@ public void setBundles(List bundles) {
this.bundles = bundles;
}
- @ApiModelProperty("Scheduling defaults for components defined in this manifest")
+ @Schema(description = "Scheduling defaults for components defined in this manifest")
public SchedulingDefaults getSchedulingDefaults() {
return schedulingDefaults;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SchedulingDefaults.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SchedulingDefaults.java
index 9a9d6d999ee8d..c2875d6e01920 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SchedulingDefaults.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SchedulingDefaults.java
@@ -17,15 +17,13 @@
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.scheduling.SchedulingStrategy;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
-@ApiModel
public class SchedulingDefaults implements Serializable {
private static final long serialVersionUID = 1L;
@@ -39,7 +37,7 @@ public class SchedulingDefaults implements Serializable {
private Map defaultConcurrentTasksBySchedulingStrategy;
private Map defaultSchedulingPeriodsBySchedulingStrategy;
- @ApiModelProperty("The name of the default scheduling strategy")
+ @Schema(description = "The name of the default scheduling strategy")
public SchedulingStrategy getDefaultSchedulingStrategy() {
return defaultSchedulingStrategy;
}
@@ -48,7 +46,7 @@ public void setDefaultSchedulingStrategy(SchedulingStrategy defaultSchedulingStr
this.defaultSchedulingStrategy = defaultSchedulingStrategy;
}
- @ApiModelProperty("The default scheduling period in milliseconds")
+ @Schema(description = "The default scheduling period in milliseconds")
public long getDefaultSchedulingPeriodMillis() {
return defaultSchedulingPeriodMillis;
}
@@ -57,7 +55,7 @@ public void setDefaultSchedulingPeriodMillis(long defaultSchedulingPeriodMillis)
this.defaultSchedulingPeriodMillis = defaultSchedulingPeriodMillis;
}
- @ApiModelProperty("The default penalization period in milliseconds")
+ @Schema(description = "The default penalization period in milliseconds")
public long getPenalizationPeriodMillis() {
return penalizationPeriodMillis;
}
@@ -66,7 +64,7 @@ public void setPenalizationPeriodMillis(long penalizationPeriodMillis) {
this.penalizationPeriodMillis = penalizationPeriodMillis;
}
- @ApiModelProperty("The default yield duration in milliseconds")
+ @Schema(description = "The default yield duration in milliseconds")
public long getYieldDurationMillis() {
return yieldDurationMillis;
}
@@ -75,7 +73,7 @@ public void setYieldDurationMillis(long yieldDurationMillis) {
this.yieldDurationMillis = yieldDurationMillis;
}
- @ApiModelProperty("The default run duration in nano-seconds")
+ @Schema(description = "The default run duration in nano-seconds")
public long getDefaultRunDurationNanos() {
return defaultRunDurationNanos;
}
@@ -84,7 +82,7 @@ public void setDefaultRunDurationNanos(long defaultRunDurationNanos) {
this.defaultRunDurationNanos = defaultRunDurationNanos;
}
- @ApiModelProperty("The default concurrent tasks")
+ @Schema(description = "The default concurrent tasks")
public String getDefaultMaxConcurrentTasks() {
return defaultMaxConcurrentTasks;
}
@@ -93,7 +91,7 @@ public void setDefaultMaxConcurrentTasks(String defaultMaxConcurrentTasks) {
this.defaultMaxConcurrentTasks = defaultMaxConcurrentTasks;
}
- @ApiModelProperty("The default concurrent tasks for each scheduling strategy")
+ @Schema(description = "The default concurrent tasks for each scheduling strategy")
public Map getDefaultConcurrentTasksBySchedulingStrategy() {
return defaultConcurrentTasksBySchedulingStrategy != null ? Collections.unmodifiableMap(defaultConcurrentTasksBySchedulingStrategy) : null;
}
@@ -102,7 +100,7 @@ public void setDefaultConcurrentTasksBySchedulingStrategy(Map d
this.defaultConcurrentTasksBySchedulingStrategy = defaultConcurrentTasksBySchedulingStrategy;
}
- @ApiModelProperty("The default scheduling period for each scheduling strategy")
+ @Schema(description = "The default scheduling period for each scheduling strategy")
public Map getDefaultSchedulingPeriodsBySchedulingStrategy() {
return defaultSchedulingPeriodsBySchedulingStrategy != null ? Collections.unmodifiableMap(defaultSchedulingPeriodsBySchedulingStrategy) : null;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Stateful.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Stateful.java
index b7c6a211984d5..742307272e4b4 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Stateful.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/Stateful.java
@@ -16,19 +16,17 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.components.state.Scope;
import java.util.Set;
-@ApiModel
public class Stateful {
private String description;
private Set scopes;
- @ApiModelProperty(value = "Description of what information is being stored in the StateManager")
+ @Schema(description = "Description of what information is being stored in the StateManager")
public String getDescription() {
return description;
}
@@ -37,7 +35,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "Indicates the Scope(s) associated with the State that is stored and retrieved")
+ @Schema(description = "Indicates the Scope(s) associated with the State that is stored and retrieved")
public Set getScopes() {
return scopes;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SystemResourceConsideration.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SystemResourceConsideration.java
index 3c008f1f72b82..8c3f45b91161d 100644
--- a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SystemResourceConsideration.java
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/SystemResourceConsideration.java
@@ -16,16 +16,14 @@
*/
package org.apache.nifi.c2.protocol.component.api;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
public class SystemResourceConsideration {
private String resource;
private String description;
- @ApiModelProperty(value = "The resource to consider")
+ @Schema(description = "The resource to consider")
public String getResource() {
return resource;
}
@@ -34,7 +32,7 @@ public void setResource(String resource) {
this.resource = resource;
}
- @ApiModelProperty(value = "The description of how the resource is affected")
+ @Schema(description = "The description of how the resource is affected")
public String getDescription() {
return description;
}
diff --git a/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/UseCase.java b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/UseCase.java
new file mode 100644
index 0000000000000..6f3c7eba7453c
--- /dev/null
+++ b/c2/c2-protocol/c2-protocol-component-api/src/main/java/org/apache/nifi/c2/protocol/component/api/UseCase.java
@@ -0,0 +1,77 @@
+/*
+ * 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 org.apache.nifi.annotation.behavior.InputRequirement;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class UseCase implements Serializable {
+ private String description;
+ private String notes;
+ private List keywords;
+ private String configuration;
+ private InputRequirement.Requirement inputRequirement;
+
+ @Schema(description="A description of the use case")
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String description) {
+ this.description = description;
+ }
+
+ @Schema(description="Any pertinent notes about the use case")
+ public String getNotes() {
+ return notes;
+ }
+
+ public void setNotes(final String notes) {
+ this.notes = notes;
+ }
+
+ @Schema(description="Keywords that pertain to the use case")
+ public List getKeywords() {
+ return keywords;
+ }
+
+ public void setKeywords(final List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Schema(description="A description of how to configure the Processor to perform the task described in the use case")
+ public String getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(final String configuration) {
+ this.configuration = configuration;
+ }
+
+ @Schema(description="Specifies whether an incoming FlowFile is expected for this use case")
+ public InputRequirement.Requirement getInputRequirement() {
+ return inputRequirement;
+ }
+
+ public void setInputRequirement(final InputRequirement.Requirement inputRequirement) {
+ this.inputRequirement = inputRequirement;
+ }
+}
diff --git a/minifi/minifi-bootstrap/pom.xml b/minifi/minifi-bootstrap/pom.xml
index bbab3d59aa7c4..8f96c8f6be909 100644
--- a/minifi/minifi-bootstrap/pom.xml
+++ b/minifi/minifi-bootstrap/pom.xml
@@ -86,6 +86,13 @@ limitations under the License.
minifi-commons-api2.0.0-SNAPSHOT
+
+
+ org.apache.nifi.minifi
+ minifi-properties-loader
+ 2.0.0-SNAPSHOT
+ org.apache.nifi.minifiminifi-commons-framework
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/RunMiNiFi.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/RunMiNiFi.java
index cbd23dcfac61b..4d00feee1a307 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/RunMiNiFi.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/RunMiNiFi.java
@@ -48,7 +48,7 @@
import org.apache.nifi.minifi.commons.api.MiNiFiCommandState;
import org.apache.nifi.minifi.commons.service.StandardFlowEnrichService;
import org.apache.nifi.minifi.commons.service.StandardFlowSerDeService;
-import org.apache.nifi.properties.ApplicationProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -103,7 +103,7 @@ public RunMiNiFi(File bootstrapConfigFile) throws IOException {
bootstrapFileProvider = new BootstrapFileProvider(bootstrapConfigFile);
objectMapper = getObjectMapper();
Properties properties = bootstrapFileProvider.getStatusProperties();
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
miNiFiParameters = new MiNiFiParameters(
Optional.ofNullable(properties.getProperty(STATUS_FILE_PORT_KEY)).map(Integer::parseInt).orElse(UNINITIALIZED),
@@ -118,7 +118,7 @@ public RunMiNiFi(File bootstrapConfigFile) throws IOException {
periodicStatusReporterManager =
new PeriodicStatusReporterManager(bootstrapProperties, miNiFiStatusProvider, miNiFiCommandSender, miNiFiParameters);
MiNiFiConfigurationChangeListener configurationChangeListener = new MiNiFiConfigurationChangeListener(this, DEFAULT_LOGGER, bootstrapFileProvider,
- new StandardFlowEnrichService(new ApplicationProperties(bootstrapProperties)), StandardFlowSerDeService.defaultInstance());
+ new StandardFlowEnrichService(bootstrapProperties), StandardFlowSerDeService.defaultInstance());
configurationChangeCoordinator = new ConfigurationChangeCoordinator(bootstrapFileProvider, this, singleton(configurationChangeListener));
currentPortProvider = new CurrentPortProvider(miNiFiCommandSender, miNiFiParameters, processUtils);
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/command/StartRunner.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/command/StartRunner.java
index 827f6ef171867..af331fcc2be90 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/command/StartRunner.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/command/StartRunner.java
@@ -55,6 +55,7 @@
import org.apache.nifi.minifi.bootstrap.service.MiNiFiStdLogHandler;
import org.apache.nifi.minifi.bootstrap.service.PeriodicStatusReporterManager;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
public class StartRunner implements CommandRunner {
@@ -122,10 +123,10 @@ private void start() throws IOException, InterruptedException, ConfigurationChan
CMD_LOGGER.warn("Failed to delete previous lock file {}; this file should be cleaned up manually", prevLockFile);
}
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
String confDir = bootstrapProperties.getProperty(CONF_DIR_KEY);
- generateMiNiFiProperties(bootstrapProperties, confDir);
+ generateMiNiFiProperties(bootstrapFileProvider.getProtectedBootstrapProperties(), confDir);
regenerateFlowConfiguration(bootstrapProperties.getProperty(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey()));
Process process = startMiNiFi();
@@ -259,7 +260,7 @@ private void deleteSwapFile(File file) {
}
}
- private void generateMiNiFiProperties(Properties bootstrapProperties, String confDir) {
+ private void generateMiNiFiProperties(BootstrapProperties bootstrapProperties, String confDir) {
DEFAULT_LOGGER.debug("Generating minifi.properties from bootstrap.conf");
try {
miNiFiPropertiesGenerator.generateMinifiProperties(confDir, bootstrapProperties);
@@ -317,7 +318,7 @@ private Process startMiNiFiProcess(final ProcessBuilder builder) throws IOExcept
}
private File getWorkingDir() throws IOException {
- Properties props = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties props = bootstrapFileProvider.getBootstrapProperties();
File bootstrapConfigAbsoluteFile = bootstrapConfigFile.getAbsoluteFile();
File binDir = bootstrapConfigAbsoluteFile.getParentFile();
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ConfigurationChangeCoordinator.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ConfigurationChangeCoordinator.java
index b6a11d2564351..fb187ae85744c 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ConfigurationChangeCoordinator.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ConfigurationChangeCoordinator.java
@@ -17,26 +17,26 @@
package org.apache.nifi.minifi.bootstrap.configuration;
+import static java.util.Optional.ofNullable;
+import static java.util.function.Predicate.not;
+import static java.util.stream.Collectors.toList;
+
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;
import org.apache.nifi.minifi.bootstrap.RunMiNiFi;
import org.apache.nifi.minifi.bootstrap.configuration.ingestors.interfaces.ChangeIngestor;
import org.apache.nifi.minifi.bootstrap.service.BootstrapFileProvider;
import org.apache.nifi.minifi.bootstrap.util.ByteBufferInputStream;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static java.util.Optional.ofNullable;
-import static java.util.function.Predicate.not;
-import static java.util.stream.Collectors.toList;
-
public class ConfigurationChangeCoordinator implements Closeable, ConfigurationChangeNotifier {
public static final String NOTIFIER_INGESTORS_KEY = "nifi.minifi.notifier.ingestors";
@@ -94,7 +94,7 @@ private ListenerHandleResult notifyListener(ByteBuffer newFlowConfig, Configurat
private void initialize() throws IOException {
closeIngestors();
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
ofNullable(bootstrapProperties.getProperty(NOTIFIER_INGESTORS_KEY))
.filter(not(String::isBlank))
.map(ingestors -> ingestors.split(COMMA))
@@ -115,7 +115,7 @@ private void closeIngestors() {
}
}
- private void instantiateIngestor(Properties bootstrapProperties, String ingestorClassname) {
+ private void instantiateIngestor(BootstrapProperties bootstrapProperties, String ingestorClassname) {
try {
Class> ingestorClass = Class.forName(ingestorClassname);
ChangeIngestor changeIngestor = (ChangeIngestor) ingestorClass.getDeclaredConstructor().newInstance();
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/AbstractPullChangeIngestor.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/AbstractPullChangeIngestor.java
index 8d3b225df7e85..14c2405a59bb6 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/AbstractPullChangeIngestor.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/AbstractPullChangeIngestor.java
@@ -18,7 +18,6 @@
package org.apache.nifi.minifi.bootstrap.configuration.ingestors;
import java.io.IOException;
-import java.util.Properties;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -26,20 +25,21 @@
import org.apache.nifi.minifi.bootstrap.ConfigurationFileHolder;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
import org.apache.nifi.minifi.bootstrap.configuration.ingestors.interfaces.ChangeIngestor;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
public abstract class AbstractPullChangeIngestor implements Runnable, ChangeIngestor {
protected static final String DEFAULT_POLLING_PERIOD_MILLISECONDS = "300000";
protected final AtomicInteger pollingPeriodMS = new AtomicInteger();
- protected final AtomicReference properties = new AtomicReference<>();
+ protected final AtomicReference properties = new AtomicReference<>();
private final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
protected volatile ConfigurationChangeNotifier configurationChangeNotifier;
@Override
- public void initialize(Properties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
+ public void initialize(BootstrapProperties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
this.configurationChangeNotifier = configurationChangeNotifier;
this.properties.set(properties);
}
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestor.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestor.java
index 95a49a0d9935d..a77b41f0044fb 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestor.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestor.java
@@ -39,7 +39,6 @@
import java.nio.file.WatchService;
import java.util.Map;
import java.util.Optional;
-import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -50,6 +49,7 @@
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.WholeConfigDifferentiator;
import org.apache.nifi.minifi.bootstrap.configuration.ingestors.interfaces.ChangeIngestor;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -84,7 +84,7 @@ public class FileChangeIngestor implements Runnable, ChangeIngestor {
private long pollingSeconds;
@Override
- public void initialize(Properties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
+ public void initialize(BootstrapProperties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
Path configFile = ofNullable(properties.getProperty(CONFIG_FILE_PATH_KEY))
.filter(not(String::isBlank))
.map(Path::of)
@@ -190,7 +190,7 @@ private Supplier unableToFindDifferentiatorExceptionSu
+ " which does not correspond to any in the FileChangeIngestor Map:" + DIFFERENTIATOR_CONSTRUCTOR_MAP.keySet());
}
- private void checkConfigFileLocationCorrectness(Properties properties, Path configFile) {
+ private void checkConfigFileLocationCorrectness(BootstrapProperties properties, Path configFile) {
Path flowConfigFile = Path.of(properties.getProperty(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey())).toAbsolutePath();
Path rawFlowConfigFile = flowConfigFile.getParent().resolve(getBaseName(flowConfigFile.toString()) + RAW_EXTENSION);
if (flowConfigFile.equals(configFile) || rawFlowConfigFile.equals(configFile)) {
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
index 0b1238f1f44df..21d666f02fa52 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/PullHttpChangeIngestor.java
@@ -41,7 +41,6 @@
import java.security.KeyStore;
import java.util.Arrays;
import java.util.Map;
-import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -59,6 +58,7 @@
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.Differentiator;
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.WholeConfigDifferentiator;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
@@ -91,18 +91,18 @@ public class PullHttpChangeIngestor extends AbstractPullChangeIngestor {
public static final String OVERRIDE_SECURITY = PULL_HTTP_BASE_KEY + ".override.security";
public static final String HTTP_HEADERS = PULL_HTTP_BASE_KEY + ".headers";
+ protected static final String DEFAULT_CONNECT_TIMEOUT_MS = "5000";
+ protected static final String DEFAULT_READ_TIMEOUT_MS = "15000";
+ protected static final String DEFAULT_PATH = "/";
private static final Logger logger = LoggerFactory.getLogger(PullHttpChangeIngestor.class);
private static final Map>> DIFFERENTIATOR_CONSTRUCTOR_MAP = Map.of(
WHOLE_CONFIG_KEY, WholeConfigDifferentiator::getByteBufferDifferentiator
);
private static final int NOT_MODIFIED_STATUS_CODE = 304;
- private static final String DEFAULT_CONNECT_TIMEOUT_MS = "5000";
- private static final String DEFAULT_READ_TIMEOUT_MS = "15000";
private static final String DOUBLE_QUOTES = "\"";
private static final String ETAG_HEADER = "ETag";
private static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
- private static final String DEFAULT_PATH = "/";
private static final int BAD_REQUEST_STATUS_CODE = 400;
private static final String IF_NONE_MATCH_HEADER_KEY = "If-None-Match";
private static final String HTTP_HEADERS_SEPARATOR = ",";
@@ -121,7 +121,7 @@ public class PullHttpChangeIngestor extends AbstractPullChangeIngestor {
private volatile boolean useEtag = false;
@Override
- public void initialize(Properties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
+ public void initialize(BootstrapProperties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
super.initialize(properties, configurationFileHolder, configurationChangeNotifier);
pollingPeriodMS.set(Integer.parseInt(properties.getProperty(PULL_HTTP_POLLING_PERIOD_KEY, DEFAULT_POLLING_PERIOD_MILLISECONDS)));
@@ -145,8 +145,7 @@ public void initialize(Properties properties, ConfigurationFileHolder configurat
.collect(toMap(split -> ofNullable(split[0]).map(String::trim).orElse(EMPTY), split -> ofNullable(split[1]).map(String::trim).orElse(EMPTY)));
logger.debug("Configured HTTP headers: {}", httpHeaders);
- ofNullable(properties.get(PORT_KEY))
- .map(String.class::cast)
+ ofNullable(properties.getProperty(PORT_KEY))
.map(Integer::parseInt)
.ifPresentOrElse(
portReference::set,
@@ -157,7 +156,7 @@ public void initialize(Properties properties, ConfigurationFileHolder configurat
pathReference.set(path);
queryReference.set(query);
httpHeadersReference.set(httpHeaders);
- useEtag = parseBoolean((String) properties.getOrDefault(USE_ETAG_KEY, FALSE.toString()));
+ useEtag = parseBoolean(properties.getProperty(USE_ETAG_KEY, FALSE.toString()));
httpClientReference.set(null);
@@ -263,7 +262,7 @@ public void run() {
}
}
- private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, Properties properties) {
+ private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, BootstrapProperties properties) {
String keystorePass = properties.getProperty(KEYSTORE_PASSWORD_KEY);
KeyStore keyStore = buildKeyStore(properties, KEYSTORE_LOCATION_KEY, KEYSTORE_PASSWORD_KEY, KEYSTORE_TYPE_KEY);
KeyStore truststore = buildKeyStore(properties, TRUSTSTORE_LOCATION_KEY, TRUSTSTORE_PASSWORD_KEY, TRUSTSTORE_TYPE_KEY);
@@ -279,7 +278,7 @@ private void setSslSocketFactory(OkHttpClient.Builder okHttpClientBuilder, Prope
okHttpClientBuilder.sslSocketFactory(sslSocketFactory, trustManager);
}
- private KeyStore buildKeyStore(Properties properties, String locationKey, String passKey, String typeKey) {
+ private KeyStore buildKeyStore(BootstrapProperties properties, String locationKey, String passKey, String typeKey) {
String keystoreLocation = ofNullable(properties.getProperty(locationKey))
.filter(StringUtils::isNotBlank)
.orElseThrow(() -> new IllegalArgumentException(locationKey + " is null or empty"));
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestor.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestor.java
index 1b9c39012555a..d8c35984cf940 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestor.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestor.java
@@ -32,7 +32,6 @@
import java.nio.ByteBuffer;
import java.security.KeyStore;
import java.util.Map;
-import java.util.Properties;
import java.util.function.Supplier;
import javax.net.ssl.SSLContext;
import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
@@ -42,6 +41,7 @@
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.Differentiator;
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.WholeConfigDifferentiator;
import org.apache.nifi.minifi.bootstrap.configuration.ingestors.interfaces.ChangeIngestor;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.security.util.KeystoreType;
@@ -100,7 +100,7 @@ public RestChangeIngestor() {
}
@Override
- public void initialize(Properties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
+ public void initialize(BootstrapProperties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier) {
logger.info("Initializing RestChangeIngestor");
this.differentiator = ofNullable(properties.getProperty(DIFFERENTIATOR_KEY))
.filter(not(String::isBlank))
@@ -154,7 +154,7 @@ public int getPort() {
return ((ServerConnector) jetty.getConnectors()[0]).getLocalPort();
}
- private void createConnector(Properties properties) {
+ private void createConnector(BootstrapProperties properties) {
ServerConnector http = new ServerConnector(jetty);
http.setPort(Integer.parseInt(properties.getProperty(PORT_KEY, "0")));
@@ -167,7 +167,7 @@ private void createConnector(Properties properties) {
logger.info("Added an http connector on the host '{}' and port '{}'", http.getHost(), http.getPort());
}
- private void createSecureConnector(Properties properties) {
+ private void createSecureConnector(BootstrapProperties properties) {
KeyStore keyStore;
KeyStore trustStore = null;
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/interfaces/ChangeIngestor.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/interfaces/ChangeIngestor.java
index dcd39cd6d11d7..96e5c4e50c581 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/interfaces/ChangeIngestor.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/interfaces/ChangeIngestor.java
@@ -17,14 +17,13 @@
package org.apache.nifi.minifi.bootstrap.configuration.ingestors.interfaces;
+import java.io.IOException;
import org.apache.nifi.minifi.bootstrap.ConfigurationFileHolder;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
-
-import java.io.IOException;
-import java.util.Properties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
public interface ChangeIngestor {
- void initialize(Properties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier);
+ void initialize(BootstrapProperties properties, ConfigurationFileHolder configurationFileHolder, ConfigurationChangeNotifier configurationChangeNotifier);
void start();
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/BootstrapFileProvider.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/BootstrapFileProvider.java
index 1957b05aec9b6..96cc04ad29061 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/BootstrapFileProvider.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/BootstrapFileProvider.java
@@ -22,7 +22,6 @@
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@@ -35,6 +34,8 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
+import org.apache.nifi.minifi.properties.BootstrapPropertiesLoader;
import org.apache.nifi.util.file.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,6 +62,10 @@ public BootstrapFileProvider(File bootstrapConfigFile) {
this.bootstrapConfigFile = bootstrapConfigFile;
}
+ public String getBootstrapFilePath() {
+ return bootstrapConfigFile.getAbsolutePath();
+ }
+
public static File getBootstrapConfFile() {
File bootstrapConfigFile = Optional.ofNullable(System.getProperty(BOOTSTRAP_CONFIG_FILE_SYSTEM_PROPERTY_KEY))
.map(File::new)
@@ -110,19 +115,12 @@ public File getBootstrapConfNewFile() {
return newFile;
}
- public Properties getBootstrapProperties() throws IOException {
- if (!bootstrapConfigFile.exists()) {
- throw new FileNotFoundException(bootstrapConfigFile.getAbsolutePath());
- }
-
- Properties bootstrapProperties = BootstrapProperties.getInstance();
- try (FileInputStream fis = new FileInputStream(bootstrapConfigFile)) {
- bootstrapProperties.load(fis);
- }
-
- logProperties("Bootstrap", bootstrapProperties);
+ public BootstrapProperties getBootstrapProperties() {
+ return BootstrapPropertiesLoader.load(bootstrapConfigFile);
+ }
- return bootstrapProperties;
+ public BootstrapProperties getProtectedBootstrapProperties() {
+ return BootstrapPropertiesLoader.loadProtectedProperties(bootstrapConfigFile).getApplicationProperties();
}
public Properties getStatusProperties() {
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProvider.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProvider.java
index b75c0157e1bec..ebe6f36b31b75 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProvider.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProvider.java
@@ -18,7 +18,7 @@
package org.apache.nifi.minifi.bootstrap.service;
import java.io.IOException;
-import java.util.Properties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,7 +36,7 @@ public GracefulShutdownParameterProvider(BootstrapFileProvider bootstrapFileProv
}
public int getGracefulShutdownSeconds() throws IOException {
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
String gracefulShutdown = bootstrapProperties.getProperty(GRACEFUL_SHUTDOWN_PROP, DEFAULT_GRACEFUL_SHUTDOWN_VALUE);
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiConfigurationChangeListener.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiConfigurationChangeListener.java
index ea0f04dd84758..5925ac3b5e298 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiConfigurationChangeListener.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiConfigurationChangeListener.java
@@ -32,7 +32,6 @@
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
-import java.util.Properties;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.io.FilenameUtils;
import org.apache.nifi.controller.flow.VersionedDataflow;
@@ -42,6 +41,7 @@
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
import org.apache.nifi.minifi.commons.service.FlowEnrichService;
import org.apache.nifi.minifi.commons.service.FlowSerDeService;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
public class MiNiFiConfigurationChangeListener implements ConfigurationChangeListener {
@@ -76,7 +76,7 @@ public void handleChange(InputStream flowConfigInputStream) throws Configuration
Path currentRawFlowConfigFile = null;
Path backupRawFlowConfigFile = null;
try {
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
currentFlowConfigFile = Path.of(bootstrapProperties.getProperty(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey())).toAbsolutePath();
backupFlowConfigFile = Path.of(currentFlowConfigFile + BACKUP_EXTENSION);
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiExecCommandProvider.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiExecCommandProvider.java
index 8f2b0fce08bde..0e99379a6df8d 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiExecCommandProvider.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiExecCommandProvider.java
@@ -28,12 +28,13 @@
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
-import java.util.Properties;
import java.util.stream.Stream;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
public class MiNiFiExecCommandProvider {
public static final String LOG_DIR = "org.apache.nifi.minifi.bootstrap.config.log.dir";
+ public static final String MINIFI_BOOTSTRAP_CONF_FILE_PATH = "minifi.bootstrap.conf.file.path";
public static final String DEFAULT_LOG_DIR = "./logs";
public static final String APP_LOG_FILE_NAME = "org.apache.nifi.minifi.bootstrap.config.log.app.file.name";
@@ -73,7 +74,7 @@ public MiNiFiExecCommandProvider(BootstrapFileProvider bootstrapFileProvider) {
this.bootstrapFileProvider = bootstrapFileProvider;
}
- public static String getMiNiFiPropertiesPath(Properties props, File confDir) {
+ public static String getMiNiFiPropertiesPath(BootstrapProperties props, File confDir) {
return ofNullable(props.getProperty(PROPERTIES_FILE_KEY))
.orElseGet(() -> ofNullable(confDir)
.filter(File::exists)
@@ -92,7 +93,7 @@ public static String getMiNiFiPropertiesPath(Properties props, File confDir) {
* @throws IOException throws IOException if any of the configuration file read fails
*/
public List getMiNiFiExecCommand(int listenPort, File workingDir) throws IOException {
- Properties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
+ BootstrapProperties bootstrapProperties = bootstrapFileProvider.getBootstrapProperties();
File confDir = getFile(bootstrapProperties.getProperty(CONF_DIR_KEY, DEFAULT_CONF_DIR).trim(), workingDir);
File libDir = getFile(bootstrapProperties.getProperty(LIB_DIR_KEY, DEFAULT_LIB_DIR).trim(), workingDir);
@@ -107,6 +108,7 @@ public List getMiNiFiExecCommand(int listenPort, File workingDir) throws
List javaAdditionalArgs = getJavaAdditionalArgs(bootstrapProperties);
List systemProperties = List.of(
systemProperty(PROPERTIES_FILE_PATH, getMiNiFiPropertiesPath(bootstrapProperties, confDir)),
+ systemProperty(MINIFI_BOOTSTRAP_CONF_FILE_PATH, bootstrapFileProvider.getBootstrapFilePath()),
systemProperty(NIFI_BOOTSTRAP_LISTEN_PORT, listenPort),
systemProperty(APP, MINIFI_CLASS_NAME),
systemProperty(LOG_DIR, minifiLogDir),
@@ -116,8 +118,7 @@ public List getMiNiFiExecCommand(int listenPort, File workingDir) throws
systemProperty(BOOTSTRAP_LOG_FILE_EXTENSION, minifiBootstrapLogFileExtension)
);
- return List.of(javaCommand, javaAdditionalArgs, systemProperties, List.of(MINIFI_FULLY_QUALIFIED_CLASS_NAME))
- .stream()
+ return Stream.of(javaCommand, javaAdditionalArgs, systemProperties, List.of(MINIFI_FULLY_QUALIFIED_CLASS_NAME))
.flatMap(List::stream)
.toList();
}
@@ -127,7 +128,7 @@ private File getFile(String filename, File workingDir) {
return file.isAbsolute() ? file : new File(workingDir, filename).getAbsoluteFile();
}
- private String getJavaCommand(Properties bootstrapProperties) {
+ private String getJavaCommand(BootstrapProperties bootstrapProperties) {
String javaCommand = bootstrapProperties.getProperty(JAVA_COMMAND_KEY, DEFAULT_JAVA_CMD);
return javaCommand.equals(DEFAULT_JAVA_CMD)
? ofNullable(System.getenv(JAVA_HOME_ENVIRONMENT_VARIABLE))
@@ -159,15 +160,16 @@ private String buildClassPath(File confDir, File libDir) {
.collect(joining(File.pathSeparator));
}
- private List getJavaAdditionalArgs(Properties props) {
- return props.entrySet()
+ private List getJavaAdditionalArgs(BootstrapProperties props) {
+ return props.getPropertyKeys()
.stream()
- .filter(entry -> ((String) entry.getKey()).startsWith(JAVA_ARG_KEY_PREFIX))
- .map(entry -> (String) entry.getValue())
+ .filter(key -> key.startsWith(JAVA_ARG_KEY_PREFIX))
+ .map(props::getProperty)
.toList();
}
private String systemProperty(String key, Object value) {
return String.format(SYSTEM_PROPERTY_TEMPLATE, key, value);
}
+
}
\ No newline at end of file
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGenerator.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGenerator.java
index 939ded36f756f..42c55c8bf6a09 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGenerator.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGenerator.java
@@ -19,6 +19,7 @@
import static java.lang.String.join;
import static java.lang.System.getProperty;
+import static java.util.Map.entry;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -59,6 +60,7 @@
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeException;
import org.apache.nifi.minifi.bootstrap.util.OrderedProperties;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.apache.nifi.util.NiFiProperties;
public class MiNiFiPropertiesGenerator {
@@ -139,15 +141,19 @@ public class MiNiFiPropertiesGenerator {
Triple.of(NiFiProperties.FLOW_CONFIGURATION_FILE, "./conf/flow.json.gz", EMPTY)
);
- static final Map MINIFI_TO_NIFI_PROPERTY_MAPPING = Map.of(
- MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey(), NiFiProperties.FLOW_CONFIGURATION_FILE,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE.getKey(), NiFiProperties.SECURITY_KEYSTORE,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_TYPE.getKey(), NiFiProperties.SECURITY_KEYSTORE_TYPE,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_PASSWD.getKey(), NiFiProperties.SECURITY_KEYSTORE_PASSWD,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_KEY_PASSWD.getKey(), NiFiProperties.SECURITY_KEY_PASSWD,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE.getKey(), NiFiProperties.SECURITY_TRUSTSTORE,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE_TYPE.getKey(), NiFiProperties.SECURITY_TRUSTSTORE_TYPE,
- MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE_PASSWD.getKey(), NiFiProperties.SECURITY_TRUSTSTORE_PASSWD
+ static final String PROTECTED_POSTFIX = ".protected";
+ static final Map MINIFI_TO_NIFI_PROPERTY_MAPPING = Map.ofEntries(
+ entry(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey(), NiFiProperties.FLOW_CONFIGURATION_FILE),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE.getKey(), NiFiProperties.SECURITY_KEYSTORE),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_TYPE.getKey(), NiFiProperties.SECURITY_KEYSTORE_TYPE),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_PASSWD.getKey(), NiFiProperties.SECURITY_KEYSTORE_PASSWD),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_PASSWD.getKey() + PROTECTED_POSTFIX, NiFiProperties.SECURITY_KEYSTORE_PASSWD + PROTECTED_POSTFIX),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEY_PASSWD.getKey(), NiFiProperties.SECURITY_KEY_PASSWD),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_KEY_PASSWD.getKey() + PROTECTED_POSTFIX, NiFiProperties.SECURITY_KEY_PASSWD + PROTECTED_POSTFIX),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE.getKey(), NiFiProperties.SECURITY_TRUSTSTORE),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE_TYPE.getKey(), NiFiProperties.SECURITY_TRUSTSTORE_TYPE),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE_PASSWD.getKey(), NiFiProperties.SECURITY_TRUSTSTORE_PASSWD),
+ entry(MiNiFiProperties.NIFI_MINIFI_SECURITY_TRUSTSTORE_PASSWD.getKey() + PROTECTED_POSTFIX, NiFiProperties.SECURITY_TRUSTSTORE_PASSWD + PROTECTED_POSTFIX)
);
static final String DEFAULT_SENSITIVE_PROPERTIES_ENCODING_ALGORITHM = "NIFI_PBKDF2_AES_GCM_256";
@@ -160,7 +166,7 @@ public class MiNiFiPropertiesGenerator {
public static final String FILE_EXTENSION_DELIMITER = ".";
- public void generateMinifiProperties(String configDirectory, Properties bootstrapProperties) throws ConfigurationChangeException {
+ public void generateMinifiProperties(String configDirectory, BootstrapProperties bootstrapProperties) throws ConfigurationChangeException {
String minifiPropertiesFileName = Path.of(getMiNiFiPropertiesPath(bootstrapProperties, new File(configDirectory))).getFileName().toString();
Path minifiPropertiesFile = Path.of(configDirectory, minifiPropertiesFileName);
@@ -188,22 +194,22 @@ private Map extractSensitivePropertiesConfiguration(Path minifiP
);
}
- private OrderedProperties prepareMinifiProperties(Properties bootstrapProperties, Map existingSensitivePropertiesConfiguration) {
+ private OrderedProperties prepareMinifiProperties(BootstrapProperties bootstrapProperties, Map existingSensitivePropertiesConfiguration) {
OrderedProperties minifiProperties = new OrderedProperties();
NIFI_PROPERTIES_WITH_DEFAULT_VALUES_AND_COMMENTS
.forEach(triple -> minifiProperties.setProperty(triple.getLeft(), triple.getMiddle(), triple.getRight()));
- getNonBlankPropertiesWithPredicate(bootstrapProperties, entry -> MINIFI_TO_NIFI_PROPERTY_MAPPING.containsKey(entry.getKey()))
+ getNonBlankPropertiesWithPredicate(bootstrapProperties, MINIFI_TO_NIFI_PROPERTY_MAPPING::containsKey)
.forEach(entry -> minifiProperties.setProperty(MINIFI_TO_NIFI_PROPERTY_MAPPING.get(entry.getKey()), entry.getValue()));
getSensitiveProperties(bootstrapProperties, existingSensitivePropertiesConfiguration)
.forEach(entry -> minifiProperties.setProperty(entry.getKey(), entry.getValue()));
- getNonBlankPropertiesWithPredicate(bootstrapProperties, entry -> ((String) entry.getKey()).startsWith(C2_PROPERTY_PREFIX))
+ getNonBlankPropertiesWithPredicate(bootstrapProperties, key -> key.startsWith(C2_PROPERTY_PREFIX))
.forEach(entry -> minifiProperties.setProperty(entry.getKey(), entry.getValue()));
- getNonBlankPropertiesWithPredicate(bootstrapProperties, entry -> ((String) entry.getKey()).startsWith(NIFI_PREFIX))
+ getNonBlankPropertiesWithPredicate(bootstrapProperties, key -> key.startsWith(NIFI_PREFIX))
.forEach(entry -> minifiProperties.setProperty(entry.getKey(), entry.getValue()));
bootstrapFileAndLogProperties()
@@ -212,19 +218,19 @@ private OrderedProperties prepareMinifiProperties(Properties bootstrapProperties
return minifiProperties;
}
- private List> getNonBlankPropertiesWithPredicate(Properties bootstrapProperties, Predicate predicate) {
+ private List> getNonBlankPropertiesWithPredicate(BootstrapProperties bootstrapProperties, Predicate predicate) {
return ofNullable(bootstrapProperties)
- .map(Properties::entrySet)
+ .map(BootstrapProperties::getPropertyKeys)
.orElseGet(Set::of)
.stream()
.filter(predicate)
- .filter(entry -> isNotBlank((String) entry.getValue()))
- .map(entry -> Pair.of((String) entry.getKey(), (String) entry.getValue()))
+ .map(key -> Pair.of(key, bootstrapProperties.getProperty(key)))
+ .filter(pair -> isNotBlank(pair.getValue()))
.sorted((o1, o2) -> Comparator.naturalOrder().compare(o1.getKey(), o2.getKey()))
.toList();
}
- private List> getSensitiveProperties(Properties bootstrapProperties, Map existingSensitivePropertiesConfiguration) {
+ private List> getSensitiveProperties(BootstrapProperties bootstrapProperties, Map existingSensitivePropertiesConfiguration) {
return existingSensitivePropertiesConfiguration.isEmpty()
? List.of(
Pair.of(NiFiProperties.SENSITIVE_PROPS_KEY,
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/PeriodicStatusReporterManager.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/PeriodicStatusReporterManager.java
index 6b9833774b5df..1db737e76b3d9 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/PeriodicStatusReporterManager.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/PeriodicStatusReporterManager.java
@@ -17,35 +17,35 @@
package org.apache.nifi.minifi.bootstrap.service;
+import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.NIFI_MINIFI_STATUS_REPORTER_COMPONENTS;
+
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Properties;
import java.util.Set;
import org.apache.nifi.minifi.bootstrap.MiNiFiParameters;
import org.apache.nifi.minifi.bootstrap.MiNiFiStatus;
import org.apache.nifi.minifi.bootstrap.QueryableStatusAggregator;
import org.apache.nifi.minifi.bootstrap.status.PeriodicStatusReporter;
import org.apache.nifi.minifi.commons.status.FlowStatusReport;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.NIFI_MINIFI_STATUS_REPORTER_COMPONENTS;
-
public class PeriodicStatusReporterManager implements QueryableStatusAggregator {
private static final Logger LOGGER = LoggerFactory.getLogger(PeriodicStatusReporterManager.class);
private static final String FLOW_STATUS_REPORT_CMD = "FLOW_STATUS_REPORT";
- private final Properties bootstrapProperties;
+ private final BootstrapProperties bootstrapProperties;
private final MiNiFiStatusProvider miNiFiStatusProvider;
private final MiNiFiCommandSender miNiFiCommandSender;
private final MiNiFiParameters miNiFiParameters;
private Set periodicStatusReporters = Collections.emptySet();
- public PeriodicStatusReporterManager(Properties bootstrapProperties, MiNiFiStatusProvider miNiFiStatusProvider, MiNiFiCommandSender miNiFiCommandSender,
+ public PeriodicStatusReporterManager(BootstrapProperties bootstrapProperties, MiNiFiStatusProvider miNiFiStatusProvider, MiNiFiCommandSender miNiFiCommandSender,
MiNiFiParameters miNiFiParameters) {
this.bootstrapProperties = bootstrapProperties;
this.miNiFiStatusProvider = miNiFiStatusProvider;
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/UpdatePropertiesService.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/UpdatePropertiesService.java
index 36e68f7124744..a6058f953c701 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/UpdatePropertiesService.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/service/UpdatePropertiesService.java
@@ -25,10 +25,10 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.Optional;
-import java.util.Properties;
import org.apache.nifi.minifi.bootstrap.RunMiNiFi;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeException;
import org.apache.nifi.minifi.commons.api.MiNiFiCommandState;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
public class UpdatePropertiesService {
@@ -59,7 +59,7 @@ public Optional handleUpdate() {
Files.copy(bootstrapFileProvider.getBootstrapConfNewFile().toPath(), bootstrapConfigFile.toPath(), REPLACE_EXISTING);
// already from new
- commandState = generateConfigfilesBasedOnNewProperties(bootstrapConfigFile, bootstrapSwapConfigFile, bootstrapFileProvider.getBootstrapProperties());
+ commandState = generateConfigfilesBasedOnNewProperties(bootstrapConfigFile, bootstrapSwapConfigFile, bootstrapFileProvider.getProtectedBootstrapProperties());
} catch (Exception e) {
commandState = Optional.of(MiNiFiCommandState.NOT_APPLIED_WITHOUT_RESTART);
logger.error("Failed to load new bootstrap properties", e);
@@ -67,7 +67,7 @@ public Optional handleUpdate() {
return commandState;
}
- private Optional generateConfigfilesBasedOnNewProperties(File bootstrapConfigFile, File bootstrapSwapConfigFile, Properties bootstrapProperties)
+ private Optional generateConfigfilesBasedOnNewProperties(File bootstrapConfigFile, File bootstrapSwapConfigFile, BootstrapProperties bootstrapProperties)
throws IOException, ConfigurationChangeException {
Optional commandState = Optional.empty();
try {
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/PeriodicStatusReporter.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/PeriodicStatusReporter.java
index 0816aa79ea921..6cb53617ff671 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/PeriodicStatusReporter.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/PeriodicStatusReporter.java
@@ -17,12 +17,11 @@
package org.apache.nifi.minifi.bootstrap.status;
-import org.apache.nifi.minifi.bootstrap.QueryableStatusAggregator;
-
-import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import org.apache.nifi.minifi.bootstrap.QueryableStatusAggregator;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
public abstract class PeriodicStatusReporter {
@@ -38,7 +37,7 @@ public abstract class PeriodicStatusReporter {
*
* @param properties from the bootstrap configuration
*/
- public abstract void initialize(Properties properties, QueryableStatusAggregator queryableStatusAggregator);
+ public abstract void initialize(BootstrapProperties properties, QueryableStatusAggregator queryableStatusAggregator);
/**
* Begins the associated reporting service provided by the given implementation. In most implementations, no action will occur until this method is invoked. The implementing class must have set
diff --git a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLogger.java b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLogger.java
index 2d6f7fa5bb031..0f58dbdcbf08f 100644
--- a/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLogger.java
+++ b/minifi/minifi-bootstrap/src/main/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLogger.java
@@ -22,11 +22,11 @@
import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY;
import java.io.IOException;
-import java.util.Properties;
import org.apache.nifi.logging.LogLevel;
import org.apache.nifi.minifi.bootstrap.QueryableStatusAggregator;
import org.apache.nifi.minifi.bootstrap.status.PeriodicStatusReporter;
import org.apache.nifi.minifi.commons.status.FlowStatusReport;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,7 +41,7 @@ public class StatusLogger extends PeriodicStatusReporter {
static final String ENCOUNTERED_IO_EXCEPTION = "Encountered an IO Exception while attempting to query the flow status.";
@Override
- public void initialize(Properties properties, QueryableStatusAggregator queryableStatusAggregator) {
+ public void initialize(BootstrapProperties properties, QueryableStatusAggregator queryableStatusAggregator) {
this.queryableStatusAggregator = queryableStatusAggregator;
String periodString = properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey());
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestorTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestorTest.java
index cc967e552c06a..08dbc4b836ca0 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestorTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/FileChangeIngestorTest.java
@@ -18,8 +18,11 @@
package org.apache.nifi.minifi.bootstrap.configuration.ingestors;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.FileChangeIngestor.DEFAULT_POLLING_PERIOD_INTERVAL;
import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.PullHttpChangeIngestor.PULL_HTTP_BASE_KEY;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,12 +34,12 @@
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Collections;
-import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.nifi.minifi.bootstrap.ConfigurationFileHolder;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
import org.apache.nifi.minifi.bootstrap.configuration.differentiators.Differentiator;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -49,7 +52,7 @@ public class FileChangeIngestorTest {
private FileChangeIngestor notifierSpy;
private WatchService mockWatchService;
- private Properties testProperties;
+ private BootstrapProperties testProperties;
private Differentiator mockDifferentiator;
private ConfigurationChangeNotifier testNotifier;
@@ -60,15 +63,15 @@ public void setUp() {
notifierSpy = Mockito.spy(new FileChangeIngestor());
mockDifferentiator = mock(Differentiator.class);
testNotifier = mock(ConfigurationChangeNotifier.class);
+ testProperties = mock(BootstrapProperties.class);
setMocks();
- testProperties = new Properties();
- testProperties.put(FileChangeIngestor.CONFIG_FILE_PATH_KEY, TEST_CONFIG_PATH);
- testProperties.put(PullHttpChangeIngestor.OVERRIDE_SECURITY, "true");
- testProperties.put(PULL_HTTP_BASE_KEY + ".override.core", "true");
- testProperties.put(FileChangeIngestor.POLLING_PERIOD_INTERVAL_KEY, FileChangeIngestor.DEFAULT_POLLING_PERIOD_INTERVAL);
- testProperties.put(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey(), MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getDefaultValue());
+ when(testProperties.getProperty(FileChangeIngestor.CONFIG_FILE_PATH_KEY)).thenReturn(TEST_CONFIG_PATH);
+ when(testProperties.getProperty(PullHttpChangeIngestor.OVERRIDE_SECURITY)).thenReturn("true");
+ when(testProperties.getProperty(PULL_HTTP_BASE_KEY + ".override.core")).thenReturn("true");
+ when(testProperties.getProperty(eq(FileChangeIngestor.POLLING_PERIOD_INTERVAL_KEY), any())).thenReturn(String.valueOf(DEFAULT_POLLING_PERIOD_INTERVAL));
+ when(testProperties.getProperty(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey())).thenReturn(MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getDefaultValue());
}
@AfterEach
@@ -78,7 +81,7 @@ public void tearDown() {
@Test
public void testInitializeInvalidFile() {
- testProperties.put(FileChangeIngestor.CONFIG_FILE_PATH_KEY, "/land/of/make/believe");
+ when(testProperties.getProperty(FileChangeIngestor.CONFIG_FILE_PATH_KEY)).thenReturn("/land/of/make/believe");
assertThrows(IllegalStateException.class, () -> notifierSpy.initialize(testProperties, mock(ConfigurationFileHolder.class), mock(ConfigurationChangeNotifier.class)));
}
@@ -89,13 +92,12 @@ public void testInitializeValidFile() {
@Test
public void testInitializeInvalidPollingPeriod() {
- testProperties.put(FileChangeIngestor.POLLING_PERIOD_INTERVAL_KEY, "abc");
+ when(testProperties.getProperty(eq(FileChangeIngestor.POLLING_PERIOD_INTERVAL_KEY), any())).thenReturn("abc");
assertThrows(IllegalStateException.class, () -> notifierSpy.initialize(testProperties, mock(ConfigurationFileHolder.class), mock(ConfigurationChangeNotifier.class)));
}
@Test
public void testInitializeUseDefaultPolling() {
- testProperties.remove(FileChangeIngestor.POLLING_PERIOD_INTERVAL_KEY);
notifierSpy.initialize(testProperties, mock(ConfigurationFileHolder.class), mock(ConfigurationChangeNotifier.class));
}
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorSSLTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorSSLTest.java
index 2f7d644ef3ac5..8597da3168e28 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorSSLTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorSSLTest.java
@@ -25,7 +25,8 @@
import java.nio.ByteBuffer;
import java.security.KeyStore;
import java.util.Collections;
-import java.util.Properties;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
@@ -35,6 +36,7 @@
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeListener;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
import org.apache.nifi.minifi.bootstrap.configuration.ListenerHandleResult;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
@@ -50,16 +52,17 @@ public class RestChangeIngestorSSLTest extends RestChangeIngestorCommonTest {
public static void setUpHttps() throws IOException, InterruptedException {
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().trustStoreType("JKS").build();
- Properties properties = new Properties();
- properties.setProperty(RestChangeIngestor.TRUSTSTORE_LOCATION_KEY, tlsConfiguration.getTruststorePath());
- properties.setProperty(RestChangeIngestor.TRUSTSTORE_PASSWORD_KEY, tlsConfiguration.getTruststorePassword());
- properties.setProperty(RestChangeIngestor.TRUSTSTORE_TYPE_KEY, tlsConfiguration.getTruststoreType().getType());
- properties.setProperty(RestChangeIngestor.KEYSTORE_LOCATION_KEY, tlsConfiguration.getKeystorePath());
- properties.setProperty(RestChangeIngestor.KEYSTORE_PASSWORD_KEY, tlsConfiguration.getKeystorePassword());
- properties.setProperty(RestChangeIngestor.KEYSTORE_TYPE_KEY, tlsConfiguration.getKeystoreType().getType());
- properties.setProperty(RestChangeIngestor.NEED_CLIENT_AUTH_KEY, "false");
- properties.put(PullHttpChangeIngestor.OVERRIDE_SECURITY, "true");
- properties.put(PULL_HTTP_BASE_KEY + ".override.core", "true");
+ Map bootstrapProperties = new HashMap<>();
+ bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_LOCATION_KEY, tlsConfiguration.getTruststorePath());
+ bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_PASSWORD_KEY, tlsConfiguration.getTruststorePassword());
+ bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_TYPE_KEY, tlsConfiguration.getTruststoreType().getType());
+ bootstrapProperties.put(RestChangeIngestor.KEYSTORE_LOCATION_KEY, tlsConfiguration.getKeystorePath());
+ bootstrapProperties.put(RestChangeIngestor.KEYSTORE_PASSWORD_KEY, tlsConfiguration.getKeystorePassword());
+ bootstrapProperties.put(RestChangeIngestor.KEYSTORE_TYPE_KEY, tlsConfiguration.getKeystoreType().getType());
+ bootstrapProperties.put(RestChangeIngestor.NEED_CLIENT_AUTH_KEY, "false");
+ bootstrapProperties.put(PullHttpChangeIngestor.OVERRIDE_SECURITY, "true");
+ bootstrapProperties.put(PULL_HTTP_BASE_KEY + ".override.core", "true");
+ BootstrapProperties properties = new BootstrapProperties(bootstrapProperties);
restChangeIngestor = new RestChangeIngestor();
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorTest.java
index 64142eccbf683..57c395975411d 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/configuration/ingestors/RestChangeIngestorTest.java
@@ -18,15 +18,20 @@
package org.apache.nifi.minifi.bootstrap.configuration.ingestors;
import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.PullHttpChangeIngestor.PULL_HTTP_BASE_KEY;
+import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.RestChangeIngestor.HOST_KEY;
+import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.RestChangeIngestor.PORT_KEY;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
-import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import okhttp3.OkHttpClient;
import org.apache.nifi.minifi.bootstrap.ConfigurationFileHolder;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.mockito.Mockito;
@@ -35,9 +40,11 @@ public class RestChangeIngestorTest extends RestChangeIngestorCommonTest {
@BeforeAll
public static void setUp() throws InterruptedException, MalformedURLException {
- Properties properties = new Properties();
- properties.put(PullHttpChangeIngestor.OVERRIDE_SECURITY, "true");
- properties.put(PULL_HTTP_BASE_KEY + ".override.core", "true");
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ when(properties.getProperty(PullHttpChangeIngestor.OVERRIDE_SECURITY)).thenReturn("true");
+ when(properties.getProperty(PULL_HTTP_BASE_KEY + ".override.core")).thenReturn("true");
+ when(properties.getProperty(eq(PORT_KEY), any())).thenReturn("0");
+ when(properties.getProperty(eq(HOST_KEY), any())).thenReturn("localhost");
restChangeIngestor = new RestChangeIngestor();
testNotifier = Mockito.mock(ConfigurationChangeNotifier.class);
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProviderTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProviderTest.java
index 87f3ecd810294..e580b1cadf504 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProviderTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/GracefulShutdownParameterProviderTest.java
@@ -20,10 +20,14 @@
import static org.apache.nifi.minifi.bootstrap.service.GracefulShutdownParameterProvider.DEFAULT_GRACEFUL_SHUTDOWN_VALUE;
import static org.apache.nifi.minifi.bootstrap.service.GracefulShutdownParameterProvider.GRACEFUL_SHUTDOWN_PROP;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
-import java.util.Properties;
+import java.util.Optional;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
@@ -46,10 +50,11 @@ class GracefulShutdownParameterProviderTest {
@NullSource
@ValueSource(strings = {"notAnInteger", "-1"})
void testGetBootstrapPropertiesShouldReturnDefaultShutdownPropertyValue(String shutdownProperty) throws IOException {
- Properties properties = new Properties();
- if (shutdownProperty != null) {
- properties.setProperty(GRACEFUL_SHUTDOWN_PROP, shutdownProperty);
- }
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+
+ when(properties.getProperty(eq(GRACEFUL_SHUTDOWN_PROP), any()))
+ .thenReturn(Optional.ofNullable(shutdownProperty).orElse(DEFAULT_GRACEFUL_SHUTDOWN_VALUE));
+
when(bootstrapFileProvider.getBootstrapProperties()).thenReturn(properties);
assertEquals(Integer.parseInt(DEFAULT_GRACEFUL_SHUTDOWN_VALUE), gracefulShutdownParameterProvider.getGracefulShutdownSeconds());
@@ -57,8 +62,8 @@ void testGetBootstrapPropertiesShouldReturnDefaultShutdownPropertyValue(String s
@Test
void testGetBootstrapPropertiesShouldReturnShutdownPropertyValue() throws IOException {
- Properties properties = new Properties();
- properties.setProperty(GRACEFUL_SHUTDOWN_PROP, "1000");
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ when(properties.getProperty(eq(GRACEFUL_SHUTDOWN_PROP), any())).thenReturn("1000");
when(bootstrapFileProvider.getBootstrapProperties()).thenReturn(properties);
assertEquals(1000, gracefulShutdownParameterProvider.getGracefulShutdownSeconds());
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGeneratorTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGeneratorTest.java
index 1ee026b445e83..9034e99b2b275 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGeneratorTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/service/MiNiFiPropertiesGeneratorTest.java
@@ -53,6 +53,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeException;
import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.apache.nifi.util.NiFiProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -82,7 +83,7 @@ public void setup() throws IOException {
@Test
public void testGenerateDefaultNiFiProperties() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Map.of());
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of());
// when
testPropertiesGenerator.generateMinifiProperties(configDirectory.toString(), bootstrapProperties);
@@ -101,7 +102,7 @@ public void testGenerateDefaultNiFiProperties() throws ConfigurationChangeExcept
@Test
public void testMiNiFiPropertiesMappedToAppropriateNiFiProperties() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Stream.of(
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Stream.of(
MiNiFiProperties.NIFI_MINIFI_FLOW_CONFIG.getKey(),
MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE.getKey(),
MiNiFiProperties.NIFI_MINIFI_SECURITY_KEYSTORE_TYPE.getKey(),
@@ -126,7 +127,7 @@ public void testMiNiFiPropertiesMappedToAppropriateNiFiProperties() throws Confi
@Test
public void testSensitivePropertiesAreGeneratedWhenNotProvidedInBootstrap() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Map.of());
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of());
// when
testPropertiesGenerator.generateMinifiProperties(configDirectory.toString(), bootstrapProperties);
@@ -142,7 +143,7 @@ public void testSensitivePropertiesAreUsedWhenProvidedInBootstrap() throws Confi
// given
String sensitivePropertiesKey = "sensitive_properties_key";
String sensitivePropertiesAlgorithm = "sensitive_properties_algorithm";
- Properties bootstrapProperties = createBootstrapProperties(Map.of(
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of(
MiNiFiProperties.NIFI_MINIFI_SENSITIVE_PROPS_KEY.getKey(), sensitivePropertiesKey,
MiNiFiProperties.NIFI_MINIFI_SENSITIVE_PROPS_ALGORITHM.getKey(), sensitivePropertiesAlgorithm
));
@@ -159,7 +160,7 @@ public void testSensitivePropertiesAreUsedWhenProvidedInBootstrap() throws Confi
@Test
public void testNonBlankC2PropertiesAreCopiedToMiNiFiProperties() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Map.of(
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of(
MiNiFiProperties.C2_ENABLE.getKey(), TRUE.toString(),
MiNiFiProperties.C2_AGENT_CLASS.getKey(), EMPTY,
MiNiFiProperties.C2_AGENT_IDENTIFIER.getKey(), SPACE
@@ -180,7 +181,7 @@ public void testNonBlankC2PropertiesAreCopiedToMiNiFiProperties() throws Configu
public void testDefaultNiFiPropertiesAreOverridden() throws ConfigurationChangeException {
// given
String archiveDir = "/path/to";
- Properties bootstrapProperties = createBootstrapProperties(Map.of(
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of(
NiFiProperties.FLOW_CONFIGURATION_ARCHIVE_ENABLED, TRUE.toString(),
NiFiProperties.FLOW_CONFIGURATION_ARCHIVE_DIR, archiveDir
));
@@ -211,7 +212,7 @@ public void testDefaultNiFiPropertiesAreOverridden() throws ConfigurationChangeE
@Test
public void testArbitraryNiFiPropertyCanBePassedViaBootstrapConf() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Map.of(
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of(
"nifi.new.property", "new_property_value",
"nifi.other.new.property", "other_new_property_value"
));
@@ -229,7 +230,7 @@ public void testArbitraryNiFiPropertyCanBePassedViaBootstrapConf() throws Config
@Test
public void bootstrapFileAndLogPropertiesAreGeneratedIntoMiNiFiProperties() throws ConfigurationChangeException {
// given
- Properties bootstrapProperties = createBootstrapProperties(Map.of());
+ BootstrapProperties bootstrapProperties = createBootstrapProperties(Map.of());
// when
testPropertiesGenerator.generateMinifiProperties(configDirectory.toString(), bootstrapProperties);
@@ -243,12 +244,13 @@ public void bootstrapFileAndLogPropertiesAreGeneratedIntoMiNiFiProperties() thro
);
}
- private Properties createBootstrapProperties(Map keyValues) {
+ private BootstrapProperties createBootstrapProperties(Map keyValues) {
try (OutputStream outputStream = newOutputStream(bootstrapPropertiesFile)) {
Properties properties = new Properties();
+ BootstrapProperties bootstrapProperties = new BootstrapProperties(keyValues);
properties.putAll(keyValues);
properties.store(outputStream, EMPTY);
- return properties;
+ return bootstrapProperties;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLoggerTest.java b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLoggerTest.java
index 918edacee7664..08cb00f403a90 100644
--- a/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLoggerTest.java
+++ b/minifi/minifi-bootstrap/src/test/java/org/apache/nifi/minifi/bootstrap/status/reporters/StatusLoggerTest.java
@@ -22,16 +22,18 @@
import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD;
import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import java.io.IOException;
-import java.util.Properties;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.logging.LogLevel;
import org.apache.nifi.minifi.bootstrap.QueryableStatusAggregator;
import org.apache.nifi.minifi.commons.status.FlowStatusReport;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -68,28 +70,28 @@ public void init() throws IOException {
@Test
public void testFailedInitDueToFatalLogLevel() {
- Properties properties = new Properties();
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey(), "1");
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey(), LogLevel.FATAL.name());
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey(), MOCK_QUERY);
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey())).willReturn("1");
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey())).willReturn(LogLevel.FATAL.name());
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey())).willReturn(MOCK_QUERY);
assertThrows(IllegalStateException.class, () -> statusLogger.initialize(properties, queryableStatusAggregator));
}
@Test
public void testFailedInitDueToNoPeriod() {
- Properties properties = new Properties();
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey(), LogLevel.INFO.name());
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey(), MOCK_QUERY);
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey())).willReturn(LogLevel.INFO.name());
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey())).willReturn(MOCK_QUERY);
assertThrows(IllegalStateException.class, () -> statusLogger.initialize(properties, queryableStatusAggregator));
}
@Test
public void testFailedInitDueToNoQuery() {
- Properties properties = new Properties();
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey(), "1");
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey(), LogLevel.INFO.name());
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey())).willReturn(LogLevel.INFO.name());
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey())).willReturn(MOCK_QUERY);
assertThrows(IllegalStateException.class, () -> statusLogger.initialize(properties, queryableStatusAggregator));
}
@@ -139,13 +141,9 @@ public void testError() {
verify(logger, Mockito.atLeastOnce()).error(MOCK_STATUS, (Throwable) null);
}
- // Exception testing
@Test
public void testTraceException() throws IOException {
- Properties properties = new Properties();
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey(), "1");
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey(), LogLevel.TRACE.name());
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey(), MOCK_QUERY);
+ BootstrapProperties properties = getProperties(LogLevel.TRACE);
IOException ioException = new IOException("This is an expected test exception");
Mockito.when(queryableStatusAggregator.statusReport(MOCK_QUERY)).thenThrow(ioException);
@@ -205,11 +203,11 @@ public void testErrorException() throws IOException {
verify(logger, Mockito.atLeastOnce()).error(ENCOUNTERED_IO_EXCEPTION, ioException);
}
- private static Properties getProperties(LogLevel logLevel) {
- Properties properties = new Properties();
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey(), "1");
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey(), logLevel.name());
- properties.setProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey(), MOCK_QUERY);
+ private static BootstrapProperties getProperties(LogLevel logLevel) {
+ BootstrapProperties properties = mock(BootstrapProperties.class);
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_PERIOD.getKey())).willReturn("1");
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_LEVEL.getKey())).willReturn(logLevel.name());
+ given(properties.getProperty(NIFI_MINIFI_STATUS_REPORTER_LOG_QUERY.getKey())).willReturn(MOCK_QUERY);
return properties;
}
@@ -222,7 +220,6 @@ public RunOnceScheduledExecutorService(int corePoolSize) {
@Override
public ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
command.run();
- // Return value is not used
return null;
}
}
diff --git a/minifi/minifi-c2/minifi-c2-service/pom.xml b/minifi/minifi-c2/minifi-c2-service/pom.xml
index 70664942db5ba..6a5fccad41905 100644
--- a/minifi/minifi-c2/minifi-c2-service/pom.xml
+++ b/minifi/minifi-c2/minifi-c2-service/pom.xml
@@ -74,7 +74,6 @@ limitations under the License.
com.google.guavaguava
- providedcom.fasterxml.jackson.core
@@ -97,7 +96,7 @@ limitations under the License.
provided
- io.swagger
+ io.swagger.core.v3swagger-annotationsprovided
diff --git a/minifi/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java b/minifi/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java
index f362ccea2213e..b233e774929ca 100644
--- a/minifi/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java
+++ b/minifi/minifi-c2/minifi-c2-service/src/main/java/org/apache/nifi/minifi/c2/service/ConfigService.java
@@ -26,11 +26,12 @@
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.UncheckedExecutionException;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -77,10 +78,6 @@
@Configuration
@Path("/config")
-@ApiModel(
- value = "/config",
- description = "Provides configuration and heartbeat/acknowledge capabilities for MiNiFi instances"
-)
public class ConfigService {
public static final String MESSAGE_400 = "MiNiFi C2 server was unable to complete the request because it was invalid. The request should not be retried without modification.";
@@ -177,15 +174,23 @@ protected ConfigurationProviderInfo initContentTypeInfo(List getPropertyValue(String name, Map, ?> properti
private static Stream keyPermutations(String name) {
return Stream.of(name, name.replace(DOT, UNDERSCORE), name.replace(HYPHEN, UNDERSCORE), name.replace(DOT, UNDERSCORE).replace(HYPHEN, UNDERSCORE)).distinct();
}
+
}
diff --git a/minifi/minifi-commons/minifi-commons-utils/src/main/java/org/apache/nifi/minifi/commons/utils/SensitivePropertyUtils.java b/minifi/minifi-commons/minifi-commons-utils/src/main/java/org/apache/nifi/minifi/commons/utils/SensitivePropertyUtils.java
new file mode 100644
index 0000000000000..301f2637d8a86
--- /dev/null
+++ b/minifi/minifi-commons/minifi-commons-utils/src/main/java/org/apache/nifi/minifi/commons/utils/SensitivePropertyUtils.java
@@ -0,0 +1,83 @@
+/*
+ * 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.minifi.commons.utils;
+
+import static java.lang.String.format;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Optional;
+import java.util.Properties;
+
+public class SensitivePropertyUtils {
+
+ public static final String MINIFI_BOOTSTRAP_SENSITIVE_KEY = "minifi.bootstrap.sensitive.key";
+ private static final String EMPTY = "";
+
+ private SensitivePropertyUtils() {
+ }
+
+ public static String getFormattedKey() {
+ String key = getKey(System.getProperty("minifi.bootstrap.conf.file.path"));
+ // Format the key (check hex validity and remove spaces)
+ return getFormattedKey(key);
+ }
+
+ public static String getFormattedKey(String unformattedKey) {
+ String key = formatHexKey(unformattedKey);
+
+ if (isNotEmpty(key) && !isHexKeyValid(key)) {
+ throw new IllegalArgumentException("The key was not provided in valid hex format and of the correct length");
+ } else {
+ return key;
+ }
+ }
+
+ private static String formatHexKey(String input) {
+ return Optional.ofNullable(input)
+ .map(String::trim)
+ .filter(SensitivePropertyUtils::isNotEmpty)
+ .map(str -> str.replaceAll("[^0-9a-fA-F]", EMPTY).toLowerCase())
+ .orElse(EMPTY);
+ }
+
+ private static boolean isHexKeyValid(String key) {
+ return Optional.ofNullable(key)
+ .map(String::trim)
+ .filter(SensitivePropertyUtils::isNotEmpty)
+ .filter(k -> k.matches("^[0-9a-fA-F]{64}$"))
+ .isPresent();
+ }
+
+ private static String getKey(String bootstrapConfigFilePath) {
+ Properties properties = new Properties();
+
+ try (InputStream inputStream = new BufferedInputStream(new FileInputStream(bootstrapConfigFilePath))) {
+ properties.load(inputStream);
+ } catch (Exception e) {
+ throw new RuntimeException(format("Loading Bootstrap Properties [%s] failed", bootstrapConfigFilePath), e);
+ }
+
+ return properties.getProperty(MINIFI_BOOTSTRAP_SENSITIVE_KEY);
+ }
+
+ private static boolean isNotEmpty(String keyFilePath) {
+ return keyFilePath != null && !keyFilePath.isBlank();
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/pom.xml b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/pom.xml
new file mode 100644
index 0000000000000..3b5249d22d86b
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/pom.xml
@@ -0,0 +1,55 @@
+
+
+
+
+ 4.0.0
+
+ org.apache.nifi.minifi
+ minifi-framework
+ 2.0.0-SNAPSHOT
+
+
+ minifi-properties-loader
+
+
+
+ org.apache.nifi
+ nifi-property-protection-loader
+
+
+ org.apache.nifi
+ nifi-property-protection-cipher
+
+
+ org.apache.nifi
+ nifi-properties
+
+
+ org.apache.nifi.minifi
+ minifi-commons-utils
+ provided
+
+
+ org.apache.nifi.minifi
+ minifi-commons-api
+
+
+
+
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapProperties.java
new file mode 100644
index 0000000000000..29bac65691cd5
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapProperties.java
@@ -0,0 +1,71 @@
+/*
+ * 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.minifi.properties;
+
+import static org.apache.nifi.minifi.commons.utils.PropertyUtil.resolvePropertyValue;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.nifi.properties.ApplicationProperties;
+
+/**
+ * Extends Properties functionality with System and Environment property override possibility. The property resolution also works with
+ * dots and hyphens that are not supported in some shells.
+ */
+public class BootstrapProperties extends ApplicationProperties {
+
+ public BootstrapProperties() {
+ super(Collections.emptyMap());
+ }
+
+ public BootstrapProperties(Properties properties) {
+ super(properties);
+ }
+ public BootstrapProperties(Map properties) {
+ super(properties);
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return resolvePropertyValue(key, System.getProperties())
+ .or(() -> resolvePropertyValue(key, System.getenv()))
+ .orElseGet(() -> super.getProperty(key));
+ }
+
+ @Override
+ public String getProperty(String key, String defaultValue) {
+ return resolvePropertyValue(key, System.getProperties())
+ .or(() -> resolvePropertyValue(key, System.getenv()))
+ .orElseGet(() -> super.getProperty(key, defaultValue));
+ }
+
+ @Override
+ public Set getPropertyKeys() {
+ Set systemKeys = System.getProperties().keySet().stream().map(String::valueOf).collect(Collectors.toSet());
+ return Stream.of(systemKeys, System.getenv().keySet(), super.getPropertyKeys())
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet());
+ }
+
+ public boolean containsKey(String key) {
+ return getPropertyKeys().contains(key);
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoader.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoader.java
new file mode 100644
index 0000000000000..4c24394b8b97b
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoader.java
@@ -0,0 +1,49 @@
+/*
+ * 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.minifi.properties;
+
+import static java.lang.String.format;
+import static org.apache.nifi.minifi.commons.utils.SensitivePropertyUtils.MINIFI_BOOTSTRAP_SENSITIVE_KEY;
+import static org.apache.nifi.minifi.commons.utils.SensitivePropertyUtils.getFormattedKey;
+
+import java.io.File;
+import org.apache.nifi.properties.AesGcmSensitivePropertyProvider;
+
+public class BootstrapPropertiesLoader {
+
+ public static BootstrapProperties load(File file) {
+ ProtectedBootstrapProperties protectedProperties = loadProtectedProperties(file);
+ if (protectedProperties.hasProtectedKeys()) {
+ String sensitiveKey = protectedProperties.getApplicationProperties().getProperty(MINIFI_BOOTSTRAP_SENSITIVE_KEY);
+ validateSensitiveKeyProperty(sensitiveKey);
+ String keyHex = getFormattedKey(sensitiveKey);
+ protectedProperties.addSensitivePropertyProvider(new AesGcmSensitivePropertyProvider(keyHex));
+ }
+ return protectedProperties.getUnprotectedProperties();
+ }
+
+ public static ProtectedBootstrapProperties loadProtectedProperties(File file) {
+ return new ProtectedBootstrapProperties(PropertiesLoader.load(file, "Bootstrap"));
+ }
+
+ private static void validateSensitiveKeyProperty(String sensitiveKey) {
+ if (sensitiveKey == null || sensitiveKey.trim().isEmpty()) {
+ throw new IllegalArgumentException(format("bootstrap.conf contains protected properties but %s is not found", MINIFI_BOOTSTRAP_SENSITIVE_KEY));
+ }
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/DuplicateDetectingProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/DuplicateDetectingProperties.java
new file mode 100644
index 0000000000000..92fc64430f9ec
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/DuplicateDetectingProperties.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.minifi.properties;
+
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+class DuplicateDetectingProperties extends Properties {
+ // Only need to retain Properties key. This will help prevent possible inadvertent exposure of sensitive Properties value
+ private final Set duplicateKeys = new HashSet<>();
+ private final Set redundantKeys = new HashSet<>();
+
+ public DuplicateDetectingProperties() {
+ super();
+ }
+
+ public Set duplicateKeySet() {
+ return duplicateKeys;
+ }
+
+ public Set redundantKeySet() {
+ return redundantKeys;
+ }
+
+ @Override
+ public Object put(Object key, Object value) {
+ Object existingValue = super.put(key, value);
+ if (existingValue != null) {
+ if (existingValue.toString().equals(value.toString())) {
+ redundantKeys.add(key.toString());
+ } else {
+ duplicateKeys.add(key.toString());
+ }
+ }
+ return value;
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoader.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoader.java
new file mode 100644
index 0000000000000..d15fa3ee37f08
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoader.java
@@ -0,0 +1,104 @@
+/*
+ * 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.minifi.properties;
+
+import java.io.File;
+import org.apache.nifi.properties.AesGcmSensitivePropertyProvider;
+import org.apache.nifi.util.NiFiBootstrapUtils;
+import org.apache.nifi.util.NiFiProperties;
+
+public class MiNiFiPropertiesLoader {
+
+ private static final String DEFAULT_APPLICATION_PROPERTIES_FILE_PATH = NiFiBootstrapUtils.getDefaultApplicationPropertiesFilePath();
+
+ private NiFiProperties instance;
+ private String keyHex;
+
+ public MiNiFiPropertiesLoader(String keyHex) {
+ this.keyHex = keyHex;
+ }
+
+ /**
+ * Returns a {@link ProtectedMiNiFiProperties} instance loaded from the
+ * serialized form in the file. Responsible for actually reading from disk
+ * and deserializing the properties. Returns a protected instance to allow
+ * for decryption operations.
+ *
+ * @param file the file containing serialized properties
+ * @return the ProtectedMiNiFiProperties instance
+ */
+ ProtectedMiNiFiProperties loadProtectedProperties(File file) {
+ return new ProtectedMiNiFiProperties(PropertiesLoader.load(file, "Application"));
+ }
+
+ /**
+ * Returns an instance of {@link NiFiProperties} loaded from the provided
+ * {@link File}. If any properties are protected, will attempt to use the
+ * {@link AesGcmSensitivePropertyProvider} to unprotect them
+ * transparently.
+ *
+ * @param file the File containing the serialized properties
+ * @return the NiFiProperties instance
+ */
+ public NiFiProperties load(File file) {
+ ProtectedMiNiFiProperties protectedProperties = loadProtectedProperties(file);
+ if (protectedProperties.hasProtectedKeys()) {
+ protectedProperties.addSensitivePropertyProvider(new AesGcmSensitivePropertyProvider(keyHex));
+ }
+ return new MultiSourceMinifiProperties(protectedProperties.getUnprotectedPropertiesAsMap());
+ }
+
+ /**
+ * Returns an instance of {@link NiFiProperties}. If the path is empty, this
+ * will load the default properties file as specified by
+ * {@code NiFiProperties.PROPERTY_FILE_PATH}.
+ *
+ * @param path the path of the serialized properties file
+ * @return the NiFiProperties instance
+ * @see MiNiFiPropertiesLoader#load(File)
+ */
+ public NiFiProperties load(String path) {
+ if (path != null && !path.trim().isEmpty()) {
+ return load(new File(path));
+ } else {
+ return loadDefault();
+ }
+ }
+
+ /**
+ * Returns the loaded {@link NiFiProperties} instance. If none is currently
+ * loaded, attempts to load the default instance.
+ *
+ *
+ * NOTE: This method is used reflectively by the process which starts MiNiFi
+ * so changes to it must be made in conjunction with that mechanism.
+ *
+ * @return the current NiFiProperties instance
+ */
+ public NiFiProperties get() {
+ if (instance == null) {
+ instance = loadDefault();
+ }
+
+ return instance;
+ }
+
+ private NiFiProperties loadDefault() {
+ return load(DEFAULT_APPLICATION_PROPERTIES_FILE_PATH);
+ }
+
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MultiSourceMinifiProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MultiSourceMinifiProperties.java
new file mode 100644
index 0000000000000..f39a97dee5ec1
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/MultiSourceMinifiProperties.java
@@ -0,0 +1,55 @@
+/*
+ * 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.minifi.properties;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.nifi.minifi.commons.utils.PropertyUtil;
+import org.apache.nifi.util.NiFiProperties;
+
+/**
+ * Extends NiFi properties functionality with System and Environment property override possibility. The property resolution also works with
+ * dots and hyphens that are not supported in some shells.
+ */
+public class MultiSourceMinifiProperties extends NiFiProperties {
+
+ public MultiSourceMinifiProperties(Map props) {
+ super(props);
+ }
+
+ @Override
+ public Set getPropertyKeys() {
+ return Stream.of(System.getProperties().stringPropertyNames(), System.getenv().keySet(), super.getPropertyKeys())
+ .flatMap(Set::stream)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public int size() {
+ return getPropertyKeys().size();
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return PropertyUtil.resolvePropertyValue(key, System.getProperties())
+ .or(() -> PropertyUtil.resolvePropertyValue(key, System.getenv()))
+ .orElseGet(() -> super.getProperty(key));
+ }
+
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/PropertiesLoader.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/PropertiesLoader.java
new file mode 100644
index 0000000000000..9785fda997cc1
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/PropertiesLoader.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.minifi.properties;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public interface PropertiesLoader {
+
+ static final Logger logger = LoggerFactory.getLogger(PropertiesLoader.class);
+
+ static Properties load(File file, String propertiesType) {
+ if (file == null || !file.exists() || !file.canRead()) {
+ throw new IllegalArgumentException(String.format("{} Properties [%s] not found", propertiesType, file));
+ }
+
+ logger.info("Loading {} Properties [{}]", propertiesType, file);
+ DuplicateDetectingProperties rawProperties = new DuplicateDetectingProperties();
+
+ try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
+ rawProperties.load(inputStream);
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Loading {} Properties [%s] failed", propertiesType, file), e);
+ }
+
+ if (!rawProperties.redundantKeySet().isEmpty()) {
+ logger.warn("Duplicate property keys with the same value were detected in the properties file: {}", String.join(", ", rawProperties.redundantKeySet()));
+ }
+ if (!rawProperties.duplicateKeySet().isEmpty()) {
+ throw new IllegalArgumentException("Duplicate property keys with different values were detected in the properties file: " + String.join(", ", rawProperties.duplicateKeySet()));
+ }
+
+ Properties properties = new Properties();
+ rawProperties.stringPropertyNames()
+ .forEach(key -> {
+ String property = rawProperties.getProperty(key);
+ properties.setProperty(key, property.trim());
+ });
+ return properties;
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedBootstrapProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedBootstrapProperties.java
new file mode 100644
index 0000000000000..486574a822b8e
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedBootstrapProperties.java
@@ -0,0 +1,134 @@
+/*
+ * 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.minifi.properties;
+
+import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+import static org.apache.nifi.minifi.properties.ProtectedMiNiFiProperties.DEFAULT_SENSITIVE_PROPERTIES;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.properties.ApplicationPropertiesProtector;
+import org.apache.nifi.properties.ProtectedProperties;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProtector;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProtectedBootstrapProperties extends BootstrapProperties implements ProtectedProperties,
+ SensitivePropertyProtector {
+
+ private static final Logger logger = LoggerFactory.getLogger(ProtectedBootstrapProperties.class);
+
+ private BootstrapProperties bootstrapProperties;
+
+ private final SensitivePropertyProtector propertyProtectionDelegate;
+
+ public ProtectedBootstrapProperties(BootstrapProperties props) {
+ super();
+ this.bootstrapProperties = props;
+ this.propertyProtectionDelegate = new ApplicationPropertiesProtector<>(this);
+ logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedBootstrapProperties", getApplicationProperties().getPropertyKeys().size(),
+ getProtectedPropertyKeys().size());
+ }
+
+ public ProtectedBootstrapProperties(Properties rawProps) {
+ this(new BootstrapProperties(rawProps));
+ }
+
+ @Override
+ public Set getPropertyKeysIncludingProtectionSchemes() {
+ return propertyProtectionDelegate.getPropertyKeysIncludingProtectionSchemes();
+ }
+
+ @Override
+ public List getSensitivePropertyKeys() {
+ return propertyProtectionDelegate.getSensitivePropertyKeys();
+ }
+
+ @Override
+ public List getPopulatedSensitivePropertyKeys() {
+ return propertyProtectionDelegate.getPopulatedSensitivePropertyKeys();
+ }
+
+ @Override
+ public boolean hasProtectedKeys() {
+ return propertyProtectionDelegate.hasProtectedKeys();
+ }
+
+ @Override
+ public Map getProtectedPropertyKeys() {
+ return propertyProtectionDelegate.getProtectedPropertyKeys();
+ }
+
+ @Override
+ public boolean isPropertySensitive(String key) {
+ return propertyProtectionDelegate.isPropertySensitive(key);
+ }
+
+ @Override
+ public boolean isPropertyProtected(String key) {
+ return propertyProtectionDelegate.isPropertyProtected(key);
+ }
+
+ @Override
+ public BootstrapProperties getUnprotectedProperties() throws SensitivePropertyProtectionException {
+ return propertyProtectionDelegate.getUnprotectedProperties();
+ }
+
+ @Override
+ public void addSensitivePropertyProvider(SensitivePropertyProvider sensitivePropertyProvider) {
+ propertyProtectionDelegate.addSensitivePropertyProvider(sensitivePropertyProvider);
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeys() {
+ return getProperty(getAdditionalSensitivePropertiesKeysName());
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeysName() {
+ return ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+ }
+
+ @Override
+ public List getDefaultSensitiveProperties() {
+ return Stream.of(DEFAULT_SENSITIVE_PROPERTIES, Arrays.stream(MiNiFiProperties.values()).filter(MiNiFiProperties::isSensitive).map(MiNiFiProperties::getKey).collect(Collectors.toList()))
+ .flatMap(List::stream).distinct().collect(Collectors.toList());
+ }
+
+ @Override
+ public BootstrapProperties getApplicationProperties() {
+ if (this.bootstrapProperties == null) {
+ this.bootstrapProperties = new BootstrapProperties();
+ }
+
+ return this.bootstrapProperties;
+ }
+
+ @Override
+ public BootstrapProperties createApplicationProperties(Properties rawProperties) {
+ return new BootstrapProperties(rawProperties);
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedMiNiFiProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedMiNiFiProperties.java
new file mode 100644
index 0000000000000..238a110413895
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/main/java/org/apache/nifi/minifi/properties/ProtectedMiNiFiProperties.java
@@ -0,0 +1,239 @@
+/*
+ * 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.minifi.properties;
+
+import static java.util.Arrays.asList;
+import static org.apache.nifi.minifi.commons.api.MiNiFiProperties.ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.nifi.minifi.commons.api.MiNiFiProperties;
+import org.apache.nifi.properties.ApplicationPropertiesProtector;
+import org.apache.nifi.properties.ProtectedProperties;
+import org.apache.nifi.properties.SensitivePropertyProtectionException;
+import org.apache.nifi.properties.SensitivePropertyProtector;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Decorator class for intermediate phase when {@link MiNiFiPropertiesLoader} loads the
+ * raw properties file and performs unprotection activities before returning a clean
+ * implementation of {@link NiFiProperties}.
+ * This encapsulates the sensitive property access logic from external consumers
+ * of {@code NiFiProperties}.
+ */
+public class ProtectedMiNiFiProperties extends NiFiProperties implements ProtectedProperties,
+ SensitivePropertyProtector {
+ private static final Logger logger = LoggerFactory.getLogger(ProtectedMiNiFiProperties.class);
+ public static final List DEFAULT_SENSITIVE_PROPERTIES = new ArrayList<>(asList(
+ SECURITY_KEY_PASSWD,
+ SECURITY_KEYSTORE_PASSWD,
+ SECURITY_TRUSTSTORE_PASSWD,
+ SENSITIVE_PROPS_KEY
+ ));
+
+ private final SensitivePropertyProtector propertyProtectionDelegate;
+
+ private NiFiProperties applicationProperties;
+
+ public ProtectedMiNiFiProperties() {
+ this(new NiFiProperties());
+ }
+
+ /**
+ * Creates an instance containing the provided {@link NiFiProperties}.
+ *
+ * @param props the NiFiProperties to contain
+ */
+ public ProtectedMiNiFiProperties(NiFiProperties props) {
+ this.applicationProperties = props;
+ this.propertyProtectionDelegate = new ApplicationPropertiesProtector<>(this);
+ logger.debug("Loaded {} properties (including {} protection schemes) into ProtectedNiFiProperties", getApplicationProperties()
+ .getPropertyKeys().size(), getProtectedPropertyKeys().size());
+ }
+
+ /**
+ * Creates an instance containing the provided raw {@link Properties}.
+ *
+ * @param rawProps the Properties to contain
+ */
+ public ProtectedMiNiFiProperties(Properties rawProps) {
+ this(new NiFiProperties(rawProps));
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeys() {
+ return getProperty(getAdditionalSensitivePropertiesKeysName());
+ }
+
+ @Override
+ public String getAdditionalSensitivePropertiesKeysName() {
+ return ADDITIONAL_SENSITIVE_PROPERTIES_KEY;
+ }
+
+ @Override
+ public List getDefaultSensitiveProperties() {
+ return Stream.of(DEFAULT_SENSITIVE_PROPERTIES,
+ Arrays.stream(MiNiFiProperties.values()).filter(MiNiFiProperties::isSensitive).map(MiNiFiProperties::getKey).toList()).flatMap(List::stream).distinct().toList();
+ }
+
+ /**
+ * Returns the internal representation of the {@link NiFiProperties} -- protected
+ * or not as determined by the current state. No guarantee is made to the
+ * protection state of these properties. If the internal reference is null, a new
+ * {@link NiFiProperties} instance is created.
+ *
+ * @return the internal properties
+ */
+ public NiFiProperties getApplicationProperties() {
+ if (this.applicationProperties == null) {
+ this.applicationProperties = new NiFiProperties();
+ }
+
+ return this.applicationProperties;
+ }
+
+ @Override
+ public NiFiProperties createApplicationProperties(final Properties rawProperties) {
+ return new NiFiProperties(rawProperties);
+ }
+
+ /**
+ * Retrieves the property value for the given property key.
+ *
+ * @param key the key of property value to lookup
+ * @return value of property at given key or null if not found
+ */
+ @Override
+ public String getProperty(String key) {
+ return getApplicationProperties().getProperty(key);
+ }
+
+ /**
+ * Retrieves all known property keys.
+ *
+ * @return all known property keys
+ */
+ @Override
+ public Set getPropertyKeys() {
+ return propertyProtectionDelegate.getPropertyKeys();
+ }
+
+ /**
+ * Returns the number of properties, excluding protection scheme properties.
+ *
+ * would return size 2
+ *
+ * @return the count of real properties
+ */
+ @Override
+ public int size() {
+ return propertyProtectionDelegate.size();
+ }
+
+ @Override
+ public Set getPropertyKeysIncludingProtectionSchemes() {
+ return propertyProtectionDelegate.getPropertyKeysIncludingProtectionSchemes();
+ }
+
+ @Override
+ public List getSensitivePropertyKeys() {
+ return propertyProtectionDelegate.getSensitivePropertyKeys();
+ }
+
+ @Override
+ public List getPopulatedSensitivePropertyKeys() {
+ return propertyProtectionDelegate.getPopulatedSensitivePropertyKeys();
+ }
+
+ @Override
+ public boolean hasProtectedKeys() {
+ return propertyProtectionDelegate.hasProtectedKeys();
+ }
+
+ @Override
+ public Map getProtectedPropertyKeys() {
+ return propertyProtectionDelegate.getProtectedPropertyKeys();
+ }
+
+ @Override
+ public boolean isPropertySensitive(final String key) {
+ return propertyProtectionDelegate.isPropertySensitive(key);
+ }
+
+ @Override
+ public boolean isPropertyProtected(final String key) {
+ return propertyProtectionDelegate.isPropertyProtected(key);
+ }
+
+ @Override
+ public NiFiProperties getUnprotectedProperties() throws SensitivePropertyProtectionException {
+ return propertyProtectionDelegate.getUnprotectedProperties();
+ }
+
+ @Override
+ public void addSensitivePropertyProvider(final SensitivePropertyProvider sensitivePropertyProvider) {
+ propertyProtectionDelegate.addSensitivePropertyProvider(sensitivePropertyProvider);
+ }
+
+ public Map getUnprotectedPropertiesAsMap() {
+ NiFiProperties niFiProperties = propertyProtectionDelegate.getUnprotectedProperties();
+ return niFiProperties.getPropertyKeys().stream().collect(Collectors.toMap(Function.identity(), niFiProperties::getProperty));
+ }
+
+ /**
+ * Returns the number of properties that are marked as protected in the provided {@link NiFiProperties} instance without requiring external creation of a
+ * {@link ProtectedMiNiFiProperties} instance.
+ *
+ * @param plainProperties the instance to count protected properties
+ * @return the number of protected properties
+ */
+ public static int countProtectedProperties(final NiFiProperties plainProperties) {
+ return new ProtectedMiNiFiProperties(plainProperties).getProtectedPropertyKeys().size();
+ }
+
+ /**
+ * Returns the number of properties that are marked as sensitive in the provided {@link NiFiProperties} instance without requiring external creation of a
+ * {@link ProtectedMiNiFiProperties} instance.
+ *
+ * @param plainProperties the instance to count sensitive properties
+ * @return the number of sensitive properties
+ */
+ public static int countSensitiveProperties(final NiFiProperties plainProperties) {
+ return new ProtectedMiNiFiProperties(plainProperties).getSensitivePropertyKeys().size();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s Size [%d]", getClass().getSimpleName(), size());
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoaderTest.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoaderTest.java
new file mode 100644
index 0000000000000..c3d3febcac776
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/BootstrapPropertiesLoaderTest.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.minifi.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+
+class BootstrapPropertiesLoaderTest {
+
+ private static final String NON_EXISTING_FILE_PATH = "/conf/nonexisting.conf";
+ private static final String DUPLICATED_ITEMS_FILE_PATH = "/conf/bootstrap_duplicated_items.conf";
+ private static final String UNPROTECTED_ITEMS_FILE_PATH = "/conf/bootstrap_unprotected.conf";
+ private static final String PROTECTED_ITEMS_FILE_PATH = "/conf/bootstrap_protected.conf";
+ private static final String PROTECTED_ITEMS_WITHOUT_SENSITIVE_KEY_FILE_PATH = "/conf/bootstrap_protected_without_sensitive_key.conf";
+
+ private static final Map UNPROTECTED_PROPERTIES = Map.of("nifi.minifi.security.keystorePasswd", "testPassword", "nifi.minifi.sensitive.props.key", "testSensitivePropsKey");
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfFileIsNotProvided() {
+ assertThrows(IllegalArgumentException.class, () -> BootstrapPropertiesLoader.load(null));
+ }
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfFileDoesNotExists() {
+ assertThrows(IllegalArgumentException.class, () -> BootstrapPropertiesLoader.load(new File(NON_EXISTING_FILE_PATH)));
+ }
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfTheConfigFileContainsDuplicatedKeysWithDifferentValues() {
+ assertThrows(IllegalArgumentException.class, () -> BootstrapPropertiesLoader.load(getFile(DUPLICATED_ITEMS_FILE_PATH)));
+ }
+
+ @Test
+ void shouldReturnPropertiesIfConfigFileDoesNotContainProtectedProperties() {
+ BootstrapProperties bootstrapProperties = BootstrapPropertiesLoader.load(getFile(UNPROTECTED_ITEMS_FILE_PATH));
+
+ assertEquals(UNPROTECTED_PROPERTIES,
+ bootstrapProperties.getPropertyKeys().stream().filter(UNPROTECTED_PROPERTIES::containsKey).collect(Collectors.toMap(Function.identity(), bootstrapProperties::getProperty)));
+ }
+
+ @Test
+ void shouldReturnUnProtectedProperties() {
+ BootstrapProperties bootstrapProperties = BootstrapPropertiesLoader.load(getFile(PROTECTED_ITEMS_FILE_PATH));
+
+ assertEquals(UNPROTECTED_PROPERTIES,
+ bootstrapProperties.getPropertyKeys().stream().filter(UNPROTECTED_PROPERTIES::containsKey).collect(Collectors.toMap(Function.identity(), bootstrapProperties::getProperty)));
+ }
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfFileContainsProtectedPropertiesButSensitiveKeyIsMissing() {
+ assertThrows(IllegalArgumentException.class, () -> BootstrapPropertiesLoader.load(getFile(PROTECTED_ITEMS_WITHOUT_SENSITIVE_KEY_FILE_PATH)));
+ }
+
+ private File getFile(String duplicatedItemsFilePath) {
+ URL resource = BootstrapPropertiesLoaderTest.class.getResource(duplicatedItemsFilePath);
+ assertNotNull(resource);
+ return new File(resource.getPath());
+ }
+}
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoaderTest.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoaderTest.java
new file mode 100644
index 0000000000000..fd4c863dd0f22
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/java/org/apache/nifi/minifi/properties/MiNiFiPropertiesLoaderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.minifi.properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.nifi.util.NiFiProperties;
+import org.junit.jupiter.api.Test;
+
+class MiNiFiPropertiesLoaderTest {
+ private static final String NON_EXISTING_FILE_PATH = "/conf/nonexisting.properties";
+ private static final String DUPLICATED_ITEMS_FILE_PATH = "/conf/bootstrap_duplicated_items.conf";
+ private static final String UNPROTECTED_ITEMS_FILE_PATH = "/conf/minifi_unprotected.properties";
+ private static final String PROTECTED_ITEMS_FILE_PATH = "/conf/minifi_protected.properties";
+ private static final Map UNPROTECTED_PROPERTIES = Map.of("nifi.security.keystorePasswd", "testPassword", "nifi.security.keyPasswd",
+ "testSensitivePropsKey");
+ private static final String PROTECTION_KEY = "00714ae7a77b24cde1d36bd19472777e0d4ab02c38913b7f9bf41f3963147b4f";
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfFileIsNotProvided() {
+ MiNiFiPropertiesLoader miNiFiPropertiesLoader = new MiNiFiPropertiesLoader("");
+ assertThrows(IllegalArgumentException.class, () -> miNiFiPropertiesLoader.load((String) null));
+ }
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfFileDoesNotExists() {
+ MiNiFiPropertiesLoader miNiFiPropertiesLoader = new MiNiFiPropertiesLoader("");
+ assertThrows(IllegalArgumentException.class, () -> miNiFiPropertiesLoader.load(new File(NON_EXISTING_FILE_PATH)));
+ }
+
+ @Test
+ void shouldThrowIllegalArgumentExceptionIfTheConfigFileContainsDuplicatedKeysWithDifferentValues() {
+ MiNiFiPropertiesLoader miNiFiPropertiesLoader = new MiNiFiPropertiesLoader("");
+
+ assertThrows(IllegalArgumentException.class, () -> miNiFiPropertiesLoader.load(getFile(DUPLICATED_ITEMS_FILE_PATH)));
+ }
+
+ @Test
+ void shouldReturnPropertiesIfConfigFileDoesNotContainProtectedProperties() {
+ MiNiFiPropertiesLoader miNiFiPropertiesLoader = new MiNiFiPropertiesLoader("");
+
+ NiFiProperties niFiProperties = miNiFiPropertiesLoader.load(getFile(UNPROTECTED_ITEMS_FILE_PATH));
+
+ assertEquals(UNPROTECTED_PROPERTIES,
+ niFiProperties.getPropertyKeys().stream().filter(UNPROTECTED_PROPERTIES::containsKey).collect(Collectors.toMap(Function.identity(), niFiProperties::getProperty)));
+ }
+
+ @Test
+ void shouldReturnUnProtectedProperties() {
+ MiNiFiPropertiesLoader miNiFiPropertiesLoader = new MiNiFiPropertiesLoader(PROTECTION_KEY);
+
+ NiFiProperties niFiProperties = miNiFiPropertiesLoader.load(getUrl(PROTECTED_ITEMS_FILE_PATH).getPath());
+
+ assertEquals(UNPROTECTED_PROPERTIES,
+ niFiProperties.getPropertyKeys().stream().filter(UNPROTECTED_PROPERTIES::containsKey).collect(Collectors.toMap(Function.identity(), niFiProperties::getProperty)));
+ }
+
+ private File getFile(String propertiesFilePath) {
+ URL resource = getUrl(propertiesFilePath);
+ return new File(resource.getPath());
+ }
+
+ private static URL getUrl(String propertiesFilePath) {
+ URL resource = BootstrapPropertiesLoaderTest.class.getResource(propertiesFilePath);
+ assertNotNull(resource);
+ return resource;
+ }
+}
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_duplicated_items.conf b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_duplicated_items.conf
new file mode 100644
index 0000000000000..dd742bafdabd8
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_duplicated_items.conf
@@ -0,0 +1,18 @@
+# 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.
+
+# property file that contains the same key with different values
+property1=test
+property1=test2
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected.conf b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected.conf
new file mode 100644
index 0000000000000..86966e8999639
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected.conf
@@ -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.
+
+# property file that contains the protected properties and a sensitive key to decrypt it
+nifi.minifi.security.keystorePasswd=noBD32A0ANcz/BHv||nhtyNhlFgNNZcVhgxEOg2xUZ5UAihgyBit1drQ==
+nifi.minifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.minifi.sensitive.props.key=JUMeAtpD1Q7CiXHt||BppiPMoWXxkKBl5mYsxP5vkVabsXZyLu2lxjyv9LMHc6RJEE9g==
+nifi.minifi.sensitive.props.key.protected=aes/gcm/256
+
+minifi.bootstrap.sensitive.key=00714ae7a77b24cde1d36bd19472777e0d4ab02c38913b7f9bf41f3963147b4f
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected_without_sensitive_key.conf b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected_without_sensitive_key.conf
new file mode 100644
index 0000000000000..bcc8fe9d8a2ba
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_protected_without_sensitive_key.conf
@@ -0,0 +1,20 @@
+# 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.
+
+# property file that contains the protected properties but without minifi.bootstrap.sensitive.key
+nifi.minifi.security.keystorePasswd=noBD32A0ANcz/BHv||nhtyNhlFgNNZcVhgxEOg2xUZ5UAihgyBit1drQ==
+nifi.minifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.minifi.sensitive.props.key=JUMeAtpD1Q7CiXHt||BppiPMoWXxkKBl5mYsxP5vkVabsXZyLu2lxjyv9LMHc6RJEE9g==
+nifi.minifi.sensitive.props.key.protected=aes/gcm/256
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_unprotected.conf b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_unprotected.conf
new file mode 100644
index 0000000000000..e08d4790a5c4c
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/bootstrap_unprotected.conf
@@ -0,0 +1,18 @@
+# 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.
+
+# property file that contains the unprotected properties
+nifi.minifi.security.keystorePasswd=testPassword
+nifi.minifi.sensitive.props.key=testSensitivePropsKey
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_duplicated.properties b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_duplicated.properties
new file mode 100644
index 0000000000000..285d126c1defe
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_duplicated.properties
@@ -0,0 +1,20 @@
+# 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.
+
+nifi.security.keystorePasswd=s+jNt0t608+F0uq9||PWPtOCr5fmItmL8ZsgpheQxkkJJzXWqrNvqdCL/gcGE2cy1NTgGDhI1apuacNHjj
+nifi.security.keystorePasswd=123
+nifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.security.keyPasswd=JUMeAtpD1Q7CiXHt||BppiPMoWXxkKBl5mYsxP5vkVabsXZyLu2lxjyv9LMHc6RJEE9g==
+nifi.security.keyPasswd.protected=aes/gcm/256
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_protected.properties b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_protected.properties
new file mode 100644
index 0000000000000..85319171cf80c
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_protected.properties
@@ -0,0 +1,19 @@
+# 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.
+
+nifi.security.keystorePasswd=noBD32A0ANcz/BHv||nhtyNhlFgNNZcVhgxEOg2xUZ5UAihgyBit1drQ==
+nifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.security.keyPasswd=JUMeAtpD1Q7CiXHt||BppiPMoWXxkKBl5mYsxP5vkVabsXZyLu2lxjyv9LMHc6RJEE9g==
+nifi.security.keyPasswd.protected=aes/gcm/256
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_unprotected.properties b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_unprotected.properties
new file mode 100644
index 0000000000000..06d04d3f31a6a
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-properties-loader/src/test/resources/conf/minifi_unprotected.properties
@@ -0,0 +1,17 @@
+# 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.
+
+nifi.security.keystorePasswd=testPassword
+nifi.security.keyPasswd=testSensitivePropsKey
\ No newline at end of file
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
index 8601de6541a13..5d9b2156330c2 100644
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/pom.xml
@@ -58,8 +58,9 @@ limitations under the License.
provided
- org.apache.nifi
- nifi-properties-loader
+ org.apache.nifi.minifi
+ minifi-properties-loader
+ 2.0.0-SNAPSHOTprovided
@@ -76,5 +77,6 @@ limitations under the License.
com.fasterxml.jackson.corejackson-databind
+
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/MiNiFi.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/MiNiFi.java
index 02ecc75e85ba7..567381ddda772 100644
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/MiNiFi.java
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/MiNiFi.java
@@ -16,8 +16,14 @@
*/
package org.apache.nifi.minifi;
+import static org.apache.nifi.minifi.commons.utils.SensitivePropertyUtils.getFormattedKey;
+import static org.apache.nifi.minifi.util.BootstrapClassLoaderUtils.createBootstrapClassLoader;
+
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -30,7 +36,6 @@
import java.util.concurrent.atomic.AtomicLong;
import org.apache.nifi.NiFiServer;
import org.apache.nifi.bundle.Bundle;
-import org.apache.nifi.minifi.util.MultiSourceMinifiProperties;
import org.apache.nifi.nar.ExtensionMapping;
import org.apache.nifi.nar.NarClassLoaders;
import org.apache.nifi.nar.NarClassLoadersHolder;
@@ -46,6 +51,7 @@
public class MiNiFi {
private static final Logger logger = LoggerFactory.getLogger(MiNiFi.class);
+
private final MiNiFiServer minifiServer;
private volatile boolean shutdown = false;
@@ -155,7 +161,7 @@ protected void shutdownHook(boolean isReload) {
logger.info("MiNiFi server shutdown completed (nicely or otherwise).");
} catch (final Throwable t) {
- logger.warn("Problem occurred ensuring MiNiFi server was properly terminated due to " + t);
+ logger.warn("Problem occurred ensuring MiNiFi server was properly terminated due to {}", t.getMessage());
}
}
@@ -218,10 +224,41 @@ public void run() {
public static void main(String[] args) {
logger.info("Launching MiNiFi...");
try {
- NiFiProperties niFiProperties = MultiSourceMinifiProperties.getInstance();
- new MiNiFi(niFiProperties);
+ NiFiProperties properties = getValidatedMiNifiProperties();
+ new MiNiFi(properties);
} catch (final Throwable t) {
logger.error("Failure to launch MiNiFi due to " + t, t);
}
}
+
+ protected static NiFiProperties getValidatedMiNifiProperties() {
+ NiFiProperties properties = initializeProperties(createBootstrapClassLoader());
+ properties.validate();
+ return properties;
+ }
+
+ private static NiFiProperties initializeProperties(ClassLoader boostrapLoader) {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ String key = getFormattedKey();
+
+ Thread.currentThread().setContextClassLoader(boostrapLoader);
+
+ try {
+ Class> propsLoaderClass = Class.forName("org.apache.nifi.minifi.properties.MiNiFiPropertiesLoader", true, boostrapLoader);
+ Constructor> constructor = propsLoaderClass.getDeclaredConstructor(String.class);
+ Object loaderInstance = constructor.newInstance(key);
+ Method getMethod = propsLoaderClass.getMethod("get");
+ NiFiProperties properties = (NiFiProperties) getMethod.invoke(loaderInstance);
+ logger.info("Application Properties loaded [{}]", properties.size());
+ return properties;
+ } catch (InvocationTargetException wrappedException) {
+ throw new IllegalArgumentException("There was an issue decrypting protected properties", wrappedException.getCause() == null ? wrappedException : wrappedException.getCause());
+ } catch (final IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException reex) {
+ throw new IllegalArgumentException("Unable to access properties loader in the expected manner - apparent classpath or build issue", reex);
+ } catch (final RuntimeException e) {
+ throw new IllegalArgumentException("There was an issue decrypting protected properties", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(contextClassLoader);
+ }
+ }
}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/BootstrapClassLoaderUtils.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/BootstrapClassLoaderUtils.java
new file mode 100644
index 0000000000000..0c7580cc60afe
--- /dev/null
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/BootstrapClassLoaderUtils.java
@@ -0,0 +1,59 @@
+/*
+ * 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.minifi.util;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util for creating class loader with bootstrap libs.
+ */
+public final class BootstrapClassLoaderUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BootstrapClassLoaderUtils.class);
+ private static final String LIB_BOOTSTRAP_DIR = "lib/bootstrap";
+
+ private BootstrapClassLoaderUtils() {
+
+ }
+
+ public static ClassLoader createBootstrapClassLoader() {
+ List urls = new ArrayList<>();
+ try (Stream files = Files.list(Paths.get(LIB_BOOTSTRAP_DIR))) {
+ files.forEach(p -> {
+ try {
+ urls.add(p.toUri().toURL());
+ } catch (MalformedURLException mef) {
+ LOGGER.warn("Unable to load bootstrap library [{}]", p.getFileName(), mef);
+ }
+ });
+ } catch (IOException ioe) {
+ LOGGER.warn("Unable to access lib/bootstrap to create bootstrap classloader", ioe);
+ }
+ return new URLClassLoader(urls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader());
+ }
+}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/MultiSourceMinifiProperties.java b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/MultiSourceMinifiProperties.java
deleted file mode 100644
index 95096b8bf7813..0000000000000
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/minifi-runtime/src/main/java/org/apache/nifi/minifi/util/MultiSourceMinifiProperties.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.minifi.util;
-
-import static org.apache.nifi.minifi.commons.utils.PropertyUtil.resolvePropertyValue;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.util.Properties;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.apache.nifi.util.NiFiProperties;
-
-/**
- * Extends NiFi properties functionality with System and Environment property override possibility. The property resolution also works with
- * dots and hyphens that are not supported in some shells.
- */
-public class MultiSourceMinifiProperties extends NiFiProperties {
-
- private final Properties properties = new Properties();
-
- public static MultiSourceMinifiProperties getInstance() {
- return new MultiSourceMinifiProperties();
- }
-
- private MultiSourceMinifiProperties() {
- readFromPropertiesFile();
- }
-
- @Override
- public Set getPropertyKeys() {
- return Stream.of(System.getProperties().stringPropertyNames(), System.getenv().keySet(), properties.stringPropertyNames())
- .flatMap(Set::stream)
- .collect(Collectors.toSet());
- }
-
- @Override
- public int size() {
- return getPropertyKeys().size();
- }
-
- @Override
- public String getProperty(String key) {
- return resolvePropertyValue(key, System.getProperties())
- .or(() -> resolvePropertyValue(key, System.getenv()))
- .orElseGet(() -> properties.getProperty(key));
- }
-
- private void readFromPropertiesFile() {
- String propertiesFilePath = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH);
-
- if (propertiesFilePath != null) {
- File propertiesFile = new File(propertiesFilePath.trim());
-
- if (!propertiesFile.exists()) {
- throw new RuntimeException("Properties file doesn't exist '" + propertiesFile.getAbsolutePath() + "'");
- }
-
- if (!propertiesFile.canRead()) {
- throw new RuntimeException("Properties file exists but cannot be read '" + propertiesFile.getAbsolutePath() + "'");
- }
-
- try (InputStream inStream = new BufferedInputStream(new FileInputStream(propertiesFile))) {
- properties.load(inStream);
- } catch (Exception ex) {
- throw new RuntimeException("Cannot load properties file due to " + ex.getLocalizedMessage(), ex);
- }
- }
- }
-}
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
index ff95f02a9d65f..8beb586c7832b 100644
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/minifi-framework/pom.xml
@@ -30,6 +30,7 @@ limitations under the License.
minifi-runtimeminifi-resourcesminifi-server
+ minifi-properties-loader
diff --git a/minifi/minifi-nar-bundles/minifi-framework-bundle/pom.xml b/minifi/minifi-nar-bundles/minifi-framework-bundle/pom.xml
index 2369df008f75b..cc6fda3dc0013 100644
--- a/minifi/minifi-nar-bundles/minifi-framework-bundle/pom.xml
+++ b/minifi/minifi-nar-bundles/minifi-framework-bundle/pom.xml
@@ -71,8 +71,7 @@ limitations under the License.
org.apache.nifi
- nifi-properties-loader
- 2.0.0-SNAPSHOT
+ minifi-properties-loaderorg.apache.nifi
@@ -80,6 +79,16 @@ limitations under the License.
2.0.0-SNAPSHOTtest
+
+ org.apache.nifi
+ nifi-property-protection-loader
+ 2.0.0-SNAPSHOT
+
+
+ org.apache.nifi
+ nifi-property-protection-cipher
+ 2.0.0-SNAPSHOT
+
\ No newline at end of file
diff --git a/minifi/minifi-toolkit/minifi-toolkit-assembly/README.md b/minifi/minifi-toolkit/minifi-toolkit-assembly/README.md
index 17fe2f564fdbd..29f8c859b6d49 100644
--- a/minifi/minifi-toolkit/minifi-toolkit-assembly/README.md
+++ b/minifi/minifi-toolkit/minifi-toolkit-assembly/README.md
@@ -19,18 +19,19 @@ MiNiFi is a child project effort of Apache NiFi. The MiNiFi toolkit aids in cre
## Table of Contents
- [Requirements](#requirements)
-- [Getting Started](#getting-started)
+- [MiNiFi Toolkit Converter](#minifi-toolkit-converter)
+- [Encrypting Sensitive Properties in bootstrap.conf](#encrypt-sensitive-properties-in-bootstrapconf)
- [Getting Help](#getting-help)
- [Documentation](#documentation)
- [License](#license)
- [Export Control](#export-control)
## Requirements
-* JRE 1.8
+* JRE 21
-## Getting Started
+The latest version of the MiNiFi Toolkit can be found at https://nifi.apache.org/minifi/download.html under the `MiNiFi Toolkit Binaries` section.
-The latest version of the MiNiFi Toolkit Converter can be found at https://nifi.apache.org/minifi/download.html under the `MiNiFi Toolkit Binaries` section.
+# MiNiFi Toolkit Converter
After downloading the binary and extracting it, to run the MiNiFi Toolkit Converter:
- Change directory to the location where you installed MiNiFi Toolkit and run it and view usage information
@@ -60,6 +61,87 @@ After downloading the binary and extracting it, to run the MiNiFi Toolkit Conver
## Note
It's not guaranteed in all circumstances that the migration will result in a correct flow. For example if a processor's configuration has changed between version, the conversion tool won't be aware of this, and will use the deprecated property names. You will need to fix such issues manually.
+# Encrypting Sensitive Properties in bootstrap.conf
+
+## MiNiFi Encrypt-Config Tool
+The encrypt-config command line tool (invoked in minifi-toolkit as ./bin/encrypt-config.sh or bin\encrypt-config.bat) reads from a bootstrap.conf file with plaintext sensitive configuration values and encrypts each value using a random encryption key. It replaces the plain values with the protected value in the same file, or writes to a new bootstrap.conf file if specified.
+
+The supported encryption algorithm utilized is AES/GCM 256-bit.
+
+### Usage
+To show help:
+
+```
+./bin/encrypt-config.sh -h
+```
+
+The following are the available options:
+* -b, --bootstrapConf Path to file containing Bootstrap Configuration [bootstrap.conf]
+* -B, --outputBootstrapConf Path to output file for Bootstrap Configuration [bootstrap.conf] with root key configured
+* -h, --help Show help message and exit.
+
+### Example
+As an example of how the tool works with the following existing values in the bootstrap.conf file:
+```
+nifi.sensitive.props.key=thisIsABadSensitiveKeyPassword
+nifi.sensitive.props.algorithm=NIFI_PBKDF2_AES_GCM_256
+nifi.sensitive.props.additional.keys=
+
+nifi.security.keystore=/path/to/keystore.jks
+nifi.security.keystoreType=JKS
+nifi.security.keystorePasswd=thisIsABadKeystorePassword
+nifi.security.keyPasswd=thisIsABadKeyPassword
+nifi.security.truststore=
+nifi.security.truststoreType=
+nifi.security.truststorePasswd=
+c2.security.truststore.location=
+c2.security.truststore.password=thisIsABadTruststorePassword
+c2.security.truststore.type=JKS
+c2.security.keystore.location=
+c2.security.keystore.password=thisIsABadKeystorePassword
+c2.security.keystore.type=JKS
+```
+Enter the following arguments when using the tool:
+```
+encrypt-config.sh \
+-b bootstrap.conf \
+```
+As a result, the bootstrap.conf file is overwritten with protected properties and sibling encryption identifiers (aes/gcm/256, the currently supported algorithm):
+```
+nifi.sensitive.props.key=4OjkrFywZb7BlGz4||Tm9pg0jV4TltvVKeiMlm9zBsqmtmYUA2QkzcLKQpspyggtQuhNAkAla5s2695A==
+nifi.sensitive.props.key.protected=aes/gcm/256
+nifi.sensitive.props.algorithm=NIFI_PBKDF2_AES_GCM_256
+nifi.sensitive.props.additional.keys=
+
+nifi.security.keystore=/path/to/keystore.jks
+nifi.security.keystoreType=JKS
+nifi.security.keystorePasswd=iXDmDCadoNJ3VotZ||WvOGbrii4Gk0vr3b6mDstZg+NE0BPZUPk6LVqQlf2Sx3G5XFbUbUYAUz
+nifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.security.keyPasswd=199uUUgpPqB4Fuoo||KckbW7iu+HZf1r4KSMQAFn8NLJK+CnUuayqPsTsdM0Wxou1BHg==
+nifi.security.keyPasswd.protected=aes/gcm/256
+nifi.security.truststore=
+nifi.security.truststoreType=
+nifi.security.truststorePasswd=
+c2.security.truststore.location=
+c2.security.truststore.password=0pHpp+l/WHsDM/sm||fXBvDAQ1BXvNQ8b4EHKa1GspsLx+UD+2EDhph0HbsdmgpVhEv4qj0q5TDo0=
+c2.security.truststore.password.protected=aes/gcm/256
+c2.security.truststore.type=JKS
+c2.security.keystore.location=
+c2.security.keystore.password=j+80L7++RNDf9INQ||RX/QkdVFwRos6Y4XJ8YSUWoI3W5Wx50dyw7HrAA84719SvfxA9eUSDEA
+c2.security.keystore.password.protected=aes/gcm/256
+c2.security.keystore.type=JKS
+```
+
+Additionally, the bootstrap.conf file is updated with the encryption key as follows:
+```
+minifi.bootstrap.sensitive.key=c92623e798be949379d0d18f432a57f1b74732141be321cb4af9ed94aa0ae8ac
+```
+
+Sensitive configuration values are encrypted by the tool by default, however you can encrypt any additional properties, if desired. To encrypt additional properties, specify them as comma-separated values in the minifi.sensitive.props.additional.keys property.
+
+If the bootstrap.conf file already has valid protected values, those property values are not modified by the tool.
+
+
## Getting Help
If you have questions, you can reach out to our mailing list: dev@nifi.apache.org
([archive](https://mail-archives.apache.org/mod_mbox/nifi-dev)).
diff --git a/minifi/minifi-toolkit/minifi-toolkit-assembly/pom.xml b/minifi/minifi-toolkit/minifi-toolkit-assembly/pom.xml
index 7f32bfcd07b79..90c1b1a9a746d 100644
--- a/minifi/minifi-toolkit/minifi-toolkit-assembly/pom.xml
+++ b/minifi/minifi-toolkit/minifi-toolkit-assembly/pom.xml
@@ -62,6 +62,16 @@ limitations under the License.
minifi-toolkit-configuration${project.version}
+
+ org.apache.nifi.minifi
+ minifi-toolkit-encrypt-config
+ ${project.version}
+
+
+ org.slf4j
+ slf4j-api
+ compile
+
diff --git a/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.bat b/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.bat
new file mode 100644
index 0000000000000..589d5a96902e1
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.bat
@@ -0,0 +1,41 @@
+@echo off
+rem
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+rem
+
+rem Use JAVA_HOME if it's set; otherwise, just use java
+
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+set JAVA_EXE=%JAVA_HOME%\bin\java.exe
+goto startConfig
+
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly.
+echo Instead the PATH will be used to find the java executable.
+echo.
+set JAVA_EXE=java
+goto startConfig
+
+:startConfig
+set LIB_DIR=%~dp0..\classpath;%~dp0..\lib
+
+if "%JAVA_OPTS%" == "" set JAVA_OPTS=-Xms128m -Xmx256m
+
+SET JAVA_PARAMS=-cp %LIB_DIR%\* %JAVA_OPTS% org.apache.nifi.minifi.toolkit.config.EncryptConfigCommand
+
+cmd.exe /C ""%JAVA_EXE%" %JAVA_PARAMS% %* ""
+
diff --git a/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.sh b/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.sh
new file mode 100644
index 0000000000000..4aa5e13707ce0
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-assembly/src/main/resources/bin/encrypt-config.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+#
+# 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.
+#
+#
+
+# Script structure inspired from Apache Karaf and other Apache projects with similar startup approaches
+
+SCRIPT_DIR=$(dirname "$0")
+SCRIPT_NAME=$(basename "$0")
+MINIFI_TOOLKIT_HOME=$(cd "${SCRIPT_DIR}" && cd .. && pwd)
+PROGNAME=$(basename "$0")
+
+
+warn() {
+ echo "${PROGNAME}: $*"
+}
+
+die() {
+ warn "$*"
+ exit 1
+}
+
+detectOS() {
+ # OS specific support (must be 'true' or 'false').
+ cygwin=false;
+ aix=false;
+ os400=false;
+ darwin=false;
+ case "$(uname)" in
+ CYGWIN*)
+ cygwin=true
+ ;;
+ AIX*)
+ aix=true
+ ;;
+ OS400*)
+ os400=true
+ ;;
+ Darwin)
+ darwin=true
+ ;;
+ esac
+ # For AIX, set an environment variable
+ if ${aix}; then
+ export LDR_CNTRL=MAXDATA=0xB0000000@DSA
+ echo ${LDR_CNTRL}
+ fi
+}
+
+locateJava() {
+ # Setup the Java Virtual Machine
+ if $cygwin ; then
+ [ -n "${JAVA}" ] && JAVA=$(cygpath --unix "${JAVA}")
+ [ -n "${JAVA_HOME}" ] && JAVA_HOME=$(cygpath --unix "${JAVA_HOME}")
+ fi
+
+ if [ "x${JAVA}" = "x" ] && [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+ if [ "x${JAVA}" = "x" ]; then
+ if [ "x${JAVA_HOME}" != "x" ]; then
+ if [ ! -d "${JAVA_HOME}" ]; then
+ die "JAVA_HOME is not valid: ${JAVA_HOME}"
+ fi
+ JAVA="${JAVA_HOME}/bin/java"
+ else
+ warn "JAVA_HOME not set; results may vary"
+ JAVA=$(type java)
+ JAVA=$(expr "${JAVA}" : '.* \(/.*\)$')
+ if [ "x${JAVA}" = "x" ]; then
+ die "java command not found"
+ fi
+ fi
+ fi
+}
+
+init() {
+ # Determine if there is special OS handling we must perform
+ detectOS
+
+ # Locate the Java VM to execute
+ locateJava
+}
+
+run() {
+ LIBS="${MINIFI_TOOLKIT_HOME}/lib/*"
+
+ sudo_cmd_prefix=""
+ if $cygwin; then
+ MINIFI_TOOLKIT_HOME=$(cygpath --path --windows "${MINIFI_TOOLKIT_HOME}")
+ CLASSPATH="$MINIFI_TOOLKIT_HOME/classpath;$(cygpath --path --windows "${LIBS}")"
+ else
+ CLASSPATH="$MINIFI_TOOLKIT_HOME/classpath:${LIBS}"
+ fi
+
+ export JAVA_HOME="$JAVA_HOME"
+ export MINIFI_TOOLKIT_HOME="$MINIFI_TOOLKIT_HOME"
+
+ umask 0077
+ exec "${JAVA}" -cp "${CLASSPATH}" ${JAVA_OPTS:--Xms128m -Xmx256m} org.apache.nifi.minifi.toolkit.config.EncryptConfigCommand "$@"
+}
+
+
+init
+run "$@"
diff --git a/minifi/minifi-toolkit/minifi-toolkit-configuration/pom.xml b/minifi/minifi-toolkit/minifi-toolkit-configuration/pom.xml
index 4dcbe0730b5ea..f95df2e635028 100644
--- a/minifi/minifi-toolkit/minifi-toolkit-configuration/pom.xml
+++ b/minifi/minifi-toolkit/minifi-toolkit-configuration/pom.xml
@@ -35,10 +35,6 @@ limitations under the License.
minifi-toolkit-schema${project.version}
-
- org.apache.nifi
- nifi-framework-core
- org.apache.nifinifi-property-utils
@@ -48,6 +44,20 @@ limitations under the License.
org.apache.nifinifi-properties
+
+
+ commons-io
+ commons-io
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jaxb-annotations
+
+
+
+ com.fasterxml.jackson.module
+ jackson-module-jakarta-xmlbind-annotations
+
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/pom.xml b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/pom.xml
new file mode 100644
index 0000000000000..fd936cad8d4cb
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/pom.xml
@@ -0,0 +1,57 @@
+
+
+
+
+ org.apache.nifi.minifi
+ minifi-toolkit
+ 2.0.0-SNAPSHOT
+
+ 4.0.0
+
+ minifi-toolkit-encrypt-config
+ Tool to encrypt sensitive configuration values
+
+
+ info.picocli
+ picocli
+ 4.7.5
+
+
+ org.apache.nifi
+ nifi-property-protection-api
+ 2.0.0-SNAPSHOT
+
+
+ org.apache.nifi
+ nifi-property-protection-cipher
+ 2.0.0-SNAPSHOT
+
+
+ org.apache.nifi.minifi
+ minifi-properties-loader
+ 2.0.0-SNAPSHOT
+
+
+ org.slf4j
+ slf4j-api
+ compile
+
+
+ org.apache.nifi.minifi
+ minifi-commons-utils
+
+
+
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/EncryptConfigCommand.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/EncryptConfigCommand.java
new file mode 100644
index 0000000000000..9e9942ac33402
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/EncryptConfigCommand.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.minifi.toolkit.config;
+
+import org.apache.nifi.minifi.toolkit.config.command.MiNiFiEncryptConfig;
+import picocli.CommandLine;
+
+/**
+ * Encrypt Config Command launcher for Command Line implementation
+ */
+public class EncryptConfigCommand {
+
+ /**
+ * Main command method launches Picocli Command Line implementation of Encrypt Config
+ *
+ * @param arguments Command line arguments
+ */
+ public static void main(String[] arguments) {
+ CommandLine commandLine = new CommandLine(new MiNiFiEncryptConfig());
+ if (arguments.length == 0) {
+ commandLine.usage(System.out);
+ } else {
+ int status = commandLine.execute(arguments);
+ System.exit(status);
+ }
+ }
+
+}
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/command/MiNiFiEncryptConfig.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/command/MiNiFiEncryptConfig.java
new file mode 100644
index 0000000000000..f5e6248240206
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/command/MiNiFiEncryptConfig.java
@@ -0,0 +1,146 @@
+/*
+ * 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.minifi.toolkit.config.command;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.HexFormat;
+import java.util.Optional;
+import java.util.Set;
+import org.apache.nifi.minifi.properties.BootstrapProperties;
+import org.apache.nifi.minifi.properties.BootstrapPropertiesLoader;
+import org.apache.nifi.minifi.properties.ProtectedBootstrapProperties;
+import org.apache.nifi.minifi.toolkit.config.transformer.ApplicationPropertiesFileTransformer;
+import org.apache.nifi.minifi.toolkit.config.transformer.BootstrapConfigurationFileTransformer;
+import org.apache.nifi.minifi.toolkit.config.transformer.FileTransformer;
+import org.apache.nifi.properties.AesGcmSensitivePropertyProvider;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Shared Encrypt Configuration for NiFi and NiFi Registry
+ */
+@Command(
+ name = "encrypt-config",
+ sortOptions = false,
+ mixinStandardHelpOptions = true,
+ usageHelpWidth = 160,
+ separator = " ",
+ version = {
+ "Java ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})"
+ },
+ descriptionHeading = "Description: ",
+ description = {
+ "encrypt-config supports protection of sensitive values in Apache MiNiFi"
+ }
+)
+public class MiNiFiEncryptConfig implements Runnable{
+
+ static final String BOOTSTRAP_ROOT_KEY_PROPERTY = "minifi.bootstrap.sensitive.key";
+
+ private static final String WORKING_FILE_NAME_FORMAT = "%s.%d.working";
+ private static final int KEY_LENGTH = 32;
+
+ @Option(
+ names = {"-b", "--bootstrapConf"},
+ description = "Path to file containing Bootstrap Configuration [bootstrap.conf] for optional root key and property protection scheme settings"
+ )
+ Path bootstrapConfPath;
+
+ @Option(
+ names = {"-B", "--outputBootstrapConf"},
+ description = "Path to output file for Bootstrap Configuration [bootstrap.conf] with root key configured"
+ )
+ Path outputBootstrapConf;
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Override
+ public void run() {
+ processBootstrapConf();
+ }
+
+ /**
+ * Process bootstrap.conf writing new Root Key to specified Root Key Property when bootstrap.conf is specified
+ *
+ */
+ protected void processBootstrapConf() {
+ BootstrapProperties unprotectedProperties = BootstrapPropertiesLoader.load(bootstrapConfPath.toFile());
+
+ logger.info("Started processing Bootstrap Configuration [{}]", bootstrapConfPath);
+
+ String newRootKey = getRootKey();
+
+ Set sensitivePropertyNames = new HashSet<>((new ProtectedBootstrapProperties(unprotectedProperties)).getSensitivePropertyKeys());
+ FileTransformer fileTransformer2 = new ApplicationPropertiesFileTransformer(unprotectedProperties, getInputSensitivePropertyProvider(newRootKey), sensitivePropertyNames);
+ runFileTransformer(fileTransformer2, bootstrapConfPath, outputBootstrapConf);
+
+ FileTransformer fileTransformer = new BootstrapConfigurationFileTransformer(BOOTSTRAP_ROOT_KEY_PROPERTY, newRootKey);
+ runFileTransformer(fileTransformer, Optional.ofNullable(outputBootstrapConf).orElse(bootstrapConfPath), outputBootstrapConf);
+ logger.info("Completed processing Bootstrap Configuration [{}]", bootstrapConfPath);
+ }
+
+ private String getRootKey() {
+ SecureRandom secureRandom = new SecureRandom();
+ byte[] sensitivePropertiesKeyBinary = new byte[KEY_LENGTH];
+ secureRandom.nextBytes(sensitivePropertiesKeyBinary);
+ return HexFormat.of().formatHex(sensitivePropertiesKeyBinary);
+ }
+
+ /**
+ * Run File Transformer using working path based on output path
+ *
+ * @param fileTransformer File Transformer to be invoked
+ * @param inputPath Input path of file to be transformed
+ * @param outputPath Output path for transformed file that defaults to the input path when not specified
+ */
+ protected void runFileTransformer(FileTransformer fileTransformer, Path inputPath, Path outputPath) {
+ Path configuredOutputPath = outputPath == null ? inputPath : outputPath;
+ Path workingPath = getWorkingPath(configuredOutputPath);
+ try {
+ fileTransformer.transform(inputPath, workingPath);
+ Files.move(workingPath, configuredOutputPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ String message = String.format("Processing Configuration [%s] failed", inputPath);
+ throw new UncheckedIOException(message, e);
+ }
+ }
+
+
+ /**
+ * Get Input Sensitive Property Provider for decrypting previous values
+ *
+ * @return Input Sensitive Property Provider
+ */
+ protected SensitivePropertyProvider getInputSensitivePropertyProvider(String keyHex) {
+ return new AesGcmSensitivePropertyProvider(keyHex);
+ }
+
+ private Path getWorkingPath(Path resourcePath) {
+ Path fileName = resourcePath.getFileName();
+ String workingFileName = String.format(WORKING_FILE_NAME_FORMAT, fileName, System.currentTimeMillis());
+ return resourcePath.resolveSibling(workingFileName);
+ }
+}
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformer.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformer.java
new file mode 100644
index 0000000000000..5cdc8668dd868
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformer.java
@@ -0,0 +1,152 @@
+/*
+ * 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.minifi.toolkit.config.transformer;
+
+import static org.apache.nifi.properties.ApplicationPropertiesProtector.PROTECTED_KEY_SUFFIX;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.nifi.properties.ApplicationProperties;
+import org.apache.nifi.properties.ApplicationPropertiesProtector;
+import org.apache.nifi.properties.ProtectedPropertyContext;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+
+/**
+ * File Transformer supporting transformation of Application Properties with sensitive property values
+ */
+public class ApplicationPropertiesFileTransformer implements FileTransformer {
+
+ private static final Pattern PROPERTY_VALUE_PATTERN = Pattern.compile("^([^#!][^=]+?)\\s*=\\s?(.+)");
+
+ private static final int NAME_GROUP = 1;
+
+ private static final int VALUE_GROUP = 2;
+
+ private static final char PROPERTY_VALUE_SEPARATOR = '=';
+
+ private final ApplicationProperties applicationProperties;
+
+ private final SensitivePropertyProvider outputSensitivePropertyProvider;
+
+ private final Set sensitivePropertyNames;
+
+ /**
+ * Application Properties File Transformer uses provided Application Properties as the source of protected values
+ *
+ * @param applicationProperties Application Properties containing decrypted source property values
+ * @param outputSensitivePropertyProvider Sensitive Property Provider encrypts specified sensitive property values
+ * @param sensitivePropertyNames Sensitive Property Names marked for encryption
+ */
+ public ApplicationPropertiesFileTransformer(
+ ApplicationProperties applicationProperties,
+ SensitivePropertyProvider outputSensitivePropertyProvider,
+ Set sensitivePropertyNames
+ ) {
+ this.applicationProperties = Objects.requireNonNull(applicationProperties, "Application Properties required");
+ this.outputSensitivePropertyProvider = Objects.requireNonNull(outputSensitivePropertyProvider, "Output Property Provider required");
+ this.sensitivePropertyNames = Objects.requireNonNull(sensitivePropertyNames, "Sensitive Property Names required");
+ }
+
+ /**
+ * Transform input application properties using configured Sensitive Property Provider and write output properties
+ *
+ * @param inputPath Input file path to be transformed containing source application properties
+ * @param outputPath Output file path for protected application properties
+ * @throws IOException Thrown on transformation failures
+ */
+ @Override
+ public void transform(Path inputPath, Path outputPath) throws IOException {
+ Objects.requireNonNull(inputPath, "Input path required");
+ Objects.requireNonNull(outputPath, "Output path required");
+
+ try (BufferedReader reader = Files.newBufferedReader(inputPath);
+ BufferedWriter writer = Files.newBufferedWriter(outputPath)) {
+ transform(reader, writer);
+ }
+ }
+
+ private void transform(BufferedReader reader, BufferedWriter writer) throws IOException {
+ String line = reader.readLine();
+ while (line != null) {
+ Matcher matcher = PROPERTY_VALUE_PATTERN.matcher(line);
+ String nextLine = null;
+ if (matcher.matches()) {
+ nextLine = processPropertyLine(reader, writer, matcher, line);
+ } else {
+ writeNonSensitiveLine(writer, line);
+ }
+
+ line = nextLine == null ? reader.readLine() : nextLine;
+ }
+ }
+
+ private String processPropertyLine(BufferedReader reader, BufferedWriter writer, Matcher matcher, String line) throws IOException {
+ String actualPropertyName = matcher.group(NAME_GROUP);
+ String actualPropertyValue = matcher.group(VALUE_GROUP);
+ String nextLine = null;
+
+ if (!actualPropertyName.endsWith(PROTECTED_KEY_SUFFIX)) {
+ nextLine = reader.readLine();
+ if (sensitivePropertyNames.contains(actualPropertyName) || isNextPropertyProtected(actualPropertyName, nextLine)) {
+ String applicationProperty = applicationProperties.getProperty(actualPropertyName, actualPropertyValue);
+ writeProtectedProperty(writer, actualPropertyName, applicationProperty);
+ } else {
+ writeNonSensitiveLine(writer, line);
+ }
+ }
+ return nextLine;
+ }
+
+ private void writeNonSensitiveLine(BufferedWriter writer, String line) throws IOException {
+ writer.write(line);
+ writer.newLine();
+ }
+
+ private boolean isNextPropertyProtected(String actualPropertyName, String nextLine) {
+ if (nextLine != null) {
+ Matcher nextLineMatcher = PROPERTY_VALUE_PATTERN.matcher(nextLine);
+ if (nextLineMatcher.matches()) {
+ String protectedActualPropertyName = ApplicationPropertiesProtector.getProtectionKey(actualPropertyName);
+ String nextName = nextLineMatcher.group(NAME_GROUP);
+ return protectedActualPropertyName.equals(nextName);
+ }
+ }
+ return false;
+ }
+
+ private void writeProtectedProperty(BufferedWriter writer, String name, String value) throws IOException {
+ ProtectedPropertyContext propertyContext = ProtectedPropertyContext.defaultContext(name);
+ String protectedValue = outputSensitivePropertyProvider.protect(value, propertyContext);
+
+ writer.write(name);
+ writer.write(PROPERTY_VALUE_SEPARATOR);
+ writeNonSensitiveLine(writer, protectedValue);
+
+ String protectedName = ApplicationPropertiesProtector.getProtectionKey(name);
+ writer.write(protectedName);
+ writer.write(PROPERTY_VALUE_SEPARATOR);
+ String protectionIdentifierKey = outputSensitivePropertyProvider.getIdentifierKey();
+ writeNonSensitiveLine(writer, protectionIdentifierKey);
+ }
+}
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformer.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformer.java
new file mode 100644
index 0000000000000..8325c1ef70efd
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformer.java
@@ -0,0 +1,108 @@
+/*
+ * 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.minifi.toolkit.config.transformer;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * File Transformer supporting Bootstrap Configuration with updated Root Key
+ */
+public class BootstrapConfigurationFileTransformer implements FileTransformer {
+
+ private static final Pattern PROPERTY_VALUE_PATTERN = Pattern.compile("^([^#!][^=]+?)\\s*=.*");
+
+ private static final int NAME_GROUP = 1;
+
+ private static final char PROPERTY_VALUE_SEPARATOR = '=';
+
+ private final String rootKeyPropertyName;
+
+ private final String rootKey;
+
+ /**
+ * Bootstrap Configuration File Transformer writes provided Root Key to output files
+ *
+ * @param rootKeyPropertyName Root Key property name to be written
+ * @param rootKey Root Key to be written
+ */
+ public BootstrapConfigurationFileTransformer(String rootKeyPropertyName, String rootKey) {
+ this.rootKeyPropertyName = Objects.requireNonNull(rootKeyPropertyName, "Root Key Property Name required");
+ this.rootKey = Objects.requireNonNull(rootKey, "Root Key required");
+ }
+
+ /**
+ * Transform input configuration and write Root Key to output location
+ *
+ * @param inputPath Input file path to be transformed containing Bootstrap Configuration
+ * @param outputPath Output file path for updated configuration
+ * @throws IOException Thrown on transformation failures
+ */
+ @Override
+ public void transform(Path inputPath, Path outputPath) throws IOException {
+ Objects.requireNonNull(inputPath, "Input path required");
+ Objects.requireNonNull(outputPath, "Output path required");
+
+ try (BufferedReader reader = Files.newBufferedReader(inputPath);
+ BufferedWriter writer = Files.newBufferedWriter(outputPath)) {
+ transform(reader, writer);
+ }
+ }
+
+ private void transform(BufferedReader reader, BufferedWriter writer) throws IOException {
+ boolean rootKeyPropertyNotFound = true;
+
+ String line = reader.readLine();
+ while (line != null) {
+ Matcher matcher = PROPERTY_VALUE_PATTERN.matcher(line);
+ if (matcher.matches()) {
+ String name = matcher.group(NAME_GROUP);
+
+ if (rootKeyPropertyName.equals(name)) {
+ writeRootKey(writer);
+ rootKeyPropertyNotFound = false;
+ } else {
+ writer.write(line);
+ writer.newLine();
+ }
+ } else {
+ writer.write(line);
+ writer.newLine();
+ }
+
+ line = reader.readLine();
+ }
+
+ if (rootKeyPropertyNotFound) {
+ writer.newLine();
+ writeRootKey(writer);
+ }
+ }
+
+ private void writeRootKey(BufferedWriter writer) throws IOException {
+ writer.write(rootKeyPropertyName);
+ writer.write(PROPERTY_VALUE_SEPARATOR);
+ writer.write(rootKey);
+ writer.newLine();
+ }
+}
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/FileTransformer.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/FileTransformer.java
new file mode 100644
index 0000000000000..ecc05a43b649c
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/main/java/org/apache/nifi/minifi/toolkit/config/transformer/FileTransformer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.minifi.toolkit.config.transformer;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+/**
+ * Abstraction for transforming Files
+ */
+public interface FileTransformer {
+ /**
+ * Transform input file and write contents to output file path
+ *
+ * @param inputPath Input file path to be transformed
+ * @param outputPath Output file path
+ * @throws IOException Thrown on input or output processing failures
+ */
+ void transform(Path inputPath, Path outputPath) throws IOException;
+}
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformerTest.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformerTest.java
new file mode 100644
index 0000000000000..11bf6b01066a3
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/ApplicationPropertiesFileTransformerTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.minifi.toolkit.config.transformer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+import java.util.Set;
+import org.apache.nifi.properties.ApplicationProperties;
+import org.apache.nifi.properties.SensitivePropertyProvider;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class ApplicationPropertiesFileTransformerTest {
+
+ private static final String UNPROTECTED_BOOTSTRAP_CONF = "/transformer/unprotected.conf";
+ private static final String PROTECTED_BOOTSTRAP_PROPERTIES = "/transformer/protected.conf";
+ private static final String PROPERTIES_TRANSFORMED = "transformed.conf";
+ private static final String SENSITIVE_PROPERTY_NAME_1 = "property1";
+ private static final String SENSITIVE_PROPERTY_NAME_2 = "property2";
+ private static final String SENSITIVE_PROPERTY_NAME_3 = "property3";
+ private static final String PROVIDER_IDENTIFIER_KEY = "mocked-provider";
+ private static final String UNPROTECTED = "UNPROTECTED";
+ private static final String ENCRYPTED = "ENCRYPTED";
+
+ private static final Set SENSITIVE_PROPERTY_NAMES = Set.of(SENSITIVE_PROPERTY_NAME_1, SENSITIVE_PROPERTY_NAME_2, SENSITIVE_PROPERTY_NAME_3);
+
+ @TempDir
+ private Path tempDir;
+
+ @Mock
+ private SensitivePropertyProvider sensitivePropertyProvider;
+
+ @Test
+ void shouldTransformProperties() throws URISyntaxException, IOException {
+ Path propertiesPath = getResourcePath(UNPROTECTED_BOOTSTRAP_CONF);
+
+ Path tempPropertiesPath = tempDir.resolve(propertiesPath.getFileName());
+ Files.copy(propertiesPath, tempPropertiesPath);
+
+ Path outputPropertiesPath = tempDir.resolve(PROPERTIES_TRANSFORMED);
+ ApplicationProperties applicationProperties = new ApplicationProperties(Map.of(SENSITIVE_PROPERTY_NAME_3, UNPROTECTED));
+ FileTransformer transformer = new ApplicationPropertiesFileTransformer(applicationProperties, sensitivePropertyProvider, SENSITIVE_PROPERTY_NAMES);
+
+ when(sensitivePropertyProvider.getIdentifierKey()).thenReturn(PROVIDER_IDENTIFIER_KEY);
+ when(sensitivePropertyProvider.protect(eq(UNPROTECTED), any())).thenReturn(ENCRYPTED);
+
+ transformer.transform(tempPropertiesPath, outputPropertiesPath);
+
+ Properties expectedProperties = loadProperties(getResourcePath(PROTECTED_BOOTSTRAP_PROPERTIES));
+ Properties resultProperties = loadProperties(outputPropertiesPath);
+
+ assertEquals(expectedProperties, resultProperties);
+ }
+
+ private Properties loadProperties(Path resourcePath) throws IOException {
+ Properties properties = new Properties();
+ try (InputStream propertiesStream = Files.newInputStream(resourcePath)) {
+ properties.load(propertiesStream);
+ }
+ return properties;
+ }
+
+ private Path getResourcePath(String resource) throws URISyntaxException {
+ final URL resourceUrl = Objects.requireNonNull(getClass().getResource(resource), String.format("Resource [%s] not found", resource));
+ return Paths.get(resourceUrl.toURI());
+ }
+}
\ No newline at end of file
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformerTest.java b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformerTest.java
new file mode 100644
index 0000000000000..cd05132aef002
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/java/org/apache/nifi/minifi/toolkit/config/transformer/BootstrapConfigurationFileTransformerTest.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.minifi.toolkit.config.transformer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.Properties;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class BootstrapConfigurationFileTransformerTest {
+ private static final String BOOTSTRAP_ROOT_KEY_PROPERTY = "minifi.bootstrap.sensitive.key";
+ private static final String MOCK_KEY = "mockKey";
+ private static final String BOOTSTRAP_CONF_FILE_WITHOUT_KEY = "/transformer/bootstrap_without_key.conf";
+ private static final String BOOTSTRAP_CONF_TRANSFORMED= "transformed.conf";
+
+ @TempDir
+ private Path tempDir;
+
+ @Test
+ void shouldWriteRootPropertyKeyIfItIsNotPresent() throws URISyntaxException, IOException {
+ Path propertiesPath = getResourcePath(BOOTSTRAP_CONF_FILE_WITHOUT_KEY);
+
+ Path tempPropertiesPath = tempDir.resolve(propertiesPath.getFileName());
+ Files.copy(propertiesPath, tempPropertiesPath);
+
+ Path outputPropertiesPath = tempDir.resolve(BOOTSTRAP_CONF_TRANSFORMED);
+
+ BootstrapConfigurationFileTransformer transformer = new BootstrapConfigurationFileTransformer(BOOTSTRAP_ROOT_KEY_PROPERTY, MOCK_KEY);
+ transformer.transform(tempPropertiesPath, outputPropertiesPath);
+
+ Properties properties = loadProperties(outputPropertiesPath);
+ assertEquals(MOCK_KEY, properties.get(BOOTSTRAP_ROOT_KEY_PROPERTY));
+ }
+
+ private Properties loadProperties(Path resourcePath) throws IOException {
+ Properties properties = new Properties();
+ try (InputStream propertiesStream = Files.newInputStream(resourcePath)) {
+ properties.load(propertiesStream);
+ }
+ return properties;
+ }
+
+ private Path getResourcePath(String resource) throws URISyntaxException {
+ final URL resourceUrl = Objects.requireNonNull(getClass().getResource(resource), String.format("Resource [%s] not found", resource));
+ return Paths.get(resourceUrl.toURI());
+ }
+}
\ No newline at end of file
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/bootstrap_without_key.conf b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/bootstrap_without_key.conf
new file mode 100644
index 0000000000000..ae1e83eeb3d49
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/bootstrap_without_key.conf
@@ -0,0 +1,14 @@
+# 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.
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/protected.conf b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/protected.conf
new file mode 100644
index 0000000000000..573f76b514abe
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/protected.conf
@@ -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.
+
+property1=ENCRYPTED
+property1.protected=mocked-provider
+property2=ENCRYPTED
+property2.protected=mocked-provider
+nonsensitive.property=value
+property3=ENCRYPTED
+property3.protected=mocked-provider
\ No newline at end of file
diff --git a/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/unprotected.conf b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/unprotected.conf
new file mode 100644
index 0000000000000..5efa9a484d0fb
--- /dev/null
+++ b/minifi/minifi-toolkit/minifi-toolkit-encrypt-config/src/test/resources/transformer/unprotected.conf
@@ -0,0 +1,20 @@
+# 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.
+
+property1=UNPROTECTED
+property2=UNPROTECTED
+nonsensitive.property=value
+property3=ENCRYPTED
+property3.protected=mocked-provider
\ No newline at end of file
diff --git a/minifi/minifi-toolkit/pom.xml b/minifi/minifi-toolkit/pom.xml
index f625769a64527..175f7d8d51221 100644
--- a/minifi/minifi-toolkit/pom.xml
+++ b/minifi/minifi-toolkit/pom.xml
@@ -29,5 +29,13 @@ limitations under the License.
minifi-toolkit-schemaminifi-toolkit-configurationminifi-toolkit-assembly
+ minifi-toolkit-encrypt-config
+
+
+
+ org.slf4j
+ slf4j-simple
+
+
diff --git a/minifi/pom.xml b/minifi/pom.xml
index aa13d86aa9590..6178e865e18ce 100644
--- a/minifi/pom.xml
+++ b/minifi/pom.xml
@@ -86,6 +86,11 @@ limitations under the License.
minifi-resources2.0.0-SNAPSHOT
+
+ org.apache.nifi.minifi
+ minifi-properties-loader
+ 2.0.0-SNAPSHOT
+ org.apache.nifi.minifiminifi-provenance-repository-nar
diff --git a/nifi-api/pom.xml b/nifi-api/pom.xml
index 92265155f22d6..a45c9e1354d6b 100644
--- a/nifi-api/pom.xml
+++ b/nifi-api/pom.xml
@@ -34,7 +34,7 @@
-->
- io.swagger
+ io.swagger.core.v3swagger-annotationscompiletrue
diff --git a/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java b/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java
index b8303ae122c4d..3a76683274521 100644
--- a/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java
+++ b/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java
@@ -43,9 +43,8 @@ public AllowableValue(final String value) {
* Constructs a new AllowableValue with the given value and display name and
* no description
*
- * @param value that is allowed
+ * @param value that is allowed
* @param displayName to display for the value
- *
* @throws NullPointerException if either argument is null
*/
public AllowableValue(final String value, final String displayName) {
@@ -56,10 +55,9 @@ public AllowableValue(final String value, final String displayName) {
* Constructs a new AllowableValue with the given value, display name, and
* description
*
- * @param value that is valid
+ * @param value that is valid
* @param displayName to show for the value
* @param description of the value
- *
* @throws NullPointerException if identifier or value is null
*/
public AllowableValue(final String value, final String displayName, final String description) {
@@ -68,6 +66,14 @@ public AllowableValue(final String value, final String displayName, final String
this.description = description;
}
+ public static AllowableValue fromDescribedValue(final DescribedValue describedValue) {
+ if (describedValue instanceof AllowableValue allowableValue) {
+ return allowableValue;
+ }
+
+ return new AllowableValue(describedValue.getValue(), describedValue.getDisplayName(), describedValue.getDescription());
+ }
+
/**
* @return the value of this AllowableValue
*/
@@ -106,8 +112,7 @@ public boolean equals(final Object obj) {
return true;
}
- if (obj instanceof AllowableValue) {
- final AllowableValue other = (AllowableValue) obj;
+ if (obj instanceof AllowableValue other) {
return (this.value.equals(other.getValue()));
} else if (obj instanceof String) {
return this.value.equals(obj);
diff --git a/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java b/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
index 1d039058f4413..35d0a8f131c50 100644
--- a/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
+++ b/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
@@ -19,9 +19,9 @@
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceDefinition;
import org.apache.nifi.components.resource.ResourceReference;
-import org.apache.nifi.components.resource.StandardResourceReferenceFactory;
import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.components.resource.StandardResourceDefinition;
+import org.apache.nifi.components.resource.StandardResourceReferenceFactory;
import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.expression.ExpressionLanguageScope;
@@ -38,7 +38,6 @@
/**
* An immutable object for holding information about a type of component
* property.
- *
*/
public final class PropertyDescriptor implements Comparable {
@@ -123,15 +122,15 @@ protected PropertyDescriptor(final Builder builder) {
this.name = builder.name;
this.description = builder.description;
this.defaultValue = builder.defaultValue;
- this.allowableValues = builder.allowableValues == null ? null : Collections.unmodifiableList(new ArrayList<>(builder.allowableValues));
+ this.allowableValues = builder.allowableValues == null ? null : List.copyOf(builder.allowableValues);
this.required = builder.required;
this.sensitive = builder.sensitive;
this.dynamic = builder.dynamic;
this.dynamicallyModifiesClasspath = builder.dynamicallyModifiesClasspath;
this.expressionLanguageScope = builder.expressionLanguageScope;
this.controllerServiceDefinition = builder.controllerServiceDefinition;
- this.validators = Collections.unmodifiableList(new ArrayList<>(builder.validators));
- this.dependencies = builder.dependencies == null ? Collections.emptySet() : Collections.unmodifiableSet(new HashSet<>(builder.dependencies));
+ this.validators = List.copyOf(builder.validators);
+ this.dependencies = builder.dependencies == null ? Collections.emptySet() : Set.copyOf(builder.dependencies);
this.resourceDefinition = builder.resourceDefinition;
}
@@ -148,7 +147,7 @@ public int compareTo(final PropertyDescriptor o) {
* If this descriptor has a set of allowable values then the given value is
* only checked against the allowable values.
*
- * @param input the value to validate
+ * @param input the value to validate
* @param context the context of validation
* @return the result of validating the input
*/
@@ -188,15 +187,15 @@ public ValidationResult validate(final String input, final ValidationContext con
final ControllerService service = context.getControllerServiceLookup().getControllerService(input);
if (service == null) {
return new ValidationResult.Builder()
- .input(input)
- .subject(getDisplayName())
- .valid(false)
- .explanation("Property references a Controller Service that does not exist")
- .build();
+ .input(input)
+ .subject(getDisplayName())
+ .valid(false)
+ .explanation("Property references a Controller Service that does not exist")
+ .build();
} else {
return new ValidationResult.Builder()
- .valid(true)
- .build();
+ .valid(true)
+ .build();
}
}
@@ -293,15 +292,13 @@ public Builder description(final String description) {
}
/**
- * Specifies the initial value and the default value that will be used
- * if the user does not specify a value. When {@link #build()} is
- * called, if Allowable Values have been set (see
- * {@link #allowableValues(AllowableValue...)}) and this value is not
- * one of those Allowable Values, an Exception will be thrown. If the
- * Allowable Values have been set using the
- * {@link #allowableValues(AllowableValue...)} method, the default value
- * should be set to the "Value" of the {@link AllowableValue} object
- * (see {@link AllowableValue#getValue()}).
+ * Specifies the initial value and the default value that will be used if the user does not specify a value.
+ *
+ * When {@link #build()} is called, if Allowable Values have been set (see {@link #allowableValues(DescribedValue...)} and overloads)
+ * and this value is not one of those Allowable Values, an Exception will be thrown.
+ * If the Allowable Values have been set, the default value should be set to
+ * the "Value" of the {@link DescribedValue} object (see {@link DescribedValue#getValue()}).
+ * There's an overload available for this (see {@link #defaultValue(DescribedValue)}).
*
* @param value default value
* @return the builder
@@ -314,15 +311,12 @@ public Builder defaultValue(final String value) {
}
/**
- * Specifies the initial value and the default value that will be used
- * if the user does not specify a value. When {@link #build()} is
- * called, if Allowable Values have been set (see
- * {@link #allowableValues(AllowableValue...)})
- * and the "Value" of the {@link DescribedValue} object is not
- * the "Value" of one of those Allowable Values, an Exception will be thrown.
- * If the Allowable Values have been set using the
- * {@link #allowableValues(AllowableValue...)} method, the default value
- * should be set providing the {@link AllowableValue} to this method.
+ * Specifies the initial value and the default value that will be used if the user does not specify a value.
+ *
+ * Sets the default value to the "Value" of the {@link DescribedValue} object.
+ * When {@link #build()} is called, if Allowable Values have been set (see {@link #allowableValues(DescribedValue...)} and overloads)
+ * and this value is not one of those Allowable Values, an Exception will be thrown.
+ * In case there is not a restricted set of Allowable Values {@link #defaultValue(String)} may be used.
*
* @param value default value holder
* @return the builder
@@ -331,6 +325,16 @@ public Builder defaultValue(final DescribedValue value) {
return defaultValue(value != null ? value.getValue() : null);
}
+ /**
+ * Clears the initial value and default value from this Property.
+ *
+ * @return the builder
+ */
+ public Builder clearDefaultValue() {
+ this.defaultValue = null;
+ return this;
+ }
+
public Builder dynamic(final boolean dynamic) {
this.dynamic = dynamic;
return this;
@@ -342,17 +346,17 @@ public Builder dynamic(final boolean dynamic) {
* libraries for the given component.
*
* NOTE: If a component contains a PropertyDescriptor where dynamicallyModifiesClasspath is set to true,
- * the component may also be annotated with @RequiresInstanceClassloading, in which case every class will
- * be loaded by a separate InstanceClassLoader for each processor instance.
- * It also allows to load native libraries from the extra classpath.
- *
- * One can chose to omit the annotation. In this case the loading of native libraries from the extra classpath
- * is not supported.
- * Also by default, classes will be loaded by a common NarClassLoader, however it's possible to acquire an
- * InstanceClassLoader by calling Thread.currentThread().getContextClassLoader() which can be used manually
- * to load required classes on an instance-by-instance basis
- * (by calling {@link Class#forName(String, boolean, ClassLoader)} for example).
- *
+ * the component may also be annotated with @RequiresInstanceClassloading, in which case every class will
+ * be loaded by a separate InstanceClassLoader for each processor instance.
+ * It also allows to load native libraries from the extra classpath.
+ *
+ * One can choose to omit the annotation. In this case the loading of native libraries from the extra classpath
+ * is not supported.
+ * Also by default, classes will be loaded by a common NarClassLoader, however it's possible to acquire an
+ * InstanceClassLoader by calling Thread.currentThread().getContextClassLoader() which can be used manually
+ * to load required classes on an instance-by-instance basis
+ * (by calling {@link Class#forName(String, boolean, ClassLoader)} for example).
+ *
* Any property descriptor that dynamically modifies the classpath should also make use of the {@link #identifiesExternalResource(ResourceCardinality, ResourceType, ResourceType...)} method
* to indicate that the property descriptor references external resources and optionally restrict which types of resources and how many resources the property allows.
*
@@ -365,92 +369,110 @@ public Builder dynamicallyModifiesClasspath(final boolean dynamicallyModifiesCla
}
/**
- * @param values contrained set of values
+ * Sets the Allowable Values for this Property.
+ *
+ * @param values constrained set of values
* @return the builder
*/
public Builder allowableValues(final Set values) {
if (null != values) {
- this.allowableValues = new ArrayList<>();
-
- for (final String value : values) {
- this.allowableValues.add(new AllowableValue(value, value));
- }
+ this.allowableValues = values.stream().map(AllowableValue::new).toList();
}
return this;
}
+ /**
+ * Sets the Allowable Values for this Property.
+ *
+ * Uses the {@link Enum#name()} of each value as "Value" for the {@link AllowableValue}.
+ * In case the enum value is a {@link DescribedValue}, uses the information provided instead
+ * (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
+ *
+ * @param values constrained set of values
+ * @return the builder
+ */
public > Builder allowableValues(final E[] values) {
if (null != values) {
- this.allowableValues = new ArrayList<>();
- for (final E value : values) {
- allowableValues.add(new AllowableValue(value.name(), value.name()));
- }
+ this.allowableValues = Arrays.stream(values)
+ .map(enumValue -> enumValue instanceof DescribedValue describedValue
+ ? AllowableValue.fromDescribedValue(describedValue) : new AllowableValue(enumValue.name()))
+ .toList();
}
return this;
}
/**
- * Stores allowable values from an enum class.
- * @param enumClass an enum class that implements the DescribedValue interface and contains a set of values
- * @param generic parameter for an enum class that implements the DescribedValue interface
+ * Sets the Allowable Values for this Property.
+ *
+ * Uses the {@link Enum#name()} of each value from {@link Class#getEnumConstants()} as "Value" for the {@link AllowableValue}.
+ * In case the enum value is a {@link DescribedValue}, uses the information provided instead
+ * (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
+ *
+ * @param enumClass an enum class that contains a set of values and optionally implements the DescribedValue interface
+ * @param generic parameter for an enum class, that may implement the DescribedValue interface
* @return the builder
*/
- public & DescribedValue> Builder allowableValues(final Class enumClass) {
- this.allowableValues = new ArrayList<>();
- for (E enumValue : enumClass.getEnumConstants()) {
- this.allowableValues.add(new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription()));
- }
- return this;
+ public > Builder allowableValues(final Class enumClass) {
+ return allowableValues(enumClass.getEnumConstants());
}
/**
- * Stores allowable values from a set of enum values.
- * @param enumValues a set of enum values that implements the DescribedValue interface
- * @param generic parameter for the enum values' class that implements the DescribedValue interface
+ * Sets the Allowable Values for this Property.
+ *
+ * Uses the {@link Enum#name()} of each value of the {@link EnumSet} as "Value" for the {@link AllowableValue}.
+ * In case the enum value is a {@link DescribedValue}, uses the information provided instead
+ * (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()}, {@link DescribedValue#getDescription()}).
+ *
+ * @param enumValues an enum set that contains a set of values and optionally implements the DescribedValue interface
+ * @param generic parameter for an enum class, that may implement the DescribedValue interface
* @return the builder
*/
- public & DescribedValue> Builder allowableValues(final EnumSet enumValues) {
- this.allowableValues = new ArrayList<>();
- for (E enumValue : enumValues) {
- this.allowableValues.add(new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription()));
+ public > Builder allowableValues(final EnumSet enumValues) {
+ if (null != enumValues) {
+ this.allowableValues = enumValues.stream()
+ .map(enumValue -> enumValue instanceof DescribedValue describedValue
+ ? AllowableValue.fromDescribedValue(describedValue) : new AllowableValue(enumValue.name()))
+ .toList();
}
return this;
}
/**
+ * Sets the Allowable Values for this Property.
+ *
* @param values constrained set of values
* @return the builder
*/
public Builder allowableValues(final String... values) {
if (null != values) {
- this.allowableValues = new ArrayList<>();
- for (final String value : values) {
- allowableValues.add(new AllowableValue(value, value));
- }
+ this.allowableValues = Arrays.stream(values).map(AllowableValue::new).toList();
}
return this;
}
/**
- * Clears all Allowable Values from this Property
+ * Sets the Allowable Values for this Property.
+ *
+ * Uses the information provided by each {@link DescribedValue} (see {@link DescribedValue#getValue()}, {@link DescribedValue#getDisplayName()},
+ * {@link DescribedValue#getDescription()}) to populate the {@link AllowableValue}s.
*
+ * @param values constrained set of values
* @return the builder
*/
- public Builder clearAllowableValues() {
- this.allowableValues = null;
+ public Builder allowableValues(final DescribedValue... values) {
+ if (null != values) {
+ this.allowableValues = Arrays.stream(values).map(AllowableValue::fromDescribedValue).toList();
+ }
return this;
}
/**
- * Sets the Allowable Values for this Property
+ * Clears all Allowable Values from this Property
*
- * @param values contrained set of values
* @return the builder
*/
- public Builder allowableValues(final AllowableValue... values) {
- if (null != values) {
- this.allowableValues = Arrays.asList(values);
- }
+ public Builder clearAllowableValues() {
+ this.allowableValues = null;
return this;
}
@@ -498,7 +520,7 @@ public Builder clearValidators() {
* Service that implements the given interface
*
* @param controllerServiceDefinition the interface that is implemented
- * by the Controller Service
+ * by the Controller Service
* @return the builder
*/
public Builder identifiesControllerService(final Class extends ControllerService> controllerServiceDefinition) {
@@ -533,12 +555,12 @@ private boolean isValueAllowed(final String value) {
*
*
If the ResourceCardinality is MULTIPLE, the given property value may consist of one or more resources, each separted by a comma and optional white space.
*
- *
+ *
* Generally, any property descriptor that makes use of the {@link #dynamicallyModifiesClasspath(boolean)} method to dynamically update its classpath should also
* make use of this method, specifying which types of resources are allowed and how many.
*
- * @param cardinality specifies how many resources the property should allow
- * @param resourceType the type of resource that is allowed
+ * @param cardinality specifies how many resources the property should allow
+ * @param resourceType the type of resource that is allowed
* @param additionalResourceTypes if more than one type of resource is allowed, any resource type in addition to the given resource type may be provided
* @return the builder
*/
@@ -558,15 +580,15 @@ public Builder identifiesExternalResource(final ResourceCardinality cardinality,
* 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.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
- *
+ *
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant.
- *
+ *
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
*
- * @param property the property that must be set in order for this property to become relevant
+ * @param property the property that must be set in order for this property to become relevant
* @param dependentValues the possible values for the given property for which this Property is relevant
* @return the builder
*/
@@ -593,16 +615,16 @@ public Builder dependsOn(final PropertyDescriptor property, final AllowableValue
* Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given
* String arguments.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
- *
+ *
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Allowable Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant.
- *
+ *
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
*
- * @param property the property that must be set in order for this property to become relevant
- * @param firstDependentValue the first value for the given property for which this Property is relevant
+ * @param property the property that must be set in order for this property to become relevant
+ * @param firstDependentValue the first value for the given property for which this Property is relevant
* @param additionalDependentValues any other values for the given property for which this Property is relevant
* @return the builder
*/
@@ -621,25 +643,25 @@ public Builder dependsOn(final PropertyDescriptor property, final String firstDe
* Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given
* {@link DescribedValue} arguments.
* If this method is called multiple times, each with a different dependency, then a relationship is established such that this Property is relevant only if all dependencies are satisfied.
- *
+ *
* In the case that this property is NOT considered to be relevant (meaning that it depends on a property whose value is not specified, or whose value does not match one of the given
* Described Values), the property will not be shown in the component's configuration in the User Interface. Additionally, this property's value will not be considered for
* validation. That is, if this property is configured with an invalid value and this property depends on Property Foo, and Property Foo does not have a value set, then the component
* will still be valid, because the value of this property is irrelevant.
- *
+ *
* If the given property is not relevant (because its dependencies are not satisfied), this property is also considered not to be valid.
*
- * @param property the property that must be set in order for this property to become relevant
- * @param firstDependentValue the first value for the given property for which this Property is relevant
+ * @param property the property that must be set in order for this property to become relevant
+ * @param firstDependentValue the first value for the given property for which this Property is relevant
* @param additionalDependentValues any other values for the given property for which this Property is relevant
* @return the builder
*/
public Builder dependsOn(final PropertyDescriptor property, final DescribedValue firstDependentValue, final DescribedValue... additionalDependentValues) {
final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1];
- dependentValues[0] = toAllowableValue(firstDependentValue);
+ dependentValues[0] = AllowableValue.fromDescribedValue(firstDependentValue);
int i = 1;
for (final DescribedValue additionalDependentValue : additionalDependentValues) {
- dependentValues[i++] = toAllowableValue(additionalDependentValue);
+ dependentValues[i++] = AllowableValue.fromDescribedValue(additionalDependentValue);
}
return dependsOn(property, dependentValues);
@@ -655,16 +677,11 @@ public Builder clearDependsOn() {
return this;
}
- private AllowableValue toAllowableValue(DescribedValue describedValue) {
- return new AllowableValue(describedValue.getValue(), describedValue.getDisplayName(), describedValue.getDescription());
- }
-
/**
* @return a PropertyDescriptor as configured
- *
* @throws IllegalStateException if allowable values are configured but
- * no default value is set, or the default value is not contained within
- * the allowable values.
+ * no default value is set, or the default value is not contained within
+ * the allowable values.
*/
public PropertyDescriptor build() {
if (name == null) {
@@ -740,18 +757,14 @@ public ResourceDefinition getResourceDefinition() {
@Override
public boolean equals(final Object other) {
- if (other == null) {
- return false;
- }
- if (!(other instanceof PropertyDescriptor)) {
- return false;
- }
if (this == other) {
return true;
}
+ if (other instanceof PropertyDescriptor otherPropertyDescriptor) {
+ return this.name.equals(otherPropertyDescriptor.name);
+ }
- final PropertyDescriptor desc = (PropertyDescriptor) other;
- return this.name.equals(desc.name);
+ return false;
}
@Override
@@ -768,7 +781,7 @@ private static final class ConstrainedSetValidator implements Validator {
private static final String POSITIVE_EXPLANATION = "Given value found in allowed set";
private static final String NEGATIVE_EXPLANATION = "Given value not found in allowed set '%1$s'";
- private static final String VALUE_DEMARCATOR = ", ";
+ private static final String VALUE_DELIMITER = ", ";
private final String validStrings;
private final Collection validValues;
@@ -780,20 +793,8 @@ private static final class ConstrainedSetValidator implements Validator {
* @throws NullPointerException if the given validValues is null
*/
private ConstrainedSetValidator(final Collection validValues) {
- String validVals = "";
- if (!validValues.isEmpty()) {
- final StringBuilder valuesBuilder = new StringBuilder();
- for (final AllowableValue value : validValues) {
- valuesBuilder.append(value).append(VALUE_DEMARCATOR);
- }
- validVals = valuesBuilder.substring(0, valuesBuilder.length() - VALUE_DEMARCATOR.length());
- }
- validStrings = validVals;
-
- this.validValues = new ArrayList<>(validValues.size());
- for (final AllowableValue value : validValues) {
- this.validValues.add(value.getValue());
- }
+ this.validValues = validValues.stream().map(AllowableValue::getValue).toList();
+ this.validStrings = String.join(VALUE_DELIMITER, this.validValues);
}
@Override
@@ -824,13 +825,13 @@ public ResourceDefinitionValidator(final ResourceDefinition resourceDefinition,
@Override
public ValidationResult validate(final String subject, final String configuredInput, final ValidationContext context) {
final ValidationResult.Builder resultBuilder = new ValidationResult.Builder()
- .input(configuredInput)
- .subject(subject);
+ .input(configuredInput)
+ .subject(subject);
if (configuredInput == null) {
return resultBuilder.valid(false)
- .explanation("No value specified")
- .build();
+ .explanation("No value specified")
+ .build();
}
// If Expression Language is supported and is used in the property value, we cannot perform validation against the configured
@@ -843,8 +844,8 @@ public ValidationResult validate(final String subject, final String configuredIn
resultBuilder.input(input);
} else {
return resultBuilder.valid(true)
- .explanation("Expression Language is present, so validation of property value cannot be performed")
- .build();
+ .explanation("Expression Language is present, so validation of property value cannot be performed")
+ .build();
}
}
@@ -854,15 +855,15 @@ public ValidationResult validate(final String subject, final String configuredIn
final boolean allowsText = resourceDefinition.getResourceTypes().contains(ResourceType.TEXT);
if (allowsText) {
return resultBuilder.valid(true)
- .explanation("Property allows for Resource Type of Text, so validation of property value cannot be performed")
- .build();
+ .explanation("Property allows for Resource Type of Text, so validation of property value cannot be performed")
+ .build();
}
final String[] splits = input.split(",");
if (resourceDefinition.getCardinality() == ResourceCardinality.SINGLE && splits.length > 1) {
return resultBuilder.valid(false)
- .explanation("Property only supports a single Resource but " + splits.length + " resources were specified")
- .build();
+ .explanation("Property only supports a single Resource but " + splits.length + " resources were specified")
+ .build();
}
final Set resourceTypes = resourceDefinition.getResourceTypes();
@@ -885,25 +886,25 @@ public ValidationResult validate(final String subject, final String configuredIn
if (!resourceTypes.contains(resourceReference.getResourceType())) {
return resultBuilder.valid(false)
- .explanation("Specified Resource is a " + resourceReference.getResourceType().name() + " but this property does not allow this type of resource")
- .build();
+ .explanation("Specified Resource is a " + resourceReference.getResourceType().name() + " but this property does not allow this type of resource")
+ .build();
}
}
if (count == 0) {
return resultBuilder.valid(false)
- .explanation("No resources were specified")
- .build();
+ .explanation("No resources were specified")
+ .build();
}
if (!nonExistentResources.isEmpty()) {
return resultBuilder.valid(false)
- .explanation("The specified resource(s) do not exist or could not be accessed: " + nonExistentResources)
- .build();
+ .explanation("The specified resource(s) do not exist or could not be accessed: " + nonExistentResources)
+ .build();
}
return resultBuilder.valid(true)
- .build();
+ .build();
}
}
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java b/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
index 096dd907acea5..4e52c54cb9b21 100644
--- a/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
+++ b/nifi-api/src/main/java/org/apache/nifi/components/PropertyValue.java
@@ -135,16 +135,18 @@ public interface PropertyValue {
ResourceReferences asResources();
/**
- * @param the generic type of the enum used as allowable values
- * @param enumType the class of the enum used as allowable values
- * @return the DescribedValue enum entry whose value is the raw value of the
- * this, or null if the value is not set.
- * Throws an IllegalArgumentException if none of the enum entries correspond to the specified raw value.
+ * Returns the property value as one of the configured allowableValues, see {@link PropertyDescriptor.Builder#allowableValues(Class)}
+ *
+ * The {@link Class#getEnumConstants()} of the provided enum are searched for an entry matching the value of this.
+ * In case an enum value is a {@link DescribedValue}, uses the defined {@link DescribedValue#getValue()} for comparison.
+ * Otherwise, the {@link Enum#name()} is used.
*
- * @throws IllegalArgumentException if the value of this
- * does not point to any of the entries of the specified enum type.
+ * @param the generic type of the enum used as allowableValues
+ * @param enumType the class of the enum used as allowableValues
+ * @return the matching enum entry, or null if the value is not set.
+ * @throws IllegalArgumentException if no enum entry matching the value of this is found.
*/
- & DescribedValue> E asDescribedValue(Class enumType) throws IllegalArgumentException;
+ > E asAllowableValue(Class enumType) throws IllegalArgumentException;
/**
* @return true if the user has configured a value, or if the
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/BatchSize.java b/nifi-api/src/main/java/org/apache/nifi/flow/BatchSize.java
index 305807a67cfa6..05ba824f6042a 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/BatchSize.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/BatchSize.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -26,7 +26,7 @@ public class BatchSize {
private String size;
private String duration;
- @ApiModelProperty("Preferred number of flow files to include in a transaction.")
+ @Schema(description = "Preferred number of flow files to include in a transaction.")
public Integer getCount() {
return count;
}
@@ -35,7 +35,7 @@ public void setCount(Integer count) {
this.count = count;
}
- @ApiModelProperty("Preferred number of bytes to include in a transaction.")
+ @Schema(description = "Preferred number of bytes to include in a transaction.")
public String getSize() {
return size;
}
@@ -44,7 +44,7 @@ public void setSize(String size) {
this.size = size;
}
- @ApiModelProperty("Preferred amount of time that a transaction should span.")
+ @Schema(description = "Preferred amount of time that a transaction should span.")
public String getDuration() {
return duration;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/Bundle.java b/nifi-api/src/main/java/org/apache/nifi/flow/Bundle.java
index 019b05255963e..ddfd5678d978c 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/Bundle.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/Bundle.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -35,7 +35,7 @@ public Bundle(final String group, final String artifact, final String version) {
this.version = version;
}
- @ApiModelProperty("The group of the bundle")
+ @Schema(description = "The group of the bundle")
public String getGroup() {
return group;
}
@@ -44,7 +44,7 @@ public void setGroup(String group) {
this.group = group;
}
- @ApiModelProperty("The artifact of the bundle")
+ @Schema(description = "The artifact of the bundle")
public String getArtifact() {
return artifact;
}
@@ -53,7 +53,7 @@ public void setArtifact(String artifact) {
this.artifact = artifact;
}
- @ApiModelProperty("The version of the bundle")
+ @Schema(description = "The version of the bundle")
public String getVersion() {
return version;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/ConnectableComponent.java b/nifi-api/src/main/java/org/apache/nifi/flow/ConnectableComponent.java
index 7926ed22660db..c4264db94e803 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/ConnectableComponent.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/ConnectableComponent.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -29,7 +29,7 @@ public class ConnectableComponent {
private String name;
private String comments;
- @ApiModelProperty(value = "The id of the connectable component.", required = true)
+ @Schema(description = "The id of the connectable component.")
public String getId() {
return id;
}
@@ -38,7 +38,7 @@ public void setId(String id) {
this.id = id;
}
- @ApiModelProperty("The instance ID of an existing component that is described by this VersionedComponent, or null if this is not mapped to an instantiated component")
+ @Schema(description = "The instance ID of an existing component that is described by this VersionedComponent, or null if this is not mapped to an instantiated component")
public String getInstanceIdentifier() {
return instanceId;
}
@@ -47,7 +47,7 @@ public void setInstanceIdentifier(String instanceIdentifier) {
this.instanceId = instanceIdentifier;
}
- @ApiModelProperty(value = "The type of component the connectable is.", required = true)
+ @Schema(description = "The type of component the connectable is.")
public ConnectableComponentType getType() {
return type;
}
@@ -56,7 +56,7 @@ public void setType(ConnectableComponentType type) {
this.type = type;
}
- @ApiModelProperty(value = "The id of the group that the connectable component resides in", required = true)
+ @Schema(description = "The id of the group that the connectable component resides in")
public String getGroupId() {
return groupId;
}
@@ -65,7 +65,7 @@ public void setGroupId(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty("The name of the connectable component")
+ @Schema(description = "The name of the connectable component")
public String getName() {
return name;
}
@@ -74,7 +74,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The comments for the connectable component.")
+ @Schema(description = "The comments for the connectable component.")
public String getComments() {
return comments;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/ControllerServiceAPI.java b/nifi-api/src/main/java/org/apache/nifi/flow/ControllerServiceAPI.java
index ab7648f6be3d0..ab28fc353ba02 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/ControllerServiceAPI.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/ControllerServiceAPI.java
@@ -19,13 +19,13 @@
import java.util.Objects;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class ControllerServiceAPI {
private String type;
private Bundle bundle;
- @ApiModelProperty("The fully qualified name of the service interface.")
+ @Schema(description = "The fully qualified name of the service interface.")
public String getType() {
return type;
}
@@ -34,7 +34,7 @@ public void setType(String type) {
this.type = type;
}
- @ApiModelProperty("The details of the artifact that bundled this service interface.")
+ @Schema(description = "The details of the artifact that bundled this service interface.")
public Bundle getBundle() {
return bundle;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/ExternalControllerServiceReference.java b/nifi-api/src/main/java/org/apache/nifi/flow/ExternalControllerServiceReference.java
index 2e7ba92fbcba4..f7d023e989bc1 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/ExternalControllerServiceReference.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/ExternalControllerServiceReference.java
@@ -16,16 +16,15 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
+@Schema
public class ExternalControllerServiceReference {
private String identifier;
private String name;
- @ApiModelProperty("The identifier of the controller service")
+ @Schema(description = "The identifier of the controller service")
public String getIdentifier() {
return identifier;
}
@@ -34,7 +33,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty("The name of the controller service")
+ @Schema(description = "The name of the controller service")
public String getName() {
return name;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/ParameterProviderReference.java b/nifi-api/src/main/java/org/apache/nifi/flow/ParameterProviderReference.java
index 93e7041dd8055..e8c82561753c0 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/ParameterProviderReference.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/ParameterProviderReference.java
@@ -16,10 +16,9 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
-@ApiModel
+@Schema
public class ParameterProviderReference {
private String identifier;
@@ -27,7 +26,7 @@ public class ParameterProviderReference {
private String type;
private Bundle bundle;
- @ApiModelProperty("The fully qualified name of the parameter provider class.")
+ @Schema(description = "The fully qualified name of the parameter provider class.")
public String getType() {
return type;
}
@@ -36,7 +35,7 @@ public void setType(final String type) {
this.type = type;
}
- @ApiModelProperty("The details of the artifact that bundled this parameter provider.")
+ @Schema(description = "The details of the artifact that bundled this parameter provider.")
public Bundle getBundle() {
return bundle;
}
@@ -45,7 +44,7 @@ public void setBundle(final Bundle bundle) {
this.bundle = bundle;
}
- @ApiModelProperty("The identifier of the parameter provider")
+ @Schema(description = "The identifier of the parameter provider")
public String getIdentifier() {
return identifier;
}
@@ -54,7 +53,7 @@ public void setIdentifier(final String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty("The name of the parameter provider")
+ @Schema(description = "The name of the parameter provider")
public String getName() {
return name;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/Position.java b/nifi-api/src/main/java/org/apache/nifi/flow/Position.java
index f7c1c272a4b3a..d9a1e5a3c719e 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/Position.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/Position.java
@@ -17,12 +17,11 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
-@ApiModel(description = "The position of a component on the graph")
+@Schema(description = "The position of a component on the graph")
public class Position {
private double x;
private double y;
@@ -35,7 +34,7 @@ public Position(double x, double y) {
this.y = y;
}
- @ApiModelProperty("The x coordinate.")
+ @Schema(description = "The x coordinate.")
public double getX() {
return x;
}
@@ -44,7 +43,7 @@ public void setX(double x) {
this.x = x;
}
- @ApiModelProperty("The y coordinate.")
+ @Schema(description = "The y coordinate.")
public double getY() {
return y;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedComponent.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedComponent.java
index 639d6fb3bbd8a..d645069433f75 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedComponent.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedComponent.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -31,7 +31,7 @@ public abstract class VersionedComponent {
private String comments;
private Position position;
- @ApiModelProperty("The component's unique identifier")
+ @Schema(description = "The component's unique identifier")
public String getIdentifier() {
return identifier;
}
@@ -40,7 +40,7 @@ public void setIdentifier(String identifier) {
this.identifier = identifier;
}
- @ApiModelProperty("The instance ID of an existing component that is described by this VersionedComponent, or null if this is not mapped to an instantiated component")
+ @Schema(description = "The instance ID of an existing component that is described by this VersionedComponent, or null if this is not mapped to an instantiated component")
public String getInstanceIdentifier() {
return instanceIdentifier;
}
@@ -49,7 +49,7 @@ public void setInstanceIdentifier(String instanceIdentifier) {
this.instanceIdentifier = instanceIdentifier;
}
- @ApiModelProperty("The ID of the Process Group that this component belongs to")
+ @Schema(description = "The ID of the Process Group that this component belongs to")
public String getGroupIdentifier() {
return groupId;
}
@@ -58,7 +58,7 @@ public void setGroupIdentifier(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty("The component's name")
+ @Schema(description = "The component's name")
public String getName() {
return name;
}
@@ -67,7 +67,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The component's position on the graph")
+ @Schema(description = "The component's position on the graph")
public Position getPosition() {
return position;
}
@@ -76,7 +76,7 @@ public void setPosition(Position position) {
this.position = position;
}
- @ApiModelProperty("The user-supplied comments for the component")
+ @Schema(description = "The user-supplied comments for the component")
public String getComments() {
return comments;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConfigurableExtension.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConfigurableExtension.java
index 5e3b5076d8286..d21059d5216f2 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConfigurableExtension.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConfigurableExtension.java
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
@@ -30,7 +30,7 @@ public abstract class VersionedConfigurableExtension extends VersionedComponent
private Map propertyDescriptors;
@Override
- @ApiModelProperty("The type of the extension component")
+ @Schema(description = "The type of the extension component")
public String getType() {
return type;
}
@@ -41,7 +41,7 @@ public void setType(final String type) {
}
@Override
- @ApiModelProperty("Information about the bundle from which the component came")
+ @Schema(description = "Information about the bundle from which the component came")
public Bundle getBundle() {
return bundle;
}
@@ -53,7 +53,7 @@ public void setBundle(Bundle bundle) {
@Override
- @ApiModelProperty("The properties for the component. Properties whose value is not set will only contain the property name.")
+ @Schema(description = "The properties for the component. Properties whose value is not set will only contain the property name.")
public Map getProperties() {
return properties;
}
@@ -64,7 +64,7 @@ public void setProperties(Map properties) {
}
@Override
- @ApiModelProperty("The property descriptors for the component.")
+ @Schema(description = "The property descriptors for the component.")
public Map getPropertyDescriptors() {
return propertyDescriptors;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConnection.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConnection.java
index 44725b8475e81..c8e36df7b52f2 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConnection.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedConnection.java
@@ -20,7 +20,7 @@
import java.util.List;
import java.util.Set;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class VersionedConnection extends VersionedComponent {
private ConnectableComponent source;
@@ -39,8 +39,7 @@ public class VersionedConnection extends VersionedComponent {
private String partitioningAttribute;
private String loadBalanceCompression;
-
- @ApiModelProperty("The source of the connection.")
+ @Schema(description = "The source of the connection.")
public ConnectableComponent getSource() {
return source;
}
@@ -49,7 +48,7 @@ public void setSource(ConnectableComponent source) {
this.source = source;
}
- @ApiModelProperty("The destination of the connection.")
+ @Schema(description = "The destination of the connection.")
public ConnectableComponent getDestination() {
return destination;
}
@@ -58,7 +57,7 @@ public void setDestination(ConnectableComponent destination) {
this.destination = destination;
}
- @ApiModelProperty("The bend points on the connection.")
+ @Schema(description = "The bend points on the connection.")
public List getBends() {
return bends;
}
@@ -67,7 +66,7 @@ public void setBends(List bends) {
this.bends = bends;
}
- @ApiModelProperty("The index of the bend point where to place the connection label.")
+ @Schema(description = "The index of the bend point where to place the connection label.")
public Integer getLabelIndex() {
return labelIndex;
}
@@ -76,8 +75,8 @@ public void setLabelIndex(Integer labelIndex) {
this.labelIndex = labelIndex;
}
- @ApiModelProperty(
- value = "The z index of the connection.",
+ @Schema(
+ description = "The z index of the connection.",
name = "zIndex") // Jackson maps this method name to JSON key "zIndex", but Swagger does not by default
public Long getzIndex() {
return zIndex;
@@ -87,7 +86,7 @@ public void setzIndex(Long zIndex) {
this.zIndex = zIndex;
}
- @ApiModelProperty("The selected relationship that comprise the connection.")
+ @Schema(description = "The selected relationship that comprise the connection.")
public Set getSelectedRelationships() {
return selectedRelationships;
}
@@ -96,8 +95,7 @@ public void setSelectedRelationships(Set relationships) {
this.selectedRelationships = relationships;
}
-
- @ApiModelProperty("The object count threshold for determining when back pressure is applied. Updating this value is a passive change in the sense that it won't impact whether existing files "
+ @Schema(description = "The object count threshold for determining when back pressure is applied. Updating this value is a passive change in the sense that it won't impact whether existing files "
+ "over the limit are affected but it does help feeder processors to stop pushing too much into this work queue.")
public Long getBackPressureObjectThreshold() {
return backPressureObjectThreshold;
@@ -107,8 +105,7 @@ public void setBackPressureObjectThreshold(Long backPressureObjectThreshold) {
this.backPressureObjectThreshold = backPressureObjectThreshold;
}
-
- @ApiModelProperty("The object data size threshold for determining when back pressure is applied. Updating this value is a passive change in the sense that it won't impact whether existing "
+ @Schema(description = "The object data size threshold for determining when back pressure is applied. Updating this value is a passive change in the sense that it won't impact whether existing "
+ "files over the limit are affected but it does help feeder processors to stop pushing too much into this work queue.")
public String getBackPressureDataSizeThreshold() {
return backPressureDataSizeThreshold;
@@ -119,7 +116,7 @@ public void setBackPressureDataSizeThreshold(String backPressureDataSizeThreshol
}
- @ApiModelProperty("The amount of time a flow file may be in the flow before it will be automatically aged out of the flow. Once a flow file reaches this age it will be terminated from "
+ @Schema(description = "The amount of time a flow file may be in the flow before it will be automatically aged out of the flow. Once a flow file reaches this age it will be terminated from "
+ "the flow the next time a processor attempts to start work on it.")
public String getFlowFileExpiration() {
return flowFileExpiration;
@@ -130,7 +127,7 @@ public void setFlowFileExpiration(String flowFileExpiration) {
}
- @ApiModelProperty("The comparators used to prioritize the queue.")
+ @Schema(description = "The comparators used to prioritize the queue.")
public List getPrioritizers() {
return prioritizers;
}
@@ -139,7 +136,7 @@ public void setPrioritizers(List prioritizers) {
this.prioritizers = prioritizers;
}
- @ApiModelProperty(value = "The Strategy to use for load balancing data across the cluster, or null, if no Load Balance Strategy has been specified.",
+ @Schema(description = "The Strategy to use for load balancing data across the cluster, or null, if no Load Balance Strategy has been specified.",
allowableValues = "DO_NOT_LOAD_BALANCE, PARTITION_BY_ATTRIBUTE, ROUND_ROBIN, SINGLE_NODE")
public String getLoadBalanceStrategy() {
return loadBalanceStrategy;
@@ -149,7 +146,7 @@ public void setLoadBalanceStrategy(String loadBalanceStrategy) {
this.loadBalanceStrategy = loadBalanceStrategy;
}
- @ApiModelProperty("The attribute to use for partitioning data as it is load balanced across the cluster. If the Load Balance Strategy is configured to use PARTITION_BY_ATTRIBUTE, the value " +
+ @Schema(description = "The attribute to use for partitioning data as it is load balanced across the cluster. If the Load Balance Strategy is configured to use PARTITION_BY_ATTRIBUTE, the value " +
"returned by this method is the name of the FlowFile Attribute that will be used to determine which node in the cluster should receive a given FlowFile. If the Load Balance Strategy is " +
"unset or is set to any other value, the Partitioning Attribute has no effect.")
public String getPartitioningAttribute() {
@@ -160,7 +157,7 @@ public void setPartitioningAttribute(final String partitioningAttribute) {
this.partitioningAttribute = partitioningAttribute;
}
- @ApiModelProperty(value = "Whether or not compression should be used when transferring FlowFiles between nodes",
+ @Schema(description = "Whether or not compression should be used when transferring FlowFiles between nodes",
allowableValues = "DO_NOT_COMPRESS, COMPRESS_ATTRIBUTES_ONLY, COMPRESS_ATTRIBUTES_AND_CONTENT")
public String getLoadBalanceCompression() {
return loadBalanceCompression;
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedControllerService.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedControllerService.java
index 67f01080ddf0d..25b857ba95559 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedControllerService.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedControllerService.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
@@ -29,7 +29,7 @@ public class VersionedControllerService extends VersionedConfigurableExtension {
private ScheduledState scheduledState;
private String bulletinLevel;
- @ApiModelProperty(value = "Lists the APIs this Controller Service implements.")
+ @Schema(description = "Lists the APIs this Controller Service implements.")
public List getControllerServiceApis() {
return controllerServiceApis;
}
@@ -38,7 +38,7 @@ public void setControllerServiceApis(List controllerServic
this.controllerServiceApis = controllerServiceApis;
}
- @ApiModelProperty(value = "The annotation for the controller service. This is how the custom UI relays configuration to the controller service.")
+ @Schema(description = "The annotation for the controller service. This is how the custom UI relays configuration to the controller service.")
public String getAnnotationData() {
return annotationData;
}
@@ -52,7 +52,7 @@ public ComponentType getComponentType() {
return ComponentType.CONTROLLER_SERVICE;
}
- @ApiModelProperty("The ScheduledState denoting whether the Controller Service is ENABLED or DISABLED")
+ @Schema(description = "The ScheduledState denoting whether the Controller Service is ENABLED or DISABLED")
public ScheduledState getScheduledState() {
return scheduledState;
}
@@ -61,7 +61,7 @@ public void setScheduledState(final ScheduledState scheduledState) {
this.scheduledState = scheduledState;
}
- @ApiModelProperty("The level at which the controller service will report bulletins.")
+ @Schema(description = "The level at which the controller service will report bulletins.")
public String getBulletinLevel() {
return bulletinLevel;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowAnalysisRule.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowAnalysisRule.java
index 233fd4adb043e..0d5abd829b369 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowAnalysisRule.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowAnalysisRule.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.flowanalysis.EnforcementPolicy;
public class VersionedFlowAnalysisRule extends VersionedConfigurableExtension {
@@ -25,7 +25,7 @@ public class VersionedFlowAnalysisRule extends VersionedConfigurableExtension {
private ScheduledState scheduledState;
private EnforcementPolicy enforcementPolicy;
- @ApiModelProperty("How to handle violations.")
+ @Schema(description = "How to handle violations.")
public EnforcementPolicy getEnforcementPolicy() {
return enforcementPolicy;
}
@@ -39,7 +39,7 @@ public ComponentType getComponentType() {
return ComponentType.FLOW_ANALYSIS_RULE;
}
- @ApiModelProperty("Indicates the scheduled state for the flow analysis rule")
+ @Schema(description = "Indicates the scheduled state for the flow analysis rule")
public ScheduledState getScheduledState() {
return scheduledState;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowCoordinates.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowCoordinates.java
index 3d5fb2e47cda4..125982297c0ec 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowCoordinates.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowCoordinates.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -29,7 +29,7 @@ public class VersionedFlowCoordinates {
private int version;
private Boolean latest;
- @ApiModelProperty("The identifier of the Flow Registry that contains the flow")
+ @Schema(description = "The identifier of the Flow Registry that contains the flow")
public String getRegistryId() {
return registryId;
}
@@ -38,7 +38,7 @@ public void setRegistryId(String registryId) {
this.registryId = registryId;
}
- @ApiModelProperty("The location of the Flow Registry that stores the flow")
+ @Schema(description = "The location of the Flow Registry that stores the flow")
public String getStorageLocation() {
return storageLocation;
}
@@ -47,7 +47,7 @@ public void setStorageLocation(String storageLocation) {
this.storageLocation = storageLocation;
}
- @ApiModelProperty("The UUID of the bucket that the flow resides in")
+ @Schema(description = "The UUID of the bucket that the flow resides in")
public String getBucketId() {
return bucketId;
}
@@ -56,7 +56,7 @@ public void setBucketId(String bucketId) {
this.bucketId = bucketId;
}
- @ApiModelProperty("The UUID of the flow")
+ @Schema(description = "The UUID of the flow")
public String getFlowId() {
return flowId;
}
@@ -65,7 +65,7 @@ public void setFlowId(String flowId) {
this.flowId = flowId;
}
- @ApiModelProperty("The version of the flow")
+ @Schema(description = "The version of the flow")
public int getVersion() {
return version;
}
@@ -74,7 +74,7 @@ public void setVersion(int version) {
this.version = version;
}
- @ApiModelProperty("Whether or not these coordinates point to the latest version of the flow")
+ @Schema(description = "Whether or not these coordinates point to the latest version of the flow")
public Boolean getLatest() {
return latest;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowRegistryClient.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowRegistryClient.java
index b0212bd0e76fd..3b4c9eb5a0c66 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowRegistryClient.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedFlowRegistryClient.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class VersionedFlowRegistryClient extends VersionedConfigurableExtension {
private String description;
@@ -28,7 +28,7 @@ public ComponentType getComponentType() {
return ComponentType.FLOW_REGISTRY_CLIENT;
}
- @ApiModelProperty("The description of the registry")
+ @Schema(description = "The description of the registry")
public String getDescription() {
return description;
}
@@ -37,7 +37,7 @@ public void setDescription(final String description) {
this.description = description;
}
- @ApiModelProperty(value = "The annotation for the reporting task. This is how the custom UI relays configuration to the reporting task.")
+ @Schema(description = "The annotation for the reporting task. This is how the custom UI relays configuration to the reporting task.")
public String getAnnotationData() {
return annotationData;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedLabel.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedLabel.java
index 002030925158a..d0e8e2448600c 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedLabel.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedLabel.java
@@ -19,7 +19,7 @@
import java.util.Map;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class VersionedLabel extends VersionedComponent {
private String label;
@@ -31,7 +31,7 @@ public class VersionedLabel extends VersionedComponent {
private Map style;
- @ApiModelProperty("The text that appears in the label.")
+ @Schema(description = "The text that appears in the label.")
public String getLabel() {
return label;
}
@@ -40,7 +40,7 @@ public void setLabel(final String label) {
this.label = label;
}
- @ApiModelProperty("The styles for this label (font-size : 12px, background-color : #eee, etc).")
+ @Schema(description = "The styles for this label (font-size : 12px, background-color : #eee, etc).")
public Map getStyle() {
return style;
}
@@ -49,7 +49,7 @@ public void setStyle(final Map style) {
this.style = style;
}
- @ApiModelProperty("The height of the label in pixels when at a 1:1 scale.")
+ @Schema(description = "The height of the label in pixels when at a 1:1 scale.")
public Double getHeight() {
return height;
}
@@ -58,7 +58,7 @@ public void setHeight(Double height) {
this.height = height;
}
- @ApiModelProperty("The width of the label in pixels when at a 1:1 scale.")
+ @Schema(description = "The width of the label in pixels when at a 1:1 scale.")
public Double getWidth() {
return width;
}
@@ -67,8 +67,8 @@ public void setWidth(Double width) {
this.width = width;
}
- @ApiModelProperty(
- value = "The z index of the connection.",
+ @Schema(
+ description = "The z index of the connection.",
name = "zIndex") // Jackson maps this method name to JSON key "zIndex", but Swagger does not by default
public Long getzIndex() {
return zIndex;
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameter.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameter.java
index 9f71a7e21b08a..987e23ad4d3d9 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameter.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameter.java
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -28,7 +28,7 @@ public class VersionedParameter {
private boolean provided;
private String value;
- @ApiModelProperty("The name of the parameter")
+ @Schema(description = "The name of the parameter")
public String getName() {
return name;
}
@@ -37,7 +37,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The description of the param")
+ @Schema(description = "The description of the param")
public String getDescription() {
return description;
}
@@ -46,7 +46,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty("Whether or not the parameter value is sensitive")
+ @Schema(description = "Whether or not the parameter value is sensitive")
public boolean isSensitive() {
return sensitive;
}
@@ -55,7 +55,7 @@ public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
- @ApiModelProperty("Whether or not the parameter value is provided by a ParameterProvider")
+ @Schema(description = "Whether or not the parameter value is provided by a ParameterProvider")
public boolean isProvided() {
return provided;
}
@@ -64,7 +64,7 @@ public void setProvided(boolean provided) {
this.provided = provided;
}
- @ApiModelProperty("The value of the parameter")
+ @Schema(description = "The value of the parameter")
public String getValue() {
return value;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterContext.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterContext.java
index e7bcfc1fe344f..717ccbdcc2092 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterContext.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterContext.java
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import java.util.Set;
@@ -29,7 +29,7 @@ public class VersionedParameterContext extends VersionedComponent {
private String parameterGroupName;
private Boolean isSynchronized;
- @ApiModelProperty("The description of the parameter context")
+ @Schema(description = "The description of the parameter context")
public String getDescription() {
return description;
}
@@ -38,7 +38,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty("The parameters in the context")
+ @Schema(description = "The parameters in the context")
public Set getParameters() {
return parameters;
}
@@ -47,7 +47,7 @@ public void setParameters(Set parameters) {
this.parameters = parameters;
}
- @ApiModelProperty("The names of additional parameter contexts from which to inherit parameters")
+ @Schema(description = "The names of additional parameter contexts from which to inherit parameters")
public List getInheritedParameterContexts() {
return inheritedParameterContexts;
}
@@ -61,7 +61,7 @@ public ComponentType getComponentType() {
return ComponentType.PARAMETER_CONTEXT;
}
- @ApiModelProperty("The identifier of an optional parameter provider")
+ @Schema(description = "The identifier of an optional parameter provider")
public String getParameterProvider() {
return parameterProvider;
}
@@ -70,7 +70,7 @@ public void setParameterProvider(String parameterProvider) {
this.parameterProvider = parameterProvider;
}
- @ApiModelProperty("The corresponding parameter group name fetched from the parameter provider, if applicable")
+ @Schema(description = "The corresponding parameter group name fetched from the parameter provider, if applicable")
public String getParameterGroupName() {
return parameterGroupName;
}
@@ -79,7 +79,7 @@ public void setParameterGroupName(String parameterGroupName) {
this.parameterGroupName = parameterGroupName;
}
- @ApiModelProperty("True if the parameter provider is set and the context should receive updates when its parameters are next fetched")
+ @Schema(description = "True if the parameter provider is set and the context should receive updates when its parameters are next fetched")
public Boolean isSynchronized() {
return isSynchronized;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterProvider.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterProvider.java
index b37d5e04a37a2..576b1d0ddb356 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterProvider.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedParameterProvider.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
@@ -30,7 +30,7 @@ public class VersionedParameterProvider extends VersionedComponent implements Ve
@Override
- @ApiModelProperty(value = "The type of the parameter provider.")
+ @Schema(description = "The type of the parameter provider.")
public String getType() {
return type;
}
@@ -41,7 +41,7 @@ public void setType(final String type) {
}
@Override
- @ApiModelProperty(value = "The details of the artifact that bundled this parameter provider type.")
+ @Schema(description = "The details of the artifact that bundled this parameter provider type.")
public Bundle getBundle() {
return bundle;
}
@@ -52,7 +52,7 @@ public void setBundle(Bundle bundle) {
}
@Override
- @ApiModelProperty(value = "The properties of the parameter provider.")
+ @Schema(description = "The properties of the parameter provider.")
public Map getProperties() {
return properties;
}
@@ -63,7 +63,7 @@ public void setProperties(Map properties) {
}
@Override
- @ApiModelProperty("The property descriptors for the parameter provider.")
+ @Schema(description = "The property descriptors for the parameter provider.")
public Map getPropertyDescriptors() {
return propertyDescriptors;
}
@@ -73,7 +73,7 @@ public void setPropertyDescriptors(Map prop
this.propertyDescriptors = propertyDescriptors;
}
- @ApiModelProperty(value = "The annotation for the parameter provider. This is how the custom UI relays configuration to the parameter provider.")
+ @Schema(description = "The annotation for the parameter provider. This is how the custom UI relays configuration to the parameter provider.")
public String getAnnotationData() {
return annotationData;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPort.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPort.java
index 6f62d159cd408..a5f40b208607d 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPort.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPort.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.nifi.components.PortFunction;
public class VersionedPort extends VersionedComponent {
@@ -27,7 +27,7 @@ public class VersionedPort extends VersionedComponent {
private Boolean allowRemoteAccess;
private PortFunction portFunction;
- @ApiModelProperty("The number of tasks that should be concurrently scheduled for the port.")
+ @Schema(description = "The number of tasks that should be concurrently scheduled for the port.")
public Integer getConcurrentlySchedulableTaskCount() {
return concurrentlySchedulableTaskCount;
}
@@ -36,7 +36,7 @@ public void setConcurrentlySchedulableTaskCount(Integer concurrentlySchedulableT
this.concurrentlySchedulableTaskCount = concurrentlySchedulableTaskCount;
}
- @ApiModelProperty("The type of port.")
+ @Schema(description = "The type of port.")
public PortType getType() {
return type;
}
@@ -45,7 +45,7 @@ public void setType(PortType type) {
this.type = type;
}
- @ApiModelProperty("The scheduled state of the component")
+ @Schema(description = "The scheduled state of the component")
public ScheduledState getScheduledState() {
return scheduledState;
}
@@ -54,7 +54,7 @@ public void setScheduledState(ScheduledState scheduledState) {
this.scheduledState = scheduledState;
}
- @ApiModelProperty("Whether or not this port allows remote access for site-to-site")
+ @Schema(description = "Whether or not this port allows remote access for site-to-site")
public Boolean isAllowRemoteAccess() {
return ((allowRemoteAccess != null) && allowRemoteAccess);
}
@@ -67,7 +67,7 @@ public void setAllowRemoteAccess(Boolean allowRemoteAccess) {
this.allowRemoteAccess = allowRemoteAccess;
}
- @ApiModelProperty("Specifies how the Port should function")
+ @Schema(description = "Specifies how the Port should function")
public PortFunction getPortFunction() {
return portFunction;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessGroup.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessGroup.java
index 00980aff47195..e63c59b9e6f31 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessGroup.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessGroup.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.HashSet;
import java.util.Set;
@@ -50,7 +50,7 @@ public class VersionedProcessGroup extends VersionedComponent {
private String logFileSuffix;
- @ApiModelProperty("The child Process Groups")
+ @Schema(description = "The child Process Groups")
public Set getProcessGroups() {
return processGroups;
}
@@ -59,7 +59,7 @@ public void setProcessGroups(Set processGroups) {
this.processGroups = new HashSet<>(processGroups);
}
- @ApiModelProperty("The Remote Process Groups")
+ @Schema(description = "The Remote Process Groups")
public Set getRemoteProcessGroups() {
return remoteProcessGroups;
}
@@ -68,7 +68,7 @@ public void setRemoteProcessGroups(Set remoteProces
this.remoteProcessGroups = new HashSet<>(remoteProcessGroups);
}
- @ApiModelProperty("The Processors")
+ @Schema(description = "The Processors")
public Set getProcessors() {
return processors;
}
@@ -77,7 +77,7 @@ public void setProcessors(Set processors) {
this.processors = new HashSet<>(processors);
}
- @ApiModelProperty("The Input Ports")
+ @Schema(description = "The Input Ports")
public Set getInputPorts() {
return inputPorts;
}
@@ -86,7 +86,7 @@ public void setInputPorts(Set inputPorts) {
this.inputPorts = new HashSet<>(inputPorts);
}
- @ApiModelProperty("The Output Ports")
+ @Schema(description = "The Output Ports")
public Set getOutputPorts() {
return outputPorts;
}
@@ -95,7 +95,7 @@ public void setOutputPorts(Set outputPorts) {
this.outputPorts = new HashSet<>(outputPorts);
}
- @ApiModelProperty("The Connections")
+ @Schema(description = "The Connections")
public Set getConnections() {
return connections;
}
@@ -104,7 +104,7 @@ public void setConnections(Set connections) {
this.connections = new HashSet<>(connections);
}
- @ApiModelProperty("The Labels")
+ @Schema(description = "The Labels")
public Set getLabels() {
return labels;
}
@@ -113,7 +113,7 @@ public void setLabels(Set labels) {
this.labels = new HashSet<>(labels);
}
- @ApiModelProperty("The Funnels")
+ @Schema(description = "The Funnels")
public Set getFunnels() {
return funnels;
}
@@ -122,7 +122,7 @@ public void setFunnels(Set funnels) {
this.funnels = new HashSet<>(funnels);
}
- @ApiModelProperty("The Controller Services")
+ @Schema(description = "The Controller Services")
public Set getControllerServices() {
return controllerServices;
}
@@ -140,12 +140,12 @@ public void setVersionedFlowCoordinates(VersionedFlowCoordinates flowCoordinates
this.versionedFlowCoordinates = flowCoordinates;
}
- @ApiModelProperty("The coordinates where the remote flow is stored, or null if the Process Group is not directly under Version Control")
+ @Schema(description = "The coordinates where the remote flow is stored, or null if the Process Group is not directly under Version Control")
public VersionedFlowCoordinates getVersionedFlowCoordinates() {
return versionedFlowCoordinates;
}
- @ApiModelProperty("The name of the parameter context used by this process group")
+ @Schema(description = "The name of the parameter context used by this process group")
public String getParameterContextName() {
return parameterContextName;
}
@@ -154,7 +154,7 @@ public void setParameterContextName(String parameterContextName) {
this.parameterContextName = parameterContextName;
}
- @ApiModelProperty(value = "The configured FlowFile Concurrency for the Process Group")
+ @Schema(description = "The configured FlowFile Concurrency for the Process Group")
public String getFlowFileConcurrency() {
return flowfileConcurrency;
}
@@ -163,7 +163,7 @@ public void setFlowFileConcurrency(final String flowfileConcurrency) {
this.flowfileConcurrency = flowfileConcurrency;
}
- @ApiModelProperty(value = "The FlowFile Outbound Policy for the Process Group")
+ @Schema(description = "The FlowFile Outbound Policy for the Process Group")
public String getFlowFileOutboundPolicy() {
return flowfileOutboundPolicy;
}
@@ -172,7 +172,7 @@ public void setFlowFileOutboundPolicy(final String outboundPolicy) {
this.flowfileOutboundPolicy = outboundPolicy;
}
- @ApiModelProperty(value = "The default FlowFile Expiration for this Process Group.")
+ @Schema(description = "The default FlowFile Expiration for this Process Group.")
public String getDefaultFlowFileExpiration() {
return defaultFlowFileExpiration;
}
@@ -181,7 +181,7 @@ public void setDefaultFlowFileExpiration(String defaultFlowFileExpiration) {
this.defaultFlowFileExpiration = defaultFlowFileExpiration;
}
- @ApiModelProperty(value = "Default value used in this Process Group for the maximum number of objects that can be queued before back pressure is applied.")
+ @Schema(description = "Default value used in this Process Group for the maximum number of objects that can be queued before back pressure is applied.")
public Long getDefaultBackPressureObjectThreshold() {
return defaultBackPressureObjectThreshold;
}
@@ -190,7 +190,7 @@ public void setDefaultBackPressureObjectThreshold(final Long defaultBackPressure
this.defaultBackPressureObjectThreshold = defaultBackPressureObjectThreshold;
}
- @ApiModelProperty(value = "Default value used in this Process Group for the maximum data size of objects that can be queued before back pressure is applied.")
+ @Schema(description = "Default value used in this Process Group for the maximum data size of objects that can be queued before back pressure is applied.")
public String getDefaultBackPressureDataSizeThreshold() {
return defaultBackPressureDataSizeThreshold;
}
@@ -199,7 +199,7 @@ public void setDefaultBackPressureDataSizeThreshold(final String defaultBackPres
this.defaultBackPressureDataSizeThreshold = defaultBackPressureDataSizeThreshold;
}
- @ApiModelProperty(value = "The log file suffix for this Process Group for dedicated logging.")
+ @Schema(description = "The log file suffix for this Process Group for dedicated logging.")
public String getLogFileSuffix() {
return logFileSuffix;
}
@@ -208,7 +208,7 @@ public void setLogFileSuffix(final String logFileSuffix) {
this.logFileSuffix = logFileSuffix;
}
- @ApiModelProperty("The Scheduled State of the Process Group, if the group is configured to use the Stateless Execution Engine. Otherwise, this value has no relevance.")
+ @Schema(description = "The Scheduled State of the Process Group, if the group is configured to use the Stateless Execution Engine. Otherwise, this value has no relevance.")
public ScheduledState getScheduledState() {
return scheduledState;
}
@@ -217,7 +217,7 @@ public void setScheduledState(final ScheduledState scheduledState) {
this.scheduledState = scheduledState;
}
- @ApiModelProperty("The Execution Engine that should be used to run the components within the group.")
+ @Schema(description = "The Execution Engine that should be used to run the components within the group.")
public ExecutionEngine getExecutionEngine() {
return executionEngine;
}
@@ -226,7 +226,7 @@ public void setExecutionEngine(final ExecutionEngine executionEngine) {
this.executionEngine = executionEngine;
}
- @ApiModelProperty("The maximum number of concurrent tasks that should be scheduled for this Process Group when using the Stateless Engine")
+ @Schema(description = "The maximum number of concurrent tasks that should be scheduled for this Process Group when using the Stateless Engine")
public Integer getMaxConcurrentTasks() {
return maxConcurrentTasks;
}
@@ -235,7 +235,7 @@ public void setMaxConcurrentTasks(final Integer maxConcurrentTasks) {
this.maxConcurrentTasks = maxConcurrentTasks;
}
- @ApiModelProperty("The maximum amount of time that the flow is allows to run using the Stateless engine before it times out and is considered a failure")
+ @Schema(description = "The maximum amount of time that the flow is allows to run using the Stateless engine before it times out and is considered a failure")
public String getStatelessFlowTimeout() {
return statelessFlowTimeout;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessor.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessor.java
index f716a9a2946de..266c6e9b97cf5 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessor.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedProcessor.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
import java.util.Set;
@@ -43,7 +43,7 @@ public class VersionedProcessor extends VersionedConfigurableExtension {
private String backoffMechanism;
private String maxBackoffPeriod;
- @ApiModelProperty("The frequency with which to schedule the processor. The format of the value will depend on th value of schedulingStrategy.")
+ @Schema(description = "The frequency with which to schedule the processor. The format of the value will depend on th value of schedulingStrategy.")
public String getSchedulingPeriod() {
return schedulingPeriod;
}
@@ -52,7 +52,7 @@ public void setSchedulingPeriod(String setSchedulingPeriod) {
this.schedulingPeriod = setSchedulingPeriod;
}
- @ApiModelProperty("Indicates how the processor should be scheduled to run.")
+ @Schema(description = "Indicates how the processor should be scheduled to run.")
public String getSchedulingStrategy() {
return schedulingStrategy;
}
@@ -61,7 +61,7 @@ public void setSchedulingStrategy(String schedulingStrategy) {
this.schedulingStrategy = schedulingStrategy;
}
- @ApiModelProperty("Indicates the node where the process will execute.")
+ @Schema(description = "Indicates the node where the process will execute.")
public String getExecutionNode() {
return executionNode;
}
@@ -70,7 +70,7 @@ public void setExecutionNode(String executionNode) {
this.executionNode = executionNode;
}
- @ApiModelProperty("The amout of time that is used when the process penalizes a flowfile.")
+ @Schema(description = "The amout of time that is used when the process penalizes a flowfile.")
public String getPenaltyDuration() {
return penaltyDuration;
}
@@ -79,7 +79,7 @@ public void setPenaltyDuration(String penaltyDuration) {
this.penaltyDuration = penaltyDuration;
}
- @ApiModelProperty("The amount of time that must elapse before this processor is scheduled again after yielding.")
+ @Schema(description = "The amount of time that must elapse before this processor is scheduled again after yielding.")
public String getYieldDuration() {
return yieldDuration;
}
@@ -88,7 +88,7 @@ public void setYieldDuration(String yieldDuration) {
this.yieldDuration = yieldDuration;
}
- @ApiModelProperty("The level at which the processor will report bulletins.")
+ @Schema(description = "The level at which the processor will report bulletins.")
public String getBulletinLevel() {
return bulletinLevel;
}
@@ -97,7 +97,7 @@ public void setBulletinLevel(String bulletinLevel) {
this.bulletinLevel = bulletinLevel;
}
- @ApiModelProperty("The number of tasks that should be concurrently schedule for the processor. If the processor doesn't allow parallol processing then any positive input will be ignored.")
+ @Schema(description = "The number of tasks that should be concurrently schedule for the processor. If the processor doesn't allow parallol processing then any positive input will be ignored.")
public Integer getConcurrentlySchedulableTaskCount() {
return concurrentlySchedulableTaskCount;
}
@@ -106,7 +106,7 @@ public void setConcurrentlySchedulableTaskCount(Integer concurrentlySchedulableT
this.concurrentlySchedulableTaskCount = concurrentlySchedulableTaskCount;
}
- @ApiModelProperty("The annotation data for the processor used to relay configuration between a custom UI and the procesosr.")
+ @Schema(description = "The annotation data for the processor used to relay configuration between a custom UI and the procesosr.")
public String getAnnotationData() {
return annotationData;
}
@@ -116,7 +116,7 @@ public void setAnnotationData(String annotationData) {
}
- @ApiModelProperty("The names of all relationships that cause a flow file to be terminated if the relationship is not connected elsewhere. This property differs "
+ @Schema(description = "The names of all relationships that cause a flow file to be terminated if the relationship is not connected elsewhere. This property differs "
+ "from the 'isAutoTerminate' property of the RelationshipDTO in that the RelationshipDTO is meant to depict the current configuration, whereas this "
+ "property can be set in a DTO when updating a Processor in order to change which Relationships should be auto-terminated.")
public Set getAutoTerminatedRelationships() {
@@ -127,7 +127,7 @@ public void setAutoTerminatedRelationships(final Set autoTerminatedRelat
this.autoTerminatedRelationships = autoTerminatedRelationships;
}
- @ApiModelProperty("The run duration for the processor in milliseconds.")
+ @Schema(description = "The run duration for the processor in milliseconds.")
public Long getRunDurationMillis() {
return runDurationMillis;
}
@@ -136,7 +136,7 @@ public void setRunDurationMillis(Long runDurationMillis) {
this.runDurationMillis = runDurationMillis;
}
- @ApiModelProperty("Stylistic data for rendering in a UI")
+ @Schema(description = "Stylistic data for rendering in a UI")
public Map getStyle() {
return style;
}
@@ -145,7 +145,7 @@ public void setStyle(Map style) {
this.style = style;
}
- @ApiModelProperty("The scheduled state of the component")
+ @Schema(description = "The scheduled state of the component")
public ScheduledState getScheduledState() {
return scheduledState;
}
@@ -159,9 +159,7 @@ public ComponentType getComponentType() {
return ComponentType.PROCESSOR;
}
- @ApiModelProperty(
- value = "Overall number of retries."
- )
+ @Schema(description = "Overall number of retries.")
public Integer getRetryCount() {
return retryCount;
}
@@ -170,9 +168,7 @@ public void setRetryCount(Integer retryCount) {
this.retryCount = retryCount;
}
- @ApiModelProperty(
- value = "All the relationships should be retried."
- )
+ @Schema(description = "All the relationships should be retried.")
public Set getRetriedRelationships() {
return retriedRelationships;
}
@@ -181,8 +177,8 @@ public void setRetriedRelationships(Set retriedRelationships) {
this.retriedRelationships = retriedRelationships;
}
- @ApiModelProperty(
- value = "Determines whether the FlowFile should be penalized or the processor should be yielded between retries.",
+ @Schema(
+ description = "Determines whether the FlowFile should be penalized or the processor should be yielded between retries.",
allowableValues = "PENALIZE_FLOWFILE, YIELD_PROCESSOR"
)
public String getBackoffMechanism() {
@@ -193,9 +189,7 @@ public void setBackoffMechanism(String backoffMechanism) {
this.backoffMechanism = backoffMechanism;
}
- @ApiModelProperty(
- value = "Maximum amount of time to be waited during a retry period."
- )
+ @Schema(description = "Maximum amount of time to be waited during a retry period.")
public String getMaxBackoffPeriod() {
return maxBackoffPeriod;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
index 25ded8cf545f4..15409351dcb4a 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedPropertyDescriptor.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class VersionedPropertyDescriptor {
private String name;
@@ -27,7 +27,7 @@ public class VersionedPropertyDescriptor {
private boolean dynamic;
private VersionedResourceDefinition resourceDefinition;
- @ApiModelProperty("The name of the property")
+ @Schema(description = "The name of the property")
public String getName() {
return name;
}
@@ -36,7 +36,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty("The display name of the property")
+ @Schema(description = "The display name of the property")
public String getDisplayName() {
return displayName;
}
@@ -45,7 +45,7 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- @ApiModelProperty("Whether or not the property provides the identifier of a Controller Service")
+ @Schema(description = "Whether or not the property provides the identifier of a Controller Service")
public boolean getIdentifiesControllerService() {
return identifiesControllerService;
}
@@ -54,7 +54,7 @@ public void setIdentifiesControllerService(boolean identifiesControllerService)
this.identifiesControllerService = identifiesControllerService;
}
- @ApiModelProperty("Whether or not the property is considered sensitive")
+ @Schema(description = "Whether or not the property is considered sensitive")
public boolean isSensitive() {
return sensitive;
}
@@ -63,7 +63,7 @@ public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
- @ApiModelProperty("Whether or not the property is user-defined")
+ @Schema(description = "Whether or not the property is user-defined")
public boolean isDynamic() {
return dynamic;
}
@@ -72,7 +72,7 @@ public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
- @ApiModelProperty("Returns the Resource Definition that defines which type(s) of resource(s) this property references, if any")
+ @Schema(description = "Returns the Resource Definition that defines which type(s) of resource(s) this property references, if any")
public VersionedResourceDefinition getResourceDefinition() {
return resourceDefinition;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteGroupPort.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteGroupPort.java
index 33b61b2efb9e0..777866a508dda 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteGroupPort.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteGroupPort.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Objects;
@@ -30,7 +30,7 @@ public class VersionedRemoteGroupPort extends VersionedComponent {
private String targetId;
private ScheduledState scheduledState;
- @ApiModelProperty("The number of task that may transmit flowfiles to the target port concurrently.")
+ @Schema(description = "The number of task that may transmit flowfiles to the target port concurrently.")
public Integer getConcurrentlySchedulableTaskCount() {
return concurrentlySchedulableTaskCount;
}
@@ -39,7 +39,7 @@ public void setConcurrentlySchedulableTaskCount(Integer concurrentlySchedulableT
this.concurrentlySchedulableTaskCount = concurrentlySchedulableTaskCount;
}
- @ApiModelProperty("The id of the remote process group that the port resides in.")
+ @Schema(description = "The id of the remote process group that the port resides in.")
public String getRemoteGroupId() {
return remoteGroupId;
}
@@ -49,7 +49,7 @@ public void setRemoteGroupId(String groupId) {
}
- @ApiModelProperty("Whether the flowfiles are compressed when sent to the target port.")
+ @Schema(description = "Whether the flowfiles are compressed when sent to the target port.")
public Boolean isUseCompression() {
return useCompression;
}
@@ -58,7 +58,7 @@ public void setUseCompression(Boolean useCompression) {
this.useCompression = useCompression;
}
- @ApiModelProperty("The batch settings for data transmission.")
+ @Schema(description = "The batch settings for data transmission.")
public BatchSize getBatchSize() {
return batchSize;
}
@@ -67,7 +67,7 @@ public void setBatchSize(BatchSize batchSize) {
this.batchSize = batchSize;
}
- @ApiModelProperty("The ID of the port on the target NiFi instance")
+ @Schema(description = "The ID of the port on the target NiFi instance")
public String getTargetId() {
return targetId;
}
@@ -76,7 +76,7 @@ public void setTargetId(final String targetId) {
this.targetId = targetId;
}
- @ApiModelProperty("The scheduled state of the component")
+ @Schema(description = "The scheduled state of the component")
public ScheduledState getScheduledState() {
return scheduledState;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteProcessGroup.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteProcessGroup.java
index 9b11b469b2b95..53590ea1117fb 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteProcessGroup.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedRemoteProcessGroup.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;
@@ -36,11 +36,9 @@ public class VersionedRemoteProcessGroup extends VersionedComponent {
private Set inputPorts;
private Set outputPorts;
-
- @ApiModelProperty(
- value = "The target URIs of the remote process group." +
- " If target uris is not set but target uri is set, then returns the single target uri." +
- " If neither target uris nor target uri is set, then returns null.")
+ @Schema(description = "The target URIs of the remote process group. " +
+ "If target uris is not set but target uri is set, then returns the single target uri. " +
+ "If neither target uris nor target uri is set, then returns null.")
public String getTargetUris() {
return !isEmpty(targetUris) ? targetUris : null;
@@ -54,7 +52,7 @@ public void setTargetUris(String targetUris) {
this.targetUris = targetUris;
}
- @ApiModelProperty("The time period used for the timeout when communicating with the target.")
+ @Schema(description = "The time period used for the timeout when communicating with the target.")
public String getCommunicationsTimeout() {
return communicationsTimeout;
}
@@ -63,7 +61,7 @@ public void setCommunicationsTimeout(String communicationsTimeout) {
this.communicationsTimeout = communicationsTimeout;
}
- @ApiModelProperty("When yielding, this amount of time must elapse before the remote process group is scheduled again.")
+ @Schema(description = "When yielding, this amount of time must elapse before the remote process group is scheduled again.")
public String getYieldDuration() {
return yieldDuration;
}
@@ -72,7 +70,7 @@ public void setYieldDuration(String yieldDuration) {
this.yieldDuration = yieldDuration;
}
- @ApiModelProperty(value = "The Transport Protocol that is used for Site-to-Site communications", allowableValues = "RAW, HTTP")
+ @Schema(description = "The Transport Protocol that is used for Site-to-Site communications", allowableValues = "RAW, HTTP")
public String getTransportProtocol() {
return transportProtocol;
}
@@ -81,7 +79,7 @@ public void setTransportProtocol(String transportProtocol) {
this.transportProtocol = transportProtocol;
}
- @ApiModelProperty("A Set of Input Ports that can be connected to, in order to send data to the remote NiFi instance")
+ @Schema(description = "A Set of Input Ports that can be connected to, in order to send data to the remote NiFi instance")
public Set getInputPorts() {
return inputPorts;
}
@@ -90,7 +88,7 @@ public void setInputPorts(Set inputPorts) {
this.inputPorts = inputPorts;
}
- @ApiModelProperty("A Set of Output Ports that can be connected to, in order to pull data from the remote NiFi instance")
+ @Schema(description = "A Set of Output Ports that can be connected to, in order to pull data from the remote NiFi instance")
public Set getOutputPorts() {
return outputPorts;
}
@@ -100,7 +98,7 @@ public void setOutputPorts(Set outputPorts) {
}
- @ApiModelProperty("The local network interface to send/receive data. If not specified, any local address is used. If clustered, all nodes must have an interface with this identifier.")
+ @Schema(description = "The local network interface to send/receive data. If not specified, any local address is used. If clustered, all nodes must have an interface with this identifier.")
public String getLocalNetworkInterface() {
return localNetworkInterface;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTask.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTask.java
index 4450cedf3f68c..6e9cc03d6fe13 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTask.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTask.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
public class VersionedReportingTask extends VersionedConfigurableExtension {
@@ -26,7 +26,7 @@ public class VersionedReportingTask extends VersionedConfigurableExtension {
private String schedulingPeriod;
private String schedulingStrategy;
- @ApiModelProperty(value = "The annotation for the reporting task. This is how the custom UI relays configuration to the reporting task.")
+ @Schema(description = "The annotation for the reporting task. This is how the custom UI relays configuration to the reporting task.")
public String getAnnotationData() {
return annotationData;
}
@@ -35,7 +35,7 @@ public void setAnnotationData(String annotationData) {
this.annotationData = annotationData;
}
- @ApiModelProperty("The frequency with which to schedule the reporting task. The format of the value will depend on the value of schedulingStrategy.")
+ @Schema(description = "The frequency with which to schedule the reporting task. The format of the value will depend on the value of schedulingStrategy.")
public String getSchedulingPeriod() {
return schedulingPeriod;
}
@@ -44,7 +44,7 @@ public void setSchedulingPeriod(String setSchedulingPeriod) {
this.schedulingPeriod = setSchedulingPeriod;
}
- @ApiModelProperty("Indicates scheduling strategy that should dictate how the reporting task is triggered.")
+ @Schema(description = "Indicates scheduling strategy that should dictate how the reporting task is triggered.")
public String getSchedulingStrategy() {
return schedulingStrategy;
}
@@ -58,7 +58,7 @@ public ComponentType getComponentType() {
return ComponentType.REPORTING_TASK;
}
- @ApiModelProperty("Indicates the scheduled state for the Reporting Task")
+ @Schema(description = "Indicates the scheduled state for the Reporting Task")
public ScheduledState getScheduledState() {
return scheduledState;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTaskSnapshot.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTaskSnapshot.java
index 3da3f8c302d14..d1d7e538d5a0b 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTaskSnapshot.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedReportingTaskSnapshot.java
@@ -16,18 +16,17 @@
*/
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
-@ApiModel
+@Schema
public class VersionedReportingTaskSnapshot {
private List reportingTasks;
private List controllerServices;
- @ApiModelProperty(value = "The controller services")
+ @Schema(description = "The controller services")
public List getControllerServices() {
return controllerServices;
}
@@ -36,7 +35,7 @@ public void setControllerServices(List controllerSer
this.controllerServices = controllerServices;
}
- @ApiModelProperty(value = "The reporting tasks")
+ @Schema(description = "The reporting tasks")
public List getReportingTasks() {
return reportingTasks;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedResourceDefinition.java b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedResourceDefinition.java
index 97c4e18210c91..a04e23bd1a53e 100644
--- a/nifi-api/src/main/java/org/apache/nifi/flow/VersionedResourceDefinition.java
+++ b/nifi-api/src/main/java/org/apache/nifi/flow/VersionedResourceDefinition.java
@@ -17,7 +17,7 @@
package org.apache.nifi.flow;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Set;
@@ -25,7 +25,7 @@ public class VersionedResourceDefinition {
private VersionedResourceCardinality cardinality;
private Set resourceTypes;
- @ApiModelProperty("The cardinality of the resource")
+ @Schema(description = "The cardinality of the resource")
public VersionedResourceCardinality getCardinality() {
return cardinality;
}
@@ -34,7 +34,7 @@ public void setCardinality(final VersionedResourceCardinality cardinality) {
this.cardinality = cardinality;
}
- @ApiModelProperty("The types of resource that the Property Descriptor is allowed to reference")
+ @Schema(description = "The types of resource that the Property Descriptor is allowed to reference")
public Set getResourceTypes() {
return resourceTypes;
}
diff --git a/nifi-api/src/main/java/org/apache/nifi/logging/LogMessage.java b/nifi-api/src/main/java/org/apache/nifi/logging/LogMessage.java
index 664bc60a1344c..bae8154bc3366 100644
--- a/nifi-api/src/main/java/org/apache/nifi/logging/LogMessage.java
+++ b/nifi-api/src/main/java/org/apache/nifi/logging/LogMessage.java
@@ -18,10 +18,8 @@
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.sql.Date;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Locale;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
public class LogMessage {
@@ -32,8 +30,9 @@ public class LogMessage {
private final String flowFileUuid;
private final Object[] objects;
- public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
- public static final String TO_STRING_FORMAT = "%1$s %2$s - %3$s";
+ private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
+ private static final String TO_STRING_FORMAT = "%1$s %2$s - %3$s";
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
public static class Builder {
@@ -109,8 +108,7 @@ public Object[] getObjects() {
@Override
public String toString() {
- final DateFormat dateFormat = new SimpleDateFormat(DATE_TIME_FORMAT, Locale.US);
- final String formattedTime = dateFormat.format(new Date(time));
+ final String formattedTime = DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(time));
String formattedMsg = String.format(TO_STRING_FORMAT, formattedTime, logLevel.toString(), message);
if (throwable != null) {
diff --git a/nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java b/nifi-api/src/test/java/org/apache/nifi/components/EnumDescribedValue.java
similarity index 93%
rename from nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java
rename to nifi-api/src/test/java/org/apache/nifi/components/EnumDescribedValue.java
index e09b9e827847a..1e4fef8c5f728 100644
--- a/nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java
+++ b/nifi-api/src/test/java/org/apache/nifi/components/EnumDescribedValue.java
@@ -16,7 +16,7 @@
*/
package org.apache.nifi.components;
-public enum EnumAllowableValue implements DescribedValue {
+public enum EnumDescribedValue implements DescribedValue {
GREEN {
@Override
diff --git a/nifi-api/src/test/java/org/apache/nifi/components/EnumNotDescribedValue.java b/nifi-api/src/test/java/org/apache/nifi/components/EnumNotDescribedValue.java
new file mode 100644
index 0000000000000..a280c9c70c370
--- /dev/null
+++ b/nifi-api/src/test/java/org/apache/nifi/components/EnumNotDescribedValue.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+public enum EnumNotDescribedValue {
+ GREEN, RED, BLUE;
+}
diff --git a/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java b/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
index fcaa0f7a1f21d..c3288e8f5f521 100644
--- a/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
+++ b/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java
@@ -20,17 +20,16 @@
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.expression.ExpressionLanguageScope;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -40,77 +39,180 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
-/**
- * Regression test for issue NIFI-49, to ensure that if a Processor's Property's
- * Default Value is not allowed, the Exception thrown should indicate what the
- * default value is
- */
public class TestPropertyDescriptor {
- private static Builder invalidDescriptorBuilder;
- private static Builder validDescriptorBuilder;
private static final String DEFAULT_VALUE = "Default Value";
private static final String DEPENDENT_PROPERTY_NAME = "dependentProperty";
- @BeforeAll
- public static void setUp() {
- validDescriptorBuilder = new PropertyDescriptor.Builder().name("").allowableValues("Allowable Value", "Another Allowable Value").defaultValue("Allowable Value");
- invalidDescriptorBuilder = new PropertyDescriptor.Builder().name("").allowableValues("Allowable Value", "Another Allowable Value").defaultValue(DEFAULT_VALUE);
- }
-
- @Test
- void testExceptionThrownByDescriptorWithInvalidDefaultValue() {
- IllegalStateException exception = assertThrows(IllegalStateException.class, () -> invalidDescriptorBuilder.build());
- assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]") );
- }
-
- @Test
- void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() {
- assertNotNull(validDescriptorBuilder.build());
+ @Nested
+ class RegardingDefaultValue {
+ @Test
+ void supportsStringValues() {
+ final PropertyDescriptor descriptor = builder().defaultValue(DEFAULT_VALUE).build();
+
+ assertEquals(DEFAULT_VALUE, descriptor.getDefaultValue());
+ }
+
+ @Test
+ void supportsDescribedValuesValues() {
+ final PropertyDescriptor descriptor = builder().defaultValue(EnumDescribedValue.GREEN).build();
+
+ assertEquals(EnumDescribedValue.GREEN.getValue(), descriptor.getDefaultValue());
+ }
+
+ /**
+ * Regression test for issue NIFI-49, to ensure that if a Processor's Property's
+ * Default Value is not allowed, the Exception thrown should indicate what the default value is
+ */
+ @Test
+ void throwsIllegalStateExceptionWhenDefaultValueNotInAllowableValues() {
+ IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
+ builder().allowableValues("NOT DEFAULT", "OTHER NOT DEFAULT").defaultValue(DEFAULT_VALUE).build();
+ });
+ assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]"));
+ }
+
+ @Test
+ void canBeCleared() {
+ final PropertyDescriptor descriptorWithDefault = builder().defaultValue(DEFAULT_VALUE).build();
+ final PropertyDescriptor resetDescriptor = builder(descriptorWithDefault).clearDefaultValue().build();
+
+ assertNull(resetDescriptor.getDefaultValue());
+ }
}
- @Test
- void testDefaultValueWithDescribedValue() {
- final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder()
- .name("defaultDescribedValueDescriptor")
- .defaultValue(EnumAllowableValue.GREEN)
- .build();
+ @Nested
+ class RegardingAllowableValues {
- assertNotNull(propertyDescriptor);
- assertEquals(EnumAllowableValue.GREEN.getValue(), propertyDescriptor.getDefaultValue());
- }
+ private static final Comparator allowableValueComparator = Comparator.comparing(AllowableValue::getValue);
+ private final List expectedMinimalAllowableValues =
+ List.of(new AllowableValue("GREEN"), new AllowableValue("RED"), new AllowableValue("BLUE"));
+ private final List expectedAllowableValuesWithDescription =
+ Arrays.stream(EnumDescribedValue.values()).map(AllowableValue::fromDescribedValue).toList();
- @Test
- void testAllowableValuesWithEnumClass() {
- final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder()
- .name("enumAllowableValueDescriptor")
- .allowableValues(EnumAllowableValue.class)
- .build();
+ @Test
+ void supportsStringVarArgValues() {
+ final List expected = expectedMinimalAllowableValues;
- assertNotNull(propertyDescriptor);
+ final PropertyDescriptor descriptor = builder().allowableValues("GREEN", "RED", "BLUE").build();
+ final List actual = descriptor.getAllowableValues();
- final List expectedAllowableValues = Arrays.stream(EnumAllowableValue.values())
- .map(enumValue -> new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription()))
- .collect(Collectors.toList());
- assertEquals(expectedAllowableValues, propertyDescriptor.getAllowableValues());
- }
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
- @Test
- void testAllowableValuesWithEnumSet() {
- final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder()
- .name("enumAllowableValueDescriptor")
- .allowableValues(EnumSet.of(
- EnumAllowableValue.GREEN,
- EnumAllowableValue.BLUE
- ))
- .build();
+ @Test
+ void supportsStringSetValues() {
+ final List expected = sort(expectedMinimalAllowableValues);
- assertNotNull(propertyDescriptor);
+ final PropertyDescriptor descriptor = builder().allowableValues(Set.of("GREEN", "RED", "BLUE")).build();
+ // the iteration order of sets is not guaranteed by all implementations, thus we unify the order here
+ final List actual = sort(descriptor.getAllowableValues());
- final List expectedAllowableValues = Stream.of(EnumAllowableValue.GREEN, EnumAllowableValue.BLUE)
- .map(enumValue -> new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription()))
- .collect(Collectors.toList());
- assertEquals(expectedAllowableValues, propertyDescriptor.getAllowableValues());
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsEnumArrayValues() {
+ final List expected = expectedMinimalAllowableValues;
+
+ final PropertyDescriptor descriptor = builder().allowableValues(EnumNotDescribedValue.values()).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ void supportsDescribedValueEnumArrayValues() {
+ final List expected = expectedAllowableValuesWithDescription;
+
+ final Enum[] enumArray = EnumDescribedValue.values();
+ final PropertyDescriptor descriptor = builder().allowableValues(enumArray).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsEnumClassValues() {
+ final List expected = expectedMinimalAllowableValues;
+
+ final PropertyDescriptor descriptor = builder().allowableValues(EnumNotDescribedValue.class).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsDescribedValueEnumClassValues() {
+ final List expected = expectedAllowableValuesWithDescription;
+
+ final PropertyDescriptor descriptor = builder().allowableValues(EnumDescribedValue.class).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsEnumSetValues() {
+ final List expected = expectedMinimalAllowableValues;
+
+ final PropertyDescriptor descriptor = builder().allowableValues(EnumSet.allOf(EnumNotDescribedValue.class)).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsDescribedValueEnumSetValues() {
+ final List expected = expectedAllowableValuesWithDescription;
+
+ final PropertyDescriptor descriptor = builder().allowableValues(EnumSet.allOf(EnumDescribedValue.class)).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ @Test
+ void supportsDescribedValueVarArgValues() {
+ final List expected = expectedAllowableValuesWithDescription;
+
+ final PropertyDescriptor descriptor = builder()
+ .allowableValues(EnumDescribedValue.GREEN, EnumDescribedValue.RED, EnumDescribedValue.BLUE).build();
+ final List actual = descriptor.getAllowableValues();
+
+ assertEquals(expected, actual); // equals only compares getValue()
+ assertEquals(displayNamesOf(expected), displayNamesOf(actual));
+ assertEquals(descriptionsOf(expected), descriptionsOf(actual));
+ }
+
+ private List sort(final List allowableValues) {
+ return allowableValues.stream().sorted(allowableValueComparator).toList();
+ }
+
+ private List displayNamesOf(final List allowableValues) {
+ return allowableValues.stream().map(AllowableValue::getDisplayName).toList();
+ }
+
+ private List descriptionsOf(final List allowableValues) {
+ return allowableValues.stream().map(AllowableValue::getDescription).toList();
+ }
}
@Test
@@ -121,7 +223,7 @@ void testDependsOnWithEnumValue() {
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor.Builder()
.name("enumDependsOnDescriptor")
- .dependsOn(dependentPropertyDescriptor, EnumAllowableValue.RED)
+ .dependsOn(dependentPropertyDescriptor, EnumDescribedValue.RED)
.build();
assertNotNull(propertyDescriptor);
@@ -133,17 +235,17 @@ void testDependsOnWithEnumValue() {
final Set dependentValues = dependency.getDependentValues();
assertEquals(1, dependentValues.size());
final String dependentValue = dependentValues.iterator().next();
- assertEquals(EnumAllowableValue.RED.getValue(), dependentValue);
+ assertEquals(EnumDescribedValue.RED.getValue(), dependentValue);
}
@Test
void testExternalResourceIgnoredIfELWithAttributesPresent() {
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
- .name("dir")
- .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
- .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
- .required(false)
- .build();
+ .name("dir")
+ .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
+ .expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
+ .required(false)
+ .build();
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
@@ -159,11 +261,11 @@ void testExternalResourceIgnoredIfELWithAttributesPresent() {
@Test
void testExternalResourceConsideredIfELVarRegistryPresent() {
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
- .name("dir")
- .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)
- .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
- .required(false)
- .build();
+ .name("dir")
+ .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)
+ .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
+ .required(false)
+ .build();
final AtomicReference variable = new AtomicReference<>("__my_var__");
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
@@ -190,9 +292,9 @@ void testExternalResourceConsideredIfELVarRegistryPresent() {
// Consider if Expression Language is not supported.
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(false);
final PropertyDescriptor withElNotAllowed = new PropertyDescriptor.Builder()
- .fromPropertyDescriptor(descriptor)
- .expressionLanguageSupported(ExpressionLanguageScope.NONE)
- .build();
+ .fromPropertyDescriptor(descriptor)
+ .expressionLanguageSupported(ExpressionLanguageScope.NONE)
+ .build();
// Expression will not be evaluated, so the directory being looked at will literally be ${TestPropertyDescriptor.Var1}
assertFalse(withElNotAllowed.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid());
@@ -232,4 +334,12 @@ void testClearingValues() {
assertTrue(pd2.getDependencies().isEmpty());
assertNull(pd2.getAllowableValues());
}
+
+ private Builder builder() {
+ return new PropertyDescriptor.Builder().name("propertyName");
+ }
+
+ private Builder builder(final PropertyDescriptor propertyDescriptor) {
+ return new PropertyDescriptor.Builder().fromPropertyDescriptor(propertyDescriptor);
+ }
}
diff --git a/nifi-assembly/NOTICE b/nifi-assembly/NOTICE
index 65553d8b0d522..c8ac921909379 100644
--- a/nifi-assembly/NOTICE
+++ b/nifi-assembly/NOTICE
@@ -43,7 +43,7 @@ This includes derived works from the Apache Hive (ASLv2 licensed) project (https
This includes derived works from the Apache Software License V2 library Jolt (https://github.com/bazaarvoice/jolt)
Copyright 2013-2014 Bazaarvoice, Inc
The derived work is adapted from com.bazaarvoice.jolt.chainr.ChainrBuilder.java, com.bazaarvoice.jolt.chainr.spec.ChainrSpec.java,
- com.bazaarvoice.jolt.chainr.spec.ChainrEntry.java and can be found in the org.apache.nifi.processors.standard.util.jolt.TransformFactory.java class.
+ com.bazaarvoice.jolt.chainr.spec.ChainrEntry.java and can be found in the org.apache.nifi.processors.jolt.TransformFactory.java class.
This includes derived works from Elastic Logstash (https://github.com/elastic/logstash/tree/v1.4.0/) and modified by Anthony Corbacho, and contributors
available under an Apache Software License V2.
diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index 630807bfe7873..ca2e987c2bd96 100644
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -98,16 +98,6 @@ language governing permissions and limitations under the License. -->
-
- jakarta.servlet
- jakarta.servlet-api
- compile
-
-
- org.eclipse.jetty.toolchain
- jetty-schemas
- compile
- ch.qos.logbacklogback-classic
@@ -788,7 +778,7 @@ language governing permissions and limitations under the License. -->
org.apache.nifi
- nifi-jolt-record-nar
+ nifi-jolt-nar2.0.0-SNAPSHOTnar
diff --git a/nifi-assembly/src/main/assembly/core.xml b/nifi-assembly/src/main/assembly/core.xml
index d18c9c0c3f567..0ab9a38420275 100644
--- a/nifi-assembly/src/main/assembly/core.xml
+++ b/nifi-assembly/src/main/assembly/core.xml
@@ -29,9 +29,6 @@
org.slf4j:jul-to-slf4jch.qos.logback:logback-classicch.qos.logback:logback-core
-
- jakarta.servlet:jakarta.servlet-api
- org.eclipse.jetty.toolchain:jetty-schemasorg.apache.nifi:nifi-apiorg.apache.nifi:nifi-framework-api
diff --git a/nifi-code-coverage/pom.xml b/nifi-code-coverage/pom.xml
index 0c8b891d5dc77..b0904b5770f68 100644
--- a/nifi-code-coverage/pom.xml
+++ b/nifi-code-coverage/pom.xml
@@ -30,6 +30,7 @@
1.10.141.6.01.24.0
+ 2.12.0
@@ -108,6 +109,22 @@
core-io1.7.24
+
+
+ org.apache.sshd
+ sshd-core
+ ${org.apache.sshd.version}
+
+
+ org.apache.sshd
+ sshd-sftp
+ ${org.apache.sshd.version}
+
+
+ org.apache.sshd
+ sshd-osgi
+ ${org.apache.sshd.version}
+
@@ -1002,6 +1019,11 @@
nifi-event-transport2.0.0-SNAPSHOT
+
+ org.apache.nifi
+ nifi-file-transfer
+ 2.0.0-SNAPSHOT
+ org.apache.nifinifi-hadoop-utils
@@ -1239,7 +1261,7 @@
org.apache.nifi
- nifi-jolt-record-processors
+ nifi-jolt-processors2.0.0-SNAPSHOT
@@ -1547,7 +1569,7 @@
org.apache.nifi
- nifi-standard-utils
+ nifi-jolt-utils2.0.0-SNAPSHOT
diff --git a/nifi-commons/nifi-expression-language/pom.xml b/nifi-commons/nifi-expression-language/pom.xml
index 41f2c129c0441..4486a1695904b 100644
--- a/nifi-commons/nifi-expression-language/pom.xml
+++ b/nifi-commons/nifi-expression-language/pom.xml
@@ -114,7 +114,6 @@
com.jayway.jsonpathjson-path
- 2.8.0com.fasterxml.jackson.core
diff --git a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
index 142ba945bc8ce..2ff6ffc64e6d6 100644
--- a/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
+++ b/nifi-commons/nifi-expression-language/src/main/java/org/apache/nifi/attribute/expression/language/StandardPropertyValue.java
@@ -237,13 +237,17 @@ public ResourceReferences asResources() {
}
@Override
- public & DescribedValue> E asDescribedValue(Class enumType) throws IllegalArgumentException {
+ public > E asAllowableValue(Class enumType) throws IllegalArgumentException {
if (rawValue == null) {
return null;
}
for (E enumConstant : enumType.getEnumConstants()) {
- if (enumConstant.getValue().equals(rawValue)) {
+ if (enumConstant instanceof DescribedValue describedValue) {
+ if (describedValue.getValue().equals(rawValue)) {
+ return enumConstant;
+ }
+ } else if (enumConstant.name().equals(rawValue)) {
return enumConstant;
}
}
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index dd8ddcf700f49..41be112c2fe96 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -326,6 +326,9 @@ public class NiFiProperties extends ApplicationProperties {
public static final String PYTHON_CONTROLLER_DEBUGPY_HOST = "nifi.python.controller.debugpy.host";
public static final String PYTHON_CONTROLLER_DEBUGPY_LOGS_DIR = "nifi.python.controller.debugpy.logs.directory";
+ // kubernetes properties
+ public static final String CLUSTER_LEADER_ELECTION_KUBERNETES_LEASE_PREFIX = "nifi.cluster.leader.election.kubernetes.lease.prefix";
+
public static final String DEFAULT_PYTHON_WORKING_DIRECTORY = "./work/python";
// automatic diagnostic defaults
diff --git a/nifi-commons/nifi-property-protection-azure/pom.xml b/nifi-commons/nifi-property-protection-azure/pom.xml
index 0a65b8f8dd416..c641939d4df78 100644
--- a/nifi-commons/nifi-property-protection-azure/pom.xml
+++ b/nifi-commons/nifi-property-protection-azure/pom.xml
@@ -26,7 +26,7 @@
com.azureazure-sdk-bom
- 1.2.18
+ 1.2.19importpom
diff --git a/nifi-commons/nifi-property-protection-cipher/src/main/java/org/apache/nifi/properties/AesGcmSensitivePropertyProvider.java b/nifi-commons/nifi-property-protection-cipher/src/main/java/org/apache/nifi/properties/AesGcmSensitivePropertyProvider.java
index 1641bc14f9940..70f94486b9b0e 100644
--- a/nifi-commons/nifi-property-protection-cipher/src/main/java/org/apache/nifi/properties/AesGcmSensitivePropertyProvider.java
+++ b/nifi-commons/nifi-property-protection-cipher/src/main/java/org/apache/nifi/properties/AesGcmSensitivePropertyProvider.java
@@ -39,7 +39,7 @@
/**
* Sensitive Property Provider implementation using AES-GCM with configurable key sizes
*/
-class AesGcmSensitivePropertyProvider implements SensitivePropertyProvider {
+public class AesGcmSensitivePropertyProvider implements SensitivePropertyProvider {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final String SECRET_KEY_ALGORITHM = "AES";
private static final String DELIMITER = "||"; // "|" is not a valid Base64 character, so ensured not to be present in cipher text
diff --git a/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/Format.java b/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/Format.java
index 6c64b71646efb..b621f3dd5823d 100644
--- a/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/Format.java
+++ b/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/Format.java
@@ -21,10 +21,14 @@
import org.apache.nifi.record.path.StandardFieldValue;
import org.apache.nifi.record.path.paths.RecordPathSegment;
import org.apache.nifi.record.path.util.RecordPathUtils;
-import org.apache.nifi.serialization.record.util.DataTypeUtils;
import org.apache.nifi.util.StringUtils;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.Date;
+import java.util.TimeZone;
import java.util.stream.Stream;
public class Format extends RecordPathSegment {
@@ -52,22 +56,37 @@ public Stream evaluate(final RecordPathEvaluationContext context) {
final Stream fieldValues = recordPath.evaluate(context);
return fieldValues.filter(fv -> fv.getValue() != null)
.map(fv -> {
- final java.text.DateFormat dateFormat = getDateFormat(this.dateFormat, this.timeZoneID, context);
- if (dateFormat == null) {
+ final DateTimeFormatter dateTimeFormatter = getDateTimeFormatter(this.dateFormat, this.timeZoneID, context);
+ if (dateTimeFormatter == null) {
return fv;
}
- if (!(fv.getValue() instanceof Date) && !(fv.getValue() instanceof Number)) {
+ final Object fieldValue = fv.getValue();
+
+ final Instant instant;
+ if (fieldValue instanceof Date dateField) {
+ instant = Instant.ofEpochMilli(dateField.getTime());
+ } else if (fieldValue instanceof Number numberField) {
+ instant = Instant.ofEpochMilli(numberField.longValue());
+ } else {
return fv;
}
- final Date dateValue = DataTypeUtils.toDate(fv.getValue(), null, fv.getField().getFieldName());
- final String formatted = dateFormat.format(dateValue);
+ final ZoneId zoneId;
+ if (timeZoneID == null) {
+ zoneId = ZoneId.systemDefault();
+ } else {
+ final String timeZoneId = RecordPathUtils.getFirstStringValue(timeZoneID, context);
+ zoneId = TimeZone.getTimeZone(timeZoneId).toZoneId();
+ }
+
+ final ZonedDateTime dateTime = instant.atZone(zoneId);
+ final String formatted = dateTimeFormatter.format(dateTime);
return new StandardFieldValue(formatted, fv.getField(), fv.getParent().orElse(null));
});
}
- private java.text.DateFormat getDateFormat(final RecordPathSegment dateFormatSegment, final RecordPathSegment timeZoneID, final RecordPathEvaluationContext context) {
+ private DateTimeFormatter getDateTimeFormatter(final RecordPathSegment dateFormatSegment, final RecordPathSegment timeZoneID, final RecordPathEvaluationContext context) {
final String dateFormatString = RecordPathUtils.getFirstStringValue(dateFormatSegment, context);
if (StringUtils.isEmpty(dateFormatString)) {
return null;
@@ -75,13 +94,14 @@ private java.text.DateFormat getDateFormat(final RecordPathSegment dateFormatSeg
try {
if (timeZoneID == null) {
- return DataTypeUtils.getDateFormat(dateFormatString);
+ return DateTimeFormatter.ofPattern(dateFormatString);
} else {
final String timeZoneStr = RecordPathUtils.getFirstStringValue(timeZoneID, context);
if (StringUtils.isEmpty(timeZoneStr)) {
return null;
}
- return DataTypeUtils.getDateFormat(dateFormatString, timeZoneStr);
+ final ZoneId zoneId = TimeZone.getTimeZone(timeZoneStr).toZoneId();
+ return DateTimeFormatter.ofPattern(dateFormatString).withZone(zoneId);
}
} catch (final Exception e) {
return null;
diff --git a/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/ToDate.java b/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/ToDate.java
index 561f73ee3f1a2..6637cc4079e9a 100644
--- a/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/ToDate.java
+++ b/nifi-commons/nifi-record-path/src/main/java/org/apache/nifi/record/path/functions/ToDate.java
@@ -21,10 +21,16 @@
import org.apache.nifi.record.path.StandardFieldValue;
import org.apache.nifi.record.path.paths.RecordPathSegment;
import org.apache.nifi.record.path.util.RecordPathUtils;
-import org.apache.nifi.serialization.record.util.DataTypeUtils;
import org.apache.nifi.util.StringUtils;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
import java.util.Date;
+import java.util.TimeZone;
import java.util.stream.Stream;
public class ToDate extends RecordPathSegment {
@@ -53,28 +59,69 @@ public Stream evaluate(RecordPathEvaluationContext context) {
return fieldValues.filter(fv -> fv.getValue() != null)
.map(fv -> {
- if (!(fv.getValue() instanceof String)) {
+ final Object fieldValue = fv.getValue();
+ if (!(fieldValue instanceof String)) {
return fv;
}
- final java.text.DateFormat dateFormat = getDateFormat(this.dateFormat, this.timeZoneID, context);
+ final DateTimeFormatter dateTimeFormatter = getDateTimeFormatter(dateFormat, context);
final Date dateValue;
try {
- dateValue = DataTypeUtils.toDate(fv.getValue(), () -> dateFormat, fv.getField().getFieldName());
+ final TemporalAccessor parsed = dateTimeFormatter.parse(fieldValue.toString());
+
+ int year = 0;
+ if (parsed.isSupported(ChronoField.YEAR_OF_ERA)) {
+ year = parsed.get(ChronoField.YEAR_OF_ERA);
+ }
+
+ int month = 0;
+ if (parsed.isSupported(ChronoField.MONTH_OF_YEAR)) {
+ month = parsed.get(ChronoField.MONTH_OF_YEAR);
+ }
+
+ int day = 0;
+ if (parsed.isSupported(ChronoField.DAY_OF_MONTH)) {
+ day = parsed.get(ChronoField.DAY_OF_MONTH);
+ }
+
+ int hour = 0;
+ if (parsed.isSupported(ChronoField.HOUR_OF_DAY)) {
+ hour = parsed.get(ChronoField.HOUR_OF_DAY);
+ }
+
+ int minute = 0;
+ if (parsed.isSupported(ChronoField.MINUTE_OF_HOUR)) {
+ minute = parsed.get(ChronoField.MINUTE_OF_HOUR);
+ }
+
+ int second = 0;
+ if (parsed.isSupported(ChronoField.SECOND_OF_MINUTE)) {
+ second = parsed.get(ChronoField.SECOND_OF_MINUTE);
+ }
+
+ int nano = 0;
+ if (parsed.isSupported(ChronoField.MILLI_OF_SECOND)) {
+ nano = parsed.get(ChronoField.NANO_OF_SECOND);
+ }
+
+ ZoneId zoneId = getZoneId(context);
+ if (zoneId == null) {
+ zoneId = ZoneId.systemDefault();
+ }
+
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of(year, month, day, hour, minute, second, nano, zoneId);
+ final Instant instant = zonedDateTime.toInstant();
+ dateValue = Date.from(instant);
} catch (final Exception e) {
return fv;
}
- if (dateValue == null) {
- return fv;
- }
-
return new StandardFieldValue(dateValue, fv.getField(), fv.getParent().orElse(null));
});
}
- private java.text.DateFormat getDateFormat(final RecordPathSegment dateFormatSegment, final RecordPathSegment timeZoneID, final RecordPathEvaluationContext context) {
+ private DateTimeFormatter getDateTimeFormatter(final RecordPathSegment dateFormatSegment, final RecordPathEvaluationContext context) {
if (dateFormatSegment == null) {
return null;
}
@@ -85,18 +132,31 @@ private java.text.DateFormat getDateFormat(final RecordPathSegment dateFormatSeg
}
try {
- if (timeZoneID == null) {
- return DataTypeUtils.getDateFormat(dateFormatString);
- } else {
- final String timeZoneStr = RecordPathUtils.getFirstStringValue(timeZoneID, context);
- if (StringUtils.isEmpty(timeZoneStr)) {
- return null;
- }
- return DataTypeUtils.getDateFormat(dateFormatString, timeZoneStr);
+ final ZoneId zoneId = getZoneId(context);
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormatString);
+ if (zoneId != null) {
+ formatter.withZone(zoneId);
}
+ return formatter;
} catch (final Exception e) {
return null;
}
}
+ private ZoneId getZoneId(final RecordPathEvaluationContext context) {
+ final ZoneId zoneId;
+
+ if (timeZoneID == null) {
+ zoneId = null;
+ } else {
+ final String timeZoneStr = RecordPathUtils.getFirstStringValue(timeZoneID, context);
+ if (StringUtils.isEmpty(timeZoneStr)) {
+ zoneId = null;
+ } else {
+ zoneId = TimeZone.getTimeZone(timeZoneStr).toZoneId();
+ }
+ }
+
+ return zoneId;
+ }
}
diff --git a/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java b/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
index 8de0e9330a6a1..2b6341124c399 100644
--- a/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
+++ b/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
@@ -20,8 +20,6 @@
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
-import java.text.DateFormat;
-import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
@@ -55,6 +53,7 @@
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -86,10 +85,10 @@ public static void setSystemTimezone() {
@Test
public void testCompile() {
- System.out.println(RecordPath.compile("/person/name/last"));
- System.out.println(RecordPath.compile("/person[2]"));
- System.out.println(RecordPath.compile("//person[2]"));
- System.out.println(RecordPath.compile("/person/child[1]//sibling/name"));
+ RecordPath.compile("/person/name/last");
+ RecordPath.compile("/person[2]");
+ RecordPath.compile("//person[2]");
+ RecordPath.compile("/person/child[1]//sibling/name");
// contains is a 'filter function' so can be used as the predicate
RecordPath.compile("/name[contains(., 'hello')]");
@@ -1461,20 +1460,22 @@ public void testToDateFromString() {
values.put("date", "2017-10-20T11:00:00Z");
final Record record = new MapRecord(schema, values);
- assertTrue(RecordPath.compile("toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss'Z'\")").evaluate(record).getSelectedFields().findFirst().get().getValue() instanceof Date);
- assertTrue(RecordPath.compile("toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss'Z'\", \"GMT+8:00\")").evaluate(record).getSelectedFields().findFirst().get().getValue() instanceof Date);
+ final Object evaluated = RecordPath.compile("toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss'Z'\")").evaluate(record).getSelectedFields().findFirst().get().getValue();
+ assertInstanceOf(java.util.Date.class, evaluated);
+
+ final Object evaluatedTimeZone = RecordPath.compile("toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss'Z'\", \"GMT+8:00\")").evaluate(record).getSelectedFields().findFirst().get().getValue();
+ assertInstanceOf(java.util.Date.class, evaluatedTimeZone);
}
@Test
- public void testToDateFromLong() throws ParseException {
+ public void testToDateFromLong() {
final List fields = new ArrayList<>();
fields.add(new RecordField("id", RecordFieldType.INT.getDataType()));
fields.add(new RecordField("date", RecordFieldType.LONG.getDataType()));
final RecordSchema schema = new SimpleRecordSchema(fields);
- final DateFormat dateFormat = DataTypeUtils.getDateFormat("yyyy-MM-dd");
- final long dateValue = dateFormat.parse("2017-10-20T11:00:00Z").getTime();
+ final long dateValue = 0L;
final Map values = new HashMap<>();
values.put("id", 48);
@@ -1540,10 +1541,10 @@ public void testFormatDateFromString() {
assertEquals(adjustedDateTime, fieldValue3.getValue());
final FieldValue fieldValueUnchanged = RecordPath.compile("format( toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss\"), 'INVALID' )").evaluate(record).getSelectedFields().findFirst().get();
- assertEquals(localDateFormatted, fieldValueUnchanged.getValue().toString());
+ assertInstanceOf(java.util.Date.class, fieldValueUnchanged.getValue());
final FieldValue fieldValueUnchanged2 = RecordPath.compile("format( toDate(/date, \"yyyy-MM-dd'T'HH:mm:ss\"), 'INVALID' , 'INVALID')")
.evaluate(record).getSelectedFields().findFirst().get();
- assertEquals(localDateFormatted, fieldValueUnchanged2.getValue().toString());
+ assertInstanceOf(java.util.Date.class, fieldValueUnchanged2.getValue());
}
@Test
diff --git a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
index 2ba5c2607fc0d..1ab6539297675 100644
--- a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
+++ b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
@@ -19,6 +19,8 @@
import org.apache.nifi.serialization.SchemaValidationException;
import org.apache.nifi.serialization.SimpleRecordSchema;
+import org.apache.nifi.serialization.record.field.FieldConverter;
+import org.apache.nifi.serialization.record.field.StandardFieldConverterRegistry;
import org.apache.nifi.serialization.record.type.ArrayDataType;
import org.apache.nifi.serialization.record.type.ChoiceDataType;
import org.apache.nifi.serialization.record.type.MapDataType;
@@ -29,7 +31,7 @@
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
-import java.text.DateFormat;
+import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,7 +44,6 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Supplier;
public class MapRecord implements Record {
private static final Logger logger = LoggerFactory.getLogger(MapRecord.class);
@@ -225,7 +226,8 @@ public String getAsString(final String fieldName) {
return convertToString(getValue(fieldName), dataTypeOption.get().getFormat());
}
- return DataTypeUtils.toString(getValue(fieldName), (Supplier) null);
+ final FieldConverter
*
*
- * Note, however, that this class is not intended to replace SimpleDateFormat, as it does not perform the actual parsing but instead only determines whether or not
- * a given input text matches the pattern, so that if it does, a SimpleDateFormat can be used parse the input.
+ * Note, however, that this class is not intended to replace DateTimeFormatter, as it does not perform the actual parsing but instead only determines whether or not
+ * a given input text matches the pattern, so that if it does, a DateTimeFormatter can be used parse the input.
*
*/
public interface DateTimeMatcher {
diff --git a/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/DateTimeMatcherCompiler.java b/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/DateTimeMatcherCompiler.java
index 5bf854be2505b..b0215b56b76c4 100644
--- a/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/DateTimeMatcherCompiler.java
+++ b/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/DateTimeMatcherCompiler.java
@@ -49,8 +49,8 @@ DateTimeMatcher compile(final String format) {
matchers.add(regexMatcher);
- // Use the SimpleDateFormatMatcher only if our regex matches. This allows us to parse the date only to guarantee that we are correct if we say that the input text matches.
- matchers.add(new SimpleDateFormatMatcher(format));
+ // Use the DateTimeFormatterMatcher only if our regex matches. This allows us to parse the date only to guarantee that we are correct if we say that the input text matches.
+ matchers.add(new DateTimeFormatterMatcher(format));
return new ListDateTimeMatcher(matchers);
}
}
diff --git a/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/RegexDateTimeMatcher.java b/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/RegexDateTimeMatcher.java
index 48f07744936b6..8bd27ff1d5fc3 100644
--- a/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/RegexDateTimeMatcher.java
+++ b/nifi-commons/nifi-utils/src/main/java/org/apache/nifi/util/text/RegexDateTimeMatcher.java
@@ -487,7 +487,7 @@ private void addGeneralTimeZone() {
}
private String getGMTOffsetTimeZone() {
- // From SimpleDateFormat JavaDocs, GMTOffsetTimeZone defined as: GMT Sign Hours : Minutes
+ // From Date Format JavaDocs, GMTOffsetTimeZone defined as: GMT Sign Hours : Minutes
// Sign defined as '-' or '+'
// Hours defined as 1 or 2 digits, Minutes defined as 1 or 2 digits
// Digit defined as number between 0-9
diff --git a/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/TestFormatUtils.java b/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/TestFormatUtils.java
index 454b086e2650b..05718393223b0 100644
--- a/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/TestFormatUtils.java
+++ b/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/TestFormatUtils.java
@@ -25,14 +25,12 @@
import org.junit.jupiter.params.provider.ValueSource;
import java.text.DecimalFormatSymbols;
-import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
-import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@@ -404,8 +402,8 @@ private static Stream getFormatDataSize() {
@ParameterizedTest
@MethodSource("getParseToInstantUsingFormatterWithoutZones")
- public void testParseToInstantUsingFormatterWithoutZones(String pattern, String parsedDateTime, String systemDefaultZoneId, String expectedUtcDateTime) throws Exception {
- checkSameResultsWithSimpleDateFormat(pattern, parsedDateTime, systemDefaultZoneId, null, expectedUtcDateTime);
+ public void testParseToInstantUsingFormatterWithoutZones(String pattern, String parsedDateTime, String systemDefaultZoneId, String expectedUtcDateTime) {
+ checkSameResultsWithFormatter(pattern, parsedDateTime, systemDefaultZoneId, null, expectedUtcDateTime);
}
private static Stream getParseToInstantUsingFormatterWithoutZones() {
@@ -428,8 +426,8 @@ private static Stream getParseToInstantUsingFormatterWithoutZones() {
@ParameterizedTest
@MethodSource("getParseToInstantUsingFormatterWithZone")
- public void testParseToInstantUsingFormatterWithZone(String pattern, String parsedDateTime, String systemDefaultZoneId, String formatZoneId, String expectedUtcDateTime) throws Exception {
- checkSameResultsWithSimpleDateFormat(pattern, parsedDateTime, systemDefaultZoneId, formatZoneId, expectedUtcDateTime);
+ public void testParseToInstantUsingFormatterWithZone(String pattern, String parsedDateTime, String systemDefaultZoneId, String formatZoneId, String expectedUtcDateTime) {
+ checkSameResultsWithFormatter(pattern, parsedDateTime, systemDefaultZoneId, formatZoneId, expectedUtcDateTime);
}
private static Stream getParseToInstantUsingFormatterWithZone() {
@@ -448,8 +446,8 @@ private static Stream getParseToInstantUsingFormatterWithZone() {
@ParameterizedTest
@MethodSource("getParseToInstantWithZonePassedInText")
- public void testParseToInstantWithZonePassedInText(String pattern, String parsedDateTime, String systemDefaultZoneId, String expectedUtcDateTime) throws Exception {
- checkSameResultsWithSimpleDateFormat(pattern, parsedDateTime, systemDefaultZoneId, null, expectedUtcDateTime);
+ public void testParseToInstantWithZonePassedInText(String pattern, String parsedDateTime, String systemDefaultZoneId, String expectedUtcDateTime) {
+ checkSameResultsWithFormatter(pattern, parsedDateTime, systemDefaultZoneId, null, expectedUtcDateTime);
}
private static Stream getParseToInstantWithZonePassedInText() {
@@ -465,27 +463,19 @@ private static Stream getParseToInstantWithZonePassedInText() {
Arguments.of(pattern, "2020-01-01 02:00:00 +0000", UTC_TIME_ZONE_ID, "2020-01-01T02:00:00"));
}
- private void checkSameResultsWithSimpleDateFormat(String pattern, String parsedDateTime, String systemDefaultZoneId, String formatZoneId, String expectedUtcDateTime) throws Exception {
+ private void checkSameResultsWithFormatter(String pattern, String parsedDateTime, String systemDefaultZoneId, String formatZoneId, String expectedUtcDateTime) {
TimeZone current = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone(systemDefaultZoneId));
try {
- checkSameResultsWithSimpleDateFormat(pattern, parsedDateTime, formatZoneId, expectedUtcDateTime);
+ checkSameResultsWithFormatter(pattern, parsedDateTime, formatZoneId, expectedUtcDateTime);
} finally {
TimeZone.setDefault(current);
}
}
- private void checkSameResultsWithSimpleDateFormat(String pattern, String parsedDateTime, String formatterZoneId, String expectedUtcDateTime) throws Exception {
+ private void checkSameResultsWithFormatter(String pattern, String parsedDateTime, String formatterZoneId, String expectedUtcDateTime) {
Instant expectedInstant = LocalDateTime.parse(expectedUtcDateTime).atZone(ZoneOffset.UTC).toInstant();
- // reference implementation
- SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US);
- if (formatterZoneId != null) {
- sdf.setTimeZone(TimeZone.getTimeZone(formatterZoneId));
- }
- Instant simpleDateFormatResult = sdf.parse(parsedDateTime).toInstant();
- assertEquals(expectedInstant, simpleDateFormatResult);
-
// current implementation
DateTimeFormatter dtf = FormatUtils.prepareLenientCaseInsensitiveDateTimeFormatter(pattern);
if (formatterZoneId != null) {
diff --git a/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/text/TestRegexDateTimeMatcher.java b/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/text/TestRegexDateTimeMatcher.java
index aadc98b07c73a..28e7e5e1a80b4 100644
--- a/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/text/TestRegexDateTimeMatcher.java
+++ b/nifi-commons/nifi-utils/src/test/java/org/apache/nifi/util/text/TestRegexDateTimeMatcher.java
@@ -45,8 +45,6 @@ public void testCommonFormatsExpectedToPass() {
exampleToPattern.put("12 Dec 2018", "dd MMM yyyy");
exampleToPattern.put("12 December 2018", "dd MMM yyyy");
- // TODO: The following examples are taken from the SimpleDateFormat's JavaDoc. Ensure that this is not a licensing concern,
- // since it is not being distributed.
exampleToPattern.put("2001.07.04 AD at 12:08:56 PDT", "yyyy.MM.dd G 'at' HH:mm:ss z");
exampleToPattern.put("Wed, Jul 4, '01", "EEE, MMM d, ''yy");
exampleToPattern.put("12:08 PM", "h:mm a");
diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml
index 034b39f12313a..d860e6c8e2f3c 100644
--- a/nifi-commons/pom.xml
+++ b/nifi-commons/pom.xml
@@ -68,6 +68,7 @@
nifi-single-user-utilsnifi-site-to-site-clientnifi-socket-utils
+ nifi-swagger-integrationnifi-utilsnifi-uuid5nifi-hashicorp-vault
diff --git a/nifi-dependency-check-maven/suppressions.xml b/nifi-dependency-check-maven/suppressions.xml
index 8a3b463fea0b1..fa25b9355d1db 100644
--- a/nifi-dependency-check-maven/suppressions.xml
+++ b/nifi-dependency-check-maven/suppressions.xml
@@ -444,4 +444,19 @@
^pkg:maven/info\.picocli/picocli@.*$CVE-2015-0897
+
+ CVE-2023-36052 applies to Azure CLI not Azure Java libraries
+ ^pkg:maven/com\.azure/.*$
+ CVE-2023-36052
+
+
+ software.amazon.ion:ion-java is newer than com.amazonaws.ion:ion-java and does not share the same vulnerabilities
+ ^pkg:maven/software\.amazon\.ion/ion\-java@.*$
+ cpe:/a:amazon:ion
+
+
+ JSON Path 2.9.0 resolves CVE-2023-51074
+ ^pkg:maven/com\.jayway\.jsonpath/json\-path@2.9.0$
+ CVE-2023-51074
+
diff --git a/nifi-docker/dockerhub/sh/start.sh b/nifi-docker/dockerhub/sh/start.sh
index 584e55b13a891..8a9b35e3135bd 100755
--- a/nifi-docker/dockerhub/sh/start.sh
+++ b/nifi-docker/dockerhub/sh/start.sh
@@ -82,6 +82,9 @@ prop_replace 'nifi.analytics.connection.model.implementation' "${NIFI_ANALYTIC
prop_replace 'nifi.analytics.connection.model.score.name' "${NIFI_ANALYTICS_MODEL_SCORE_NAME:-rSquared}"
prop_replace 'nifi.analytics.connection.model.score.threshold' "${NIFI_ANALYTICS_MODEL_SCORE_THRESHOLD:-.90}"
+# Set kubernetes properties
+prop_replace 'nifi.cluster.leader.election.kubernetes.lease.prefix' "${NIFI_CLUSTER_LEADER_ELECTION_KUBERNETES_LEASE_PREFIX:-}"
+
# Add NAR provider properties
# nifi-registry NAR provider
if [ -n "${NIFI_NAR_LIBRARY_PROVIDER_NIFI_REGISTRY_URL}" ]; then
diff --git a/nifi-docker/dockerhub/sh/update_cluster_state_management.sh b/nifi-docker/dockerhub/sh/update_cluster_state_management.sh
index 718e52de77687..06879470e58af 100755
--- a/nifi-docker/dockerhub/sh/update_cluster_state_management.sh
+++ b/nifi-docker/dockerhub/sh/update_cluster_state_management.sh
@@ -28,4 +28,6 @@ edit_property() {
}
edit_property 'Connect String' "${NIFI_ZK_CONNECT_STRING}"
-edit_property "Root Node" "${NIFI_ZK_ROOT_NODE}"
\ No newline at end of file
+edit_property "Root Node" "${NIFI_ZK_ROOT_NODE}"
+
+edit_property 'ConfigMap Name Prefix' "${NIFI_KUBERNETES_CONFIGMAP_NAME_PREFIX}"
\ No newline at end of file
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 332290f382162..66ac8ef0f2e7e 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -24,7 +24,7 @@ Apache NiFi Team
Apache NiFi can run on something as simple as a laptop, but it can also be clustered across many enterprise-class servers. Therefore, the amount of hardware and memory needed will depend on the size and nature of the dataflow involved. The data is stored on disk while NiFi is processing it. So NiFi needs to have sufficient disk space allocated for its various repositories, particularly the content repository, flowfile repository, and provenance repository (see the <> section for more information about these repositories). NiFi has the following minimum system requirements:
* Requires Java 21
-* Use of Python-based Processors (beta feature) requires Python 3.9+
+* Use of Python-based Processors (beta feature) requires Python 3.9, 3.10 or 3.11
* Supported Operating Systems:
** Linux
** Unix
@@ -159,12 +159,6 @@ Set how long sockets stay in a TIMED_WAIT state when closed::
You don't want your sockets to sit and linger too long given that you want to be
able to quickly setup and teardown new sockets. It is a good idea to read more about
it and adjust to something like
-
-for kernel 2.6
-----
-sudo sysctl -w net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait="1"
-----
-for kernel 3.0
----
sudo sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait="1"
----
@@ -237,7 +231,7 @@ The `name` attribute must start with `deprecation`, followed by the component cl
NiFi is a Java-based application. NiFi 2.0 introduces support for a Python-based Processor API. This capability is still
considered to be in "Beta" mode and should not be used in production. By default, support for Python-based Processors is disabled. In order to enable it,
-Python 3.9+ must be installed on the NiFi node.
+Python 3.9, 3.10 or 3.11 must be installed on the NiFi node (Python 3.12 is not supported yet).
The following properties may be used to configure the Python 3 installation and process management. These properties are all located under the
"Python Extensions" heading in the _nifi.properties_ file:
diff --git a/nifi-docs/src/main/asciidoc/python-developer-guide.adoc b/nifi-docs/src/main/asciidoc/python-developer-guide.adoc
index 415b2f0ba4f21..d610c482a673d 100644
--- a/nifi-docs/src/main/asciidoc/python-developer-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/python-developer-guide.adoc
@@ -315,7 +315,7 @@ class PrettyPrintJson(FlowFileTransform):
numspaces = PropertyDescriptor(name="Number of Spaces",
description="Number of spaces to use for pretty-printing",
validators=[StandardValidators.POSITIVE_INTEGER_VALIDATOR],
- defaultValue="4",
+ default_value="4",
required=True)
self.descriptors = [numspaces]
@@ -424,10 +424,92 @@ that there are no longer any invocations of the `transform` method running when
+[[documenting_use_cases]]
+== Documenting Use Cases
+
+No matter how powerful a piece of software is, it has no value unless people are able to use it. To that end, documentation of Processors is
+very important. While a description of the Processor should be provided in the `ProcessorDetails` class and each PropertyDescriptor is expected to have a description,
+it is usually helpful to also call out specific use cases that can be performed by the Processor. This is particularly important for Processors that perform
+more generalized transformations on objects, where a single Processor may be capable of performing multiple tasks, based on its configuration.
+
+[[use_case_decorator]]
+=== The `@use_case` Decorator
+
+The `@use_case` decorator, defined in the `nifiapi.documentation` module can facilitate this. The decorator takes four arguments:
+
+- `description`: A simple 1 (at most 2) sentence description of the use case. Generally, this should not include any extraneous details,
+ such as caveats, etc. Those can be provided using the `notes` argument. The description is required.
+- `notes`: Most of the time, 1-2 sentences is sufficient to describe a use case. Those 1-2 sentence should then be returned
+ by the `description`. In the event that the description is not sufficient, details may be provided to
+ further explain, by providing caveats, etc. This is optional.
+- `keywords`: An array of keywords that can be associated with the use case. This is optional.
+- `configuration`: A description of how to configure the Processor for this particular use case. This may include explicit values to set for some properties,
+ and may include instructions on how to choose the appropriate value for other properties. The configuration is required.
+
+A single Processor may have multiple `@use_case` decorators.
+
+
+[[multi_processor_use_case_decorator]]
+=== The `@multi_processor_use_case` Decorator
+
+When designing and creating Processors, it is important to keep in mind the idea of loose coupling. One Processor should not be dependent on another Processor
+in order to perform its task. That being said, it is often advantageous to build Processors that are designed to work well together. For example, a Processor that
+is able to perform a listing of files in a directory can provide an important capability in and of itself. Similarly, a Processor that is able to ingest the contents
+of a specific file and make that file's contents the contents of a FlowFile is also an important capability in and of itself. But far more powerful than either of these
+individual capabilities is the notion of being able to compose a flow that lists all files in a directory and then ingests each of those files as a FlowFile. This is
+done by using a combination of the two. As such, it is important that the two Processors be able to work together in such a way that the output of the first is
+easily understood as the input of the second.
+
+In this case, it makes sense to document this composition of Processors as a use case so that users can understand how to compose such a pipeline. This is accomplished
+by using the `@multi_processor_use_case` decorator. This decorator is very similar to the <> but instead of a `configuration` element, it has a
+`configurations` element, which is a `list` of `ProcessorConfiguration` objects, where each `ProcessorConfiguration` object has both a `processor_type`, which is the
+name of the Processor, and a `configuration` that explains how to configure that particular Processor. The `configuration` element typically also explains how to connect
+outbound Relationships.
+
+For example, we might use these decorators as such:
+----
+@use_case(description="Retrieve the contents of a given file on disk and create a FlowFile from it without modifying the file",
+ keywords=["file", "filesystem"],
+ configuration="""
+ Set the 'Filename' property to the fully qualified path of the file to ingest
+ Set the 'Completion Strategy' to 'None'
+ """)
+@use_case(description="Retrieve the contents of a given file on disk and create a FlowFile from it, deleting the local file upon success",
+ keywords=["file", "filesystem"],
+ configuration="""
+ Set the 'Filename' property to the fully qualified path of the file to ingest
+ Set the 'Completion Strategy' to 'Delete'
+ """)
+@multi_processor_use_case(
+ description="Ingest all files from a landing directory on the filesystem and delete them after ingesting them.",
+ keywords=["file", "filesystem", "landing directory"],
+ configurations=[
+ ProcessorConfiguration(
+ processor_type="org.apache.nifi.processors.standard.ListFile",
+ configuration="""
+ Set 'Input Directory' to the directory that files should be ingested from
+ Set 'Input Directory Location' to 'Local'
+ """
+ ),
+ ProcessorConfiguration(
+ processor_type="FetchFile",
+ configuration="""
+ Set the 'Filename' property to `${absolute.path}/${filename}`
+ Set the 'Completion Strategy' to 'Delete'
+ """
+ )
+ ])
+class FetchFile(FlowFileTransform):
+----
+
+Note that in this case, we are able to specifically tell the user that the Filename property of FetchFile should be set to the value `${absolute.path}/${filename}`
+because we know that the ListFile Processor will produce these attributes for us.
+
+
[[requirements]]
== Requirements
-The Python API requires that Python 3.9+ is available on the machine hosting NiFi.
+The Python API requires that Python 3.9, 3.10 or 3.11 is available on the machine hosting NiFi (Python 3.12 is not supported yet).
Each Processor may have its own list of requirements / dependencies. These are made available to the Processor by creating a separate
environment for each Processor implementation (not for each instance of a Processor on the canvas). PyPI is then used to install these
diff --git a/nifi-docs/src/main/asciidoc/record-path-guide.adoc b/nifi-docs/src/main/asciidoc/record-path-guide.adoc
index 864dbcbfb80a6..a4d0323c4cb30 100644
--- a/nifi-docs/src/main/asciidoc/record-path-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/record-path-guide.adoc
@@ -640,7 +640,7 @@ Converts a Date to a String in the given format with an optional time zone. The
time zone when the second argument is not provided.
The first argument to this function must be a Date or a Number, and the second argument must be a format String that
-follows the Java SimpleDateFormat, and the third argument, optional, must be a format String that
+follows the Java DateTimeFormatter, and the third argument, optional, must be a format String that
either an abbreviation such as "PST", a full name such as "America/Los_Angeles", or a custom ID such as "GMT-8:00"
For example, given a schema such as:
diff --git a/nifi-external/nifi-kafka-connect/nifi-kafka-connector-assembly/pom.xml b/nifi-external/nifi-kafka-connect/nifi-kafka-connector-assembly/pom.xml
index 488202b15cb9a..f7bd45370a0b9 100644
--- a/nifi-external/nifi-kafka-connect/nifi-kafka-connector-assembly/pom.xml
+++ b/nifi-external/nifi-kafka-connect/nifi-kafka-connector-assembly/pom.xml
@@ -45,17 +45,6 @@
2.0.0-SNAPSHOT
-
-
- jakarta.servlet
- jakarta.servlet-api
- compile
-
-
- org.eclipse.jetty.toolchain
- jetty-schemas
- compile
- org.slf4jslf4j-api
diff --git a/nifi-external/nifi-kafka-connect/nifi-kafka-connector/pom.xml b/nifi-external/nifi-kafka-connect/nifi-kafka-connector/pom.xml
index 77a70a5f8ba37..6632c22bfec8b 100644
--- a/nifi-external/nifi-kafka-connect/nifi-kafka-connector/pom.xml
+++ b/nifi-external/nifi-kafka-connect/nifi-kafka-connector/pom.xml
@@ -51,17 +51,6 @@
2.0.0-SNAPSHOT
-
-
- jakarta.servlet
- jakarta.servlet-api
- compile
-
-
- org.eclipse.jetty.toolchain
- jetty-schemas
- compile
- org.slf4jslf4j-api
diff --git a/nifi-framework-api/src/main/java/org/apache/nifi/bundle/BundleDetails.java b/nifi-framework-api/src/main/java/org/apache/nifi/bundle/BundleDetails.java
index c1f2117f77867..28be25a94d0ec 100644
--- a/nifi-framework-api/src/main/java/org/apache/nifi/bundle/BundleDetails.java
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/bundle/BundleDetails.java
@@ -17,8 +17,9 @@
package org.apache.nifi.bundle;
import java.io.File;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.Date;
/**
@@ -107,10 +108,9 @@ public String toString() {
public Date getBuildTimestampDate() {
if (buildTimestamp != null && !buildTimestamp.isEmpty()) {
try {
- SimpleDateFormat buildTimestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
- Date buildTimestampDate = buildTimestampFormat.parse(buildTimestamp);
- return buildTimestampDate;
- } catch (ParseException parseEx) {
+ final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ return Date.from(OffsetDateTime.parse(buildTimestamp, dateTimeFormatter).toInstant());
+ } catch (DateTimeParseException e) {
return null;
}
} else {
diff --git a/nifi-manifest/nifi-extension-manifest-model/pom.xml b/nifi-manifest/nifi-extension-manifest-model/pom.xml
index 706d9ab68692a..2087ca6860f16 100644
--- a/nifi-manifest/nifi-extension-manifest-model/pom.xml
+++ b/nifi-manifest/nifi-extension-manifest-model/pom.xml
@@ -25,7 +25,7 @@
- io.swagger
+ io.swagger.core.v3swagger-annotations
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/AllowableValue.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/AllowableValue.java
index 02efeb28b61bc..cb927b357280f 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/AllowableValue.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/AllowableValue.java
@@ -16,13 +16,13 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
+
@XmlAccessorType(XmlAccessType.FIELD)
public class AllowableValue {
@@ -30,7 +30,7 @@ public class AllowableValue {
private String displayName;
private String description;
- @ApiModelProperty(value = "The value of the allowable value")
+ @Schema(description = "The value of the allowable value")
public String getValue() {
return value;
}
@@ -39,7 +39,7 @@ public void setValue(String value) {
this.value = value;
}
- @ApiModelProperty(value = "The display name of the allowable value")
+ @Schema(description = "The display name of the allowable value")
public String getDisplayName() {
return displayName;
}
@@ -48,7 +48,7 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- @ApiModelProperty(value = "The description of the allowable value")
+ @Schema(description = "The description of the allowable value")
public String getDescription() {
return description;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Attribute.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Attribute.java
index 0f900ee94f96c..82ee5f06b0d00 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Attribute.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Attribute.java
@@ -16,20 +16,18 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Attribute {
private String name;
private String description;
- @ApiModelProperty(value = "The name of the attribute")
+ @Schema(description = "The name of the attribute")
public String getName() {
return name;
}
@@ -38,7 +36,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the attribute")
+ @Schema(description = "The description of the attribute")
public String getDescription() {
return description;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/BuildInfo.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/BuildInfo.java
index d884603bd6da8..a4f8774d73095 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/BuildInfo.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/BuildInfo.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class BuildInfo {
@@ -33,7 +31,7 @@ public class BuildInfo {
private String builtBy;
private String timestamp;
- @ApiModelProperty(value = "The tag the NAR was built from")
+ @Schema(description = "The tag the NAR was built from")
public String getTag() {
return tag;
}
@@ -42,7 +40,7 @@ public void setTag(String tag) {
this.tag = tag;
}
- @ApiModelProperty(value = "The branch the NAR was built from")
+ @Schema(description = "The branch the NAR was built from")
public String getBranch() {
return branch;
}
@@ -51,7 +49,7 @@ public void setBranch(String branch) {
this.branch = branch;
}
- @ApiModelProperty(value = "The revision the NAR was built from")
+ @Schema(description = "The revision the NAR was built from")
public String getRevision() {
return revision;
}
@@ -60,7 +58,7 @@ public void setRevision(String revision) {
this.revision = revision;
}
- @ApiModelProperty(value = "The JDK the NAR was built with")
+ @Schema(description = "The JDK the NAR was built with")
public String getJdk() {
return jdk;
}
@@ -69,7 +67,7 @@ public void setJdk(String jdk) {
this.jdk = jdk;
}
- @ApiModelProperty(value = "The OS user that performed the build")
+ @Schema(description = "The OS user that performed the build")
public String getBuiltBy() {
return builtBy;
}
@@ -78,7 +76,7 @@ public void setBuiltBy(String builtBy) {
this.builtBy = builtBy;
}
- @ApiModelProperty(value = "The timestamp of the build")
+ @Schema(description = "The timestamp of the build")
public String getTimestamp() {
return timestamp;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Cardinality.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Cardinality.java
index 0b0966e2f3f26..5b0b2d5a2c043 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Cardinality.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Cardinality.java
@@ -16,9 +16,6 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public enum Cardinality {
/**
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ControllerServiceDefinition.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ControllerServiceDefinition.java
index c6670d1c7676f..37c623f8e82b0 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ControllerServiceDefinition.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ControllerServiceDefinition.java
@@ -16,14 +16,12 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import java.util.Objects;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class ControllerServiceDefinition {
@@ -32,7 +30,7 @@ public class ControllerServiceDefinition {
private String artifactId;
private String version;
- @ApiModelProperty(value = "The class name of the service API")
+ @Schema(description = "The class name of the service API")
public String getClassName() {
return className;
}
@@ -41,7 +39,7 @@ public void setClassName(String className) {
this.className = className;
}
- @ApiModelProperty(value = "The group id of the service API")
+ @Schema(description = "The group id of the service API")
public String getGroupId() {
return groupId;
}
@@ -50,7 +48,7 @@ public void setGroupId(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty(value = "The artifact id of the service API")
+ @Schema(description = "The artifact id of the service API")
public String getArtifactId() {
return artifactId;
}
@@ -59,7 +57,7 @@ public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
- @ApiModelProperty(value = "The version of the service API")
+ @Schema(description = "The version of the service API")
public String getVersion() {
return version;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSchedule.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSchedule.java
index 8cd79aae5443b..6609ccc48b6e9 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSchedule.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSchedule.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class DefaultSchedule {
@@ -30,7 +28,7 @@ public class DefaultSchedule {
private String period;
private String concurrentTasks;
- @ApiModelProperty("The default scheduling strategy")
+ @Schema(description = "The default scheduling strategy")
public String getStrategy() {
return strategy;
}
@@ -39,7 +37,7 @@ public void setStrategy(String strategy) {
this.strategy = strategy;
}
- @ApiModelProperty("The default scheduling period")
+ @Schema(description = "The default scheduling period")
public String getPeriod() {
return period;
}
@@ -48,7 +46,7 @@ public void setPeriod(String period) {
this.period = period;
}
- @ApiModelProperty("The default concurrent tasks")
+ @Schema(description = "The default concurrent tasks")
public String getConcurrentTasks() {
return concurrentTasks;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSettings.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSettings.java
index a7b97998965ec..d4cf2ce0a0a55 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSettings.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DefaultSettings.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class DefaultSettings {
@@ -30,7 +28,7 @@ public class DefaultSettings {
private String penaltyDuration;
private String bulletinLevel;
- @ApiModelProperty("The default yield duration")
+ @Schema(description = "The default yield duration")
public String getYieldDuration() {
return yieldDuration;
}
@@ -39,7 +37,7 @@ public void setYieldDuration(String yieldDuration) {
this.yieldDuration = yieldDuration;
}
- @ApiModelProperty("The default penalty duration")
+ @Schema(description = "The default penalty duration")
public String getPenaltyDuration() {
return penaltyDuration;
}
@@ -48,7 +46,7 @@ public void setPenaltyDuration(String penaltyDuration) {
this.penaltyDuration = penaltyDuration;
}
- @ApiModelProperty("The default bulletin level")
+ @Schema(description = "The default bulletin level")
public String getBulletinLevel() {
return bulletinLevel;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Dependency.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Dependency.java
index fce50f5737270..04aee18590a2f 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Dependency.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Dependency.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Dependency {
@@ -30,7 +28,7 @@ public class Dependency {
private String propertyDisplayName;
private DependentValues dependentValues;
- @ApiModelProperty(value = "The name of the dependent property")
+ @Schema(description = "The name of the dependent property")
public String getPropertyName() {
return propertyName;
}
@@ -39,7 +37,7 @@ public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
- @ApiModelProperty(value = "The display name of the dependent property")
+ @Schema(description = "The display name of the dependent property")
public String getPropertyDisplayName() {
return propertyDisplayName;
}
@@ -48,7 +46,7 @@ public void setPropertyDisplayName(String propertyDisplayName) {
this.propertyDisplayName = propertyDisplayName;
}
- @ApiModelProperty(value = "The values of the dependent property that enable the depending property")
+ @Schema(description = "The values of the dependent property that enable the depending property")
public DependentValues getDependentValues() {
return dependentValues;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DependentValues.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DependentValues.java
index 0e88780ba6c91..bb13b126eb084 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DependentValues.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DependentValues.java
@@ -16,22 +16,20 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+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 java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class DependentValues {
@XmlElement(name = "dependentValue")
private List values;
- @ApiModelProperty(value = "The dependent values")
+ @Schema(description = "The dependent values")
public List getValues() {
return values;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DeprecationNotice.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DeprecationNotice.java
index dbffb6a06ce46..3b271cc01f1fc 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DeprecationNotice.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DeprecationNotice.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
@@ -25,7 +24,6 @@
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class DeprecationNotice {
@@ -35,7 +33,7 @@ public class DeprecationNotice {
@XmlElement(name = "alternative")
private List alternatives;
- @ApiModelProperty(value = "The reason for the deprecation")
+ @Schema(description = "The reason for the deprecation")
public String getReason() {
return reason;
}
@@ -44,7 +42,7 @@ public void setReason(String reason) {
this.reason = reason;
}
- @ApiModelProperty(value = "The alternatives to use")
+ @Schema(description = "The alternatives to use")
public List getAlternatives() {
return alternatives;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicProperty.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicProperty.java
index 9de143aa1f8d9..a049c95e40cbb 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicProperty.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicProperty.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class DynamicProperty {
@@ -32,7 +30,7 @@ public class DynamicProperty {
private ExpressionLanguageScope expressionLanguageScope;
private boolean expressionLanguageSupported;
- @ApiModelProperty(value = "The description of the dynamic property name")
+ @Schema(description = "The description of the dynamic property name")
public String getName() {
return name;
}
@@ -41,7 +39,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the dynamic property value")
+ @Schema(description = "The description of the dynamic property value")
public String getValue() {
return value;
}
@@ -50,7 +48,7 @@ public void setValue(String value) {
this.value = value;
}
- @ApiModelProperty(value = "The description of the dynamic property")
+ @Schema(description = "The description of the dynamic property")
public String getDescription() {
return description;
}
@@ -59,7 +57,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "Whether or not expression language is supported")
+ @Schema(description = "Whether or not expression language is supported")
public boolean isExpressionLanguageSupported() {
return expressionLanguageSupported;
}
@@ -68,7 +66,7 @@ public void setExpressionLanguageSupported(boolean expressionLanguageSupported)
this.expressionLanguageSupported = expressionLanguageSupported;
}
- @ApiModelProperty(value = "The scope of the expression language support")
+ @Schema(description = "The scope of the expression language support")
public ExpressionLanguageScope getExpressionLanguageScope() {
return expressionLanguageScope;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicRelationship.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicRelationship.java
index bbf75010006df..92185cdaf2fb0 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicRelationship.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/DynamicRelationship.java
@@ -16,20 +16,20 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
+
@XmlAccessorType(XmlAccessType.FIELD)
public class DynamicRelationship {
private String name;
private String description;
- @ApiModelProperty(value = "The description of the dynamic relationship name")
+ @Schema(description = "The description of the dynamic relationship name")
public String getName() {
return name;
}
@@ -38,7 +38,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the dynamic relationship")
+ @Schema(description = "The description of the dynamic relationship")
public String getDescription() {
return description;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExpressionLanguageScope.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExpressionLanguageScope.java
index c713724d7d46e..9d2e6daca6d4a 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExpressionLanguageScope.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExpressionLanguageScope.java
@@ -16,9 +16,6 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public enum ExpressionLanguageScope {
/**
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Extension.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Extension.java
index a0b2a380dd2ad..3459efd49185d 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Extension.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Extension.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.Valid;
import jakarta.xml.bind.annotation.XmlAccessType;
@@ -27,7 +26,6 @@
import java.util.List;
import java.util.Objects;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Extension {
@@ -109,7 +107,7 @@ public class Extension {
@XmlElement(name = "multiProcessorUseCase")
private List multiProcessorUseCases;
- @ApiModelProperty(value = "The name of the extension")
+ @Schema(description = "The name of the extension")
public String getName() {
return name;
}
@@ -118,7 +116,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The type of the extension")
+ @Schema(description = "The type of the extension")
public ExtensionType getType() {
return type;
}
@@ -127,7 +125,7 @@ public void setType(ExtensionType type) {
this.type = type;
}
- @ApiModelProperty(value = "The deprecation notice of the extension")
+ @Schema(description = "The deprecation notice of the extension")
public DeprecationNotice getDeprecationNotice() {
return deprecationNotice;
}
@@ -136,7 +134,7 @@ public void setDeprecationNotice(DeprecationNotice deprecationNotice) {
this.deprecationNotice = deprecationNotice;
}
- @ApiModelProperty(value = "The description of the extension")
+ @Schema(description = "The description of the extension")
public String getDescription() {
return description;
}
@@ -145,7 +143,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "The tags of the extension")
+ @Schema(description = "The tags of the extension")
public List getTags() {
return tags;
}
@@ -154,7 +152,7 @@ public void setTags(List tags) {
this.tags = tags;
}
- @ApiModelProperty(value = "The properties of the extension")
+ @Schema(description = "The properties of the extension")
public List getProperties() {
return properties;
}
@@ -171,7 +169,7 @@ public void setSupportsSensitiveDynamicProperties(boolean supportsSensitiveDynam
this.supportsSensitiveDynamicProperties = supportsSensitiveDynamicProperties;
}
- @ApiModelProperty(value = "The dynamic properties of the extension")
+ @Schema(description = "The dynamic properties of the extension")
public List getDynamicProperties() {
return dynamicProperties;
}
@@ -180,7 +178,7 @@ public void setDynamicProperties(List dynamicProperties) {
this.dynamicProperties = dynamicProperties;
}
- @ApiModelProperty(value = "The relationships of the extension")
+ @Schema(description = "The relationships of the extension")
public List getRelationships() {
return relationships;
}
@@ -189,7 +187,7 @@ public void setRelationships(List relationships) {
this.relationships = relationships;
}
- @ApiModelProperty(value = "The dynamic relationships of the extension")
+ @Schema(description = "The dynamic relationships of the extension")
public DynamicRelationship getDynamicRelationship() {
return dynamicRelationship;
}
@@ -198,7 +196,7 @@ public void setDynamicRelationship(DynamicRelationship dynamicRelationship) {
this.dynamicRelationship = dynamicRelationship;
}
- @ApiModelProperty(value = "The attributes read from flow files by the extension")
+ @Schema(description = "The attributes read from flow files by the extension")
public List getReadsAttributes() {
return readsAttributes;
}
@@ -207,7 +205,7 @@ public void setReadsAttributes(List readsAttributes) {
this.readsAttributes = readsAttributes;
}
- @ApiModelProperty(value = "The attributes written to flow files by the extension")
+ @Schema(description = "The attributes written to flow files by the extension")
public List getWritesAttributes() {
return writesAttributes;
}
@@ -216,7 +214,7 @@ public void setWritesAttributes(List writesAttributes) {
this.writesAttributes = writesAttributes;
}
- @ApiModelProperty(value = "The information about how the extension stores state")
+ @Schema(description = "The information about how the extension stores state")
public Stateful getStateful() {
return stateful;
}
@@ -225,7 +223,7 @@ public void setStateful(Stateful stateful) {
this.stateful = stateful;
}
- @ApiModelProperty(value = "The restrictions of the extension")
+ @Schema(description = "The restrictions of the extension")
public Restricted getRestricted() {
return restricted;
}
@@ -234,7 +232,7 @@ public void setRestricted(Restricted restricted) {
this.restricted = restricted;
}
- @ApiModelProperty(value = "The input requirement of the extension")
+ @Schema(description = "The input requirement of the extension")
public InputRequirement getInputRequirement() {
return inputRequirement;
}
@@ -243,7 +241,7 @@ public void setInputRequirement(InputRequirement inputRequirement) {
this.inputRequirement = inputRequirement;
}
- @ApiModelProperty(value = "The resource considerations of the extension")
+ @Schema(description = "The resource considerations of the extension")
public List getSystemResourceConsiderations() {
return systemResourceConsiderations;
}
@@ -252,7 +250,7 @@ public void setSystemResourceConsiderations(List sy
this.systemResourceConsiderations = systemResourceConsiderations;
}
- @ApiModelProperty(value = "The names of other extensions to see")
+ @Schema(description = "The names of other extensions to see")
public List getSeeAlso() {
return seeAlso;
}
@@ -261,7 +259,7 @@ public void setSeeAlso(List seeAlso) {
this.seeAlso = seeAlso;
}
- @ApiModelProperty(value = "The service APIs provided by this extension")
+ @Schema(description = "The service APIs provided by this extension")
public List getProvidedServiceAPIs() {
return providedServiceAPIs;
}
@@ -270,7 +268,7 @@ public void setProvidedServiceAPIs(List providedServiceAPIs)
this.providedServiceAPIs = providedServiceAPIs;
}
- @ApiModelProperty(value = "The default settings for a processor")
+ @Schema(description = "The default settings for a processor")
public DefaultSettings getDefaultSettings() {
return defaultSettings;
}
@@ -279,7 +277,7 @@ public void setDefaultSettings(DefaultSettings defaultSettings) {
this.defaultSettings = defaultSettings;
}
- @ApiModelProperty(value = "The default schedule for a processor reporting task")
+ @Schema(description = "The default schedule for a processor reporting task")
public DefaultSchedule getDefaultSchedule() {
return defaultSchedule;
}
@@ -288,7 +286,7 @@ public void setDefaultSchedule(DefaultSchedule defaultSchedule) {
this.defaultSchedule = defaultSchedule;
}
- @ApiModelProperty(value = "Indicates that a processor should be triggered serially")
+ @Schema(description = "Indicates that a processor should be triggered serially")
public boolean getTriggerSerially() {
return triggerSerially;
}
@@ -297,7 +295,7 @@ public void setTriggerSerially(boolean triggerSerially) {
this.triggerSerially = triggerSerially;
}
- @ApiModelProperty(value = "Indicates that a processor should be triggered when the incoming queues are empty")
+ @Schema(description = "Indicates that a processor should be triggered when the incoming queues are empty")
public boolean getTriggerWhenEmpty() {
return triggerWhenEmpty;
}
@@ -306,7 +304,7 @@ public void setTriggerWhenEmpty(boolean triggerWhenEmpty) {
this.triggerWhenEmpty = triggerWhenEmpty;
}
- @ApiModelProperty(value = "Indicates that a processor should be triggered when any destinations have space for flow files")
+ @Schema(description = "Indicates that a processor should be triggered when any destinations have space for flow files")
public boolean getTriggerWhenAnyDestinationAvailable() {
return triggerWhenAnyDestinationAvailable;
}
@@ -315,7 +313,7 @@ public void setTriggerWhenAnyDestinationAvailable(boolean triggerWhenAnyDestinat
this.triggerWhenAnyDestinationAvailable = triggerWhenAnyDestinationAvailable;
}
- @ApiModelProperty(value = "Indicates that a processor supports batching")
+ @Schema(description = "Indicates that a processor supports batching")
public boolean getSupportsBatching() {
return supportsBatching;
}
@@ -324,7 +322,7 @@ public void setSupportsBatching(boolean supportsBatching) {
this.supportsBatching = supportsBatching;
}
- @ApiModelProperty(value = "Indicates that a processor should be scheduled only on the primary node")
+ @Schema(description = "Indicates that a processor should be scheduled only on the primary node")
public boolean getPrimaryNodeOnly() {
return primaryNodeOnly;
}
@@ -333,7 +331,7 @@ public void setPrimaryNodeOnly(boolean primaryNodeOnly) {
this.primaryNodeOnly = primaryNodeOnly;
}
- @ApiModelProperty(value = "Indicates that a processor is side effect free")
+ @Schema(description = "Indicates that a processor is side effect free")
public boolean getSideEffectFree() {
return sideEffectFree;
}
@@ -342,7 +340,7 @@ public void setSideEffectFree(boolean sideEffectFree) {
this.sideEffectFree = sideEffectFree;
}
- @ApiModelProperty(value = "Zero or more documented use cases for how the extension may be used")
+ @Schema(description = "Zero or more documented use cases for how the extension may be used")
public List getUseCases() {
return useCases;
}
@@ -351,7 +349,7 @@ public void setUseCases(final List useCases) {
this.useCases = useCases;
}
- @ApiModelProperty(value = "Zero or more documented use cases for how the processor may be used in conjunction with other processors")
+ @Schema(description = "Zero or more documented use cases for how the processor may be used in conjunction with other processors")
public List getMultiProcessorUseCases() {
return multiProcessorUseCases;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExtensionManifest.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExtensionManifest.java
index 70dffed57518f..a4c924d25617b 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExtensionManifest.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ExtensionManifest.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
@@ -26,7 +25,6 @@
import jakarta.xml.bind.annotation.XmlRootElement;
import java.util.List;
-@ApiModel
@XmlRootElement(name = "extensionManifest")
@XmlAccessorType(XmlAccessType.FIELD)
public class ExtensionManifest {
@@ -54,7 +52,7 @@ public ExtensionManifest(String systemApiVersion, List extensions) {
this.extensions = extensions;
}
- @ApiModelProperty(value = "The group id of this NAR")
+ @Schema(description = "The group id of this NAR")
public String getGroupId() {
return groupId;
}
@@ -63,7 +61,7 @@ public void setGroupId(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty(value = "The artifact id of this NAR")
+ @Schema(description = "The artifact id of this NAR")
public String getArtifactId() {
return artifactId;
}
@@ -72,7 +70,7 @@ public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
- @ApiModelProperty(value = "The version of this NAR")
+ @Schema(description = "The version of this NAR")
public String getVersion() {
return version;
}
@@ -81,7 +79,7 @@ public void setVersion(String version) {
this.version = version;
}
- @ApiModelProperty(value = "The info for the parent NAR of this NAR")
+ @Schema(description = "The info for the parent NAR of this NAR")
public ParentNar getParentNar() {
return parentNar;
}
@@ -90,7 +88,7 @@ public void setParentNar(ParentNar parentNar) {
this.parentNar = parentNar;
}
- @ApiModelProperty(value = "The version of nifi-api this NAR was built against")
+ @Schema(description = "The version of nifi-api this NAR was built against")
public String getSystemApiVersion() {
return systemApiVersion;
}
@@ -99,7 +97,7 @@ public void setSystemApiVersion(String systemApiVersion) {
this.systemApiVersion = systemApiVersion;
}
- @ApiModelProperty(value = "The build info for the NAR")
+ @Schema(description = "The build info for the NAR")
public BuildInfo getBuildInfo() {
return buildInfo;
}
@@ -108,7 +106,7 @@ public void setBuildInfo(BuildInfo buildInfo) {
this.buildInfo = buildInfo;
}
- @ApiModelProperty(value = "The list of extensions contained in this NAR")
+ @Schema(description = "The list of extensions contained in this NAR")
public List getExtensions() {
return extensions;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/InputRequirement.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/InputRequirement.java
index 51cd7d3c254b5..15e038c2861c6 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/InputRequirement.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/InputRequirement.java
@@ -16,9 +16,6 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public enum InputRequirement {
/**
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/MultiProcessorUseCase.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/MultiProcessorUseCase.java
index 2fdd2b2c6a18a..dfdec5bb1f2a9 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/MultiProcessorUseCase.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/MultiProcessorUseCase.java
@@ -17,15 +17,12 @@
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
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;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class MultiProcessorUseCase {
private String description;
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ParentNar.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ParentNar.java
index a86c44e7f61b8..8ffcd7eb16744 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ParentNar.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ParentNar.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class ParentNar {
@@ -30,7 +28,7 @@ public class ParentNar {
private String artifactId;
private String version;
- @ApiModelProperty(value = "The group id of the parent NAR")
+ @Schema(description = "The group id of the parent NAR")
public String getGroupId() {
return groupId;
}
@@ -39,7 +37,7 @@ public void setGroupId(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty(value = "The artifact id of the parent NAR")
+ @Schema(description = "The artifact id of the parent NAR")
public String getArtifactId() {
return artifactId;
}
@@ -48,7 +46,7 @@ public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
- @ApiModelProperty(value = "The version of the parent NAR")
+ @Schema(description = "The version of the parent NAR")
public String getVersion() {
return version;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProcessorConfiguration.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProcessorConfiguration.java
index 0f9b118b3e4b7..5125a8e139a7a 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProcessorConfiguration.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProcessorConfiguration.java
@@ -17,9 +17,6 @@
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public class ProcessorConfiguration {
private String processorClassName;
private String configuration;
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 6f97a4f6296b3..acd5739c968ac 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
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
@@ -25,7 +24,6 @@
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Property {
@@ -54,7 +52,7 @@ public class Property {
private ResourceDefinition resourceDefinition;
- @ApiModelProperty(value = "The name of the property")
+ @Schema(description = "The name of the property")
public String getName() {
return name;
}
@@ -63,7 +61,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The display name")
+ @Schema(description = "The display name")
public String getDisplayName() {
return displayName;
}
@@ -72,7 +70,7 @@ public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- @ApiModelProperty(value = "The description")
+ @Schema(description = "The description")
public String getDescription() {
return description;
}
@@ -81,7 +79,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "The default value")
+ @Schema(description = "The default value")
public String getDefaultValue() {
return defaultValue;
}
@@ -90,7 +88,7 @@ public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
- @ApiModelProperty(value = "The controller service required by this property, or null if none is required")
+ @Schema(description = "The controller service required by this property, or null if none is required")
public ControllerServiceDefinition getControllerServiceDefinition() {
return controllerServiceDefinition;
}
@@ -99,7 +97,7 @@ public void setControllerServiceDefinition(ControllerServiceDefinition controlle
this.controllerServiceDefinition = controllerServiceDefinition;
}
- @ApiModelProperty(value = "The allowable values for this property")
+ @Schema(description = "The allowable values for this property")
public List getAllowableValues() {
return allowableValues;
}
@@ -108,7 +106,7 @@ public void setAllowableValues(List allowableValues) {
this.allowableValues = allowableValues;
}
- @ApiModelProperty(value = "Whether or not the property is required")
+ @Schema(description = "Whether or not the property is required")
public boolean isRequired() {
return required;
}
@@ -117,7 +115,7 @@ public void setRequired(boolean required) {
this.required = required;
}
- @ApiModelProperty(value = "Whether or not the property is sensitive")
+ @Schema(description = "Whether or not the property is sensitive")
public boolean isSensitive() {
return sensitive;
}
@@ -126,7 +124,7 @@ public void setSensitive(boolean sensitive) {
this.sensitive = sensitive;
}
- @ApiModelProperty(value = "Whether or not expression language is supported")
+ @Schema(description = "Whether or not expression language is supported")
public boolean isExpressionLanguageSupported() {
return expressionLanguageSupported;
}
@@ -135,7 +133,7 @@ public void setExpressionLanguageSupported(boolean expressionLanguageSupported)
this.expressionLanguageSupported = expressionLanguageSupported;
}
- @ApiModelProperty(value = "The scope of expression language support")
+ @Schema(description = "The scope of expression language support")
public ExpressionLanguageScope getExpressionLanguageScope() {
return expressionLanguageScope;
}
@@ -144,7 +142,7 @@ public void setExpressionLanguageScope(ExpressionLanguageScope expressionLanguag
this.expressionLanguageScope = expressionLanguageScope;
}
- @ApiModelProperty(value = "Whether or not the processor dynamically modifies the classpath")
+ @Schema(description = "Whether or not the processor dynamically modifies the classpath")
public boolean isDynamicallyModifiesClasspath() {
return dynamicallyModifiesClasspath;
}
@@ -153,7 +151,7 @@ public void setDynamicallyModifiesClasspath(boolean dynamicallyModifiesClasspath
this.dynamicallyModifiesClasspath = dynamicallyModifiesClasspath;
}
- @ApiModelProperty(value = "Whether or not the processor is dynamic")
+ @Schema(description = "Whether or not the processor is dynamic")
public boolean isDynamic() {
return dynamic;
}
@@ -162,7 +160,7 @@ public void setDynamic(boolean dynamic) {
this.dynamic = dynamic;
}
- @ApiModelProperty(value = "The properties that this property depends on")
+ @Schema(description = "The properties that this property depends on")
public List getDependencies() {
return dependencies;
}
@@ -171,7 +169,7 @@ public void setDependencies(List dependencies) {
this.dependencies = dependencies;
}
- @ApiModelProperty(value = "The optional resource definition")
+ @Schema(description = "The optional resource definition")
public ResourceDefinition getResourceDefinition() {
return resourceDefinition;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProvidedServiceAPI.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProvidedServiceAPI.java
index 06a98105fdf1a..a92216cecbc50 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProvidedServiceAPI.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ProvidedServiceAPI.java
@@ -16,15 +16,13 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.NotBlank;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import java.util.Objects;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class ProvidedServiceAPI {
@@ -37,7 +35,7 @@ public class ProvidedServiceAPI {
@NotBlank
private String version;
- @ApiModelProperty(value = "The class name of the service API being provided")
+ @Schema(description = "The class name of the service API being provided")
public String getClassName() {
return className;
}
@@ -46,7 +44,7 @@ public void setClassName(String className) {
this.className = className;
}
- @ApiModelProperty(value = "The group id of the service API being provided")
+ @Schema(description = "The group id of the service API being provided")
public String getGroupId() {
return groupId;
}
@@ -55,7 +53,7 @@ public void setGroupId(String groupId) {
this.groupId = groupId;
}
- @ApiModelProperty(value = "The artifact id of the service API being provided")
+ @Schema(description = "The artifact id of the service API being provided")
public String getArtifactId() {
return artifactId;
}
@@ -64,7 +62,7 @@ public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
- @ApiModelProperty(value = "The version of the service API being provided")
+ @Schema(description = "The version of the service API being provided")
public String getVersion() {
return version;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Relationship.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Relationship.java
index 576a6c8c76732..80f45ccc0f0c0 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Relationship.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Relationship.java
@@ -16,13 +16,11 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Relationship {
@@ -30,7 +28,7 @@ public class Relationship {
private String description;
private boolean autoTerminated;
- @ApiModelProperty(value = "The name of the relationship")
+ @Schema(description = "The name of the relationship")
public String getName() {
return name;
}
@@ -39,7 +37,7 @@ public void setName(String name) {
this.name = name;
}
- @ApiModelProperty(value = "The description of the relationship")
+ @Schema(description = "The description of the relationship")
public String getDescription() {
return description;
}
@@ -48,7 +46,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "Whether or not the relationship is auto-terminated by default")
+ @Schema(description = "Whether or not the relationship is auto-terminated by default")
public boolean isAutoTerminated() {
return autoTerminated;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceDefinition.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceDefinition.java
index 3755e0c934925..6525f7b844379 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceDefinition.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceDefinition.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
@@ -25,7 +24,6 @@
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class ResourceDefinition {
@@ -35,7 +33,7 @@ public class ResourceDefinition {
@XmlElement(name = "resourceType")
private List resourceTypes;
- @ApiModelProperty(value = "The cardinality of the resource definition")
+ @Schema(description = "The cardinality of the resource definition")
public Cardinality getCardinality() {
return cardinality;
}
@@ -44,7 +42,7 @@ public void setCardinality(Cardinality cardinality) {
this.cardinality = cardinality;
}
- @ApiModelProperty(value = "The types of resources")
+ @Schema(description = "The types of resources")
public List getResourceTypes() {
return resourceTypes;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceType.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceType.java
index 2f217f9ef9b72..f481839d01893 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceType.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/ResourceType.java
@@ -16,9 +16,6 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
-@ApiModel
public enum ResourceType {
/**
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restricted.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restricted.java
index 861b0ab501e6f..0162806f54214 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restricted.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restricted.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.Valid;
import jakarta.xml.bind.annotation.XmlAccessType;
@@ -26,7 +25,6 @@
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Restricted {
@@ -37,7 +35,7 @@ public class Restricted {
@XmlElement(name = "restriction")
private List restrictions;
- @ApiModelProperty(value = "The general restriction for the extension, or null if only specific restrictions exist")
+ @Schema(description = "The general restriction for the extension, or null if only specific restrictions exist")
public String getGeneralRestrictionExplanation() {
return generalRestrictionExplanation;
}
@@ -46,7 +44,7 @@ public void setGeneralRestrictionExplanation(String generalRestrictionExplanatio
this.generalRestrictionExplanation = generalRestrictionExplanation;
}
- @ApiModelProperty(value = "The specific restrictions")
+ @Schema(description = "The specific restrictions")
public List getRestrictions() {
return restrictions;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restriction.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restriction.java
index 92bd0e2bededc..241a4981505f8 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restriction.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Restriction.java
@@ -16,15 +16,13 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.NotBlank;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import java.util.Objects;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Restriction {
@@ -33,7 +31,7 @@ public class Restriction {
@NotBlank
private String explanation;
- @ApiModelProperty(value = "The permission required for this restriction")
+ @Schema(description = "The permission required for this restriction")
public String getRequiredPermission() {
return requiredPermission;
}
@@ -42,7 +40,7 @@ public void setRequiredPermission(String requiredPermission) {
this.requiredPermission = requiredPermission;
}
- @ApiModelProperty(value = "The explanation of this restriction")
+ @Schema(description = "The explanation of this restriction")
public String getExplanation() {
return explanation;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Scope.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Scope.java
index 7ee414ded46c7..e4db465a3ecfd 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Scope.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Scope.java
@@ -16,12 +16,9 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
/**
* Possible scopes for storing state.
*/
-@ApiModel
public enum Scope {
CLUSTER,
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Stateful.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Stateful.java
index 9b576b43ea4d7..ae718952a9bf9 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Stateful.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/Stateful.java
@@ -16,8 +16,7 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
@@ -25,7 +24,6 @@
import jakarta.xml.bind.annotation.XmlElementWrapper;
import java.util.List;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class Stateful {
@@ -35,7 +33,7 @@ public class Stateful {
@XmlElement(name = "scope")
private List scopes;
- @ApiModelProperty(value = "The description for how the extension stores state")
+ @Schema(description = "The description for how the extension stores state")
public String getDescription() {
return description;
}
@@ -44,7 +42,7 @@ public void setDescription(String description) {
this.description = description;
}
- @ApiModelProperty(value = "The scopes used to store state")
+ @Schema(description = "The scopes used to store state")
public List getScopes() {
return scopes;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/SystemResourceConsideration.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/SystemResourceConsideration.java
index b4528fc162613..b1fe60c0d2c92 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/SystemResourceConsideration.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/SystemResourceConsideration.java
@@ -16,20 +16,18 @@
*/
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class SystemResourceConsideration {
private String resource;
private String description;
- @ApiModelProperty(value = "The resource to consider")
+ @Schema(description = "The resource to consider")
public String getResource() {
return resource;
}
@@ -38,7 +36,7 @@ public void setResource(String resource) {
this.resource = resource;
}
- @ApiModelProperty(value = "The description of how the resource is affected")
+ @Schema(description = "The description of how the resource is affected")
public String getDescription() {
return description;
}
diff --git a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/UseCase.java b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/UseCase.java
index fc8fa093481ff..b62e03a712718 100644
--- a/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/UseCase.java
+++ b/nifi-manifest/nifi-extension-manifest-model/src/main/java/org/apache/nifi/extension/manifest/UseCase.java
@@ -17,15 +17,12 @@
package org.apache.nifi.extension.manifest;
-import io.swagger.annotations.ApiModel;
-
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;
-@ApiModel
@XmlAccessorType(XmlAccessType.FIELD)
public class UseCase {
diff --git a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
index bbbe1f9db1ccf..1d76db0a43291 100644
--- a/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
+++ b/nifi-manifest/nifi-runtime-manifest-core/src/main/java/org/apache/nifi/runtime/manifest/impl/RuntimeManifestGenerator.java
@@ -39,8 +39,8 @@
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -59,6 +59,7 @@ public class RuntimeManifestGenerator {
private static final String BUILD_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private static final String BUILD_JDK = "Build-Jdk";
private static final String BUILD_JDK_VENDOR = "Build-Jdk-Vendor";
+ private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(BUILD_TIMESTAMP_FORMAT);
private final File extensionManifestBaseDir;
private final File buildPropertiesFile;
@@ -87,9 +88,7 @@ public void execute() throws IOException {
long buildTimestampMillis;
try {
- final SimpleDateFormat buildTimestampFormat = new SimpleDateFormat(BUILD_TIMESTAMP_FORMAT);
- final Date buildTimestampDate = buildTimestampFormat.parse(buildTimestamp);
- buildTimestampMillis = buildTimestampDate.getTime();
+ buildTimestampMillis = OffsetDateTime.parse(buildTimestamp, TIMESTAMP_FORMATTER).toInstant().toEpochMilli();
} catch (Exception e) {
buildTimestampMillis = System.currentTimeMillis();
}
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 e027b13d286b9..2fa5446f56b1f 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
@@ -23,6 +23,8 @@
import org.apache.nifi.c2.protocol.component.api.ControllerServiceDefinition;
import org.apache.nifi.c2.protocol.component.api.DefinedType;
import org.apache.nifi.c2.protocol.component.api.ExtensionComponent;
+import org.apache.nifi.c2.protocol.component.api.MultiProcessorUseCase;
+import org.apache.nifi.c2.protocol.component.api.ProcessorConfiguration;
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;
@@ -33,6 +35,7 @@
import org.apache.nifi.c2.protocol.component.api.Restriction;
import org.apache.nifi.c2.protocol.component.api.RuntimeManifest;
import org.apache.nifi.c2.protocol.component.api.SchedulingDefaults;
+import org.apache.nifi.c2.protocol.component.api.UseCase;
import org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.components.state.Scope;
@@ -151,7 +154,7 @@ public RuntimeManifestBuilder addBundle(final ExtensionManifestContainer extensi
@Override
public RuntimeManifestBuilder addBundles(final Iterable extensionManifests) {
- extensionManifests.forEach(em -> addBundle(em));
+ extensionManifests.forEach(this::addBundle);
return this;
}
@@ -277,9 +280,45 @@ private void addProcessorDefinition(final ExtensionManifest extensionManifest, f
);
}
+ final List useCases = extension.getUseCases() == null ? List.of() : extension.getUseCases().stream()
+ .map(StandardRuntimeManifestBuilder::createUseCase)
+ .toList();
+ processorDefinition.setUseCases(useCases);
+
+ final List multiProcessorUseCases = extension.getMultiProcessorUseCases() == null ? List.of() : extension.getMultiProcessorUseCases().stream()
+ .map(StandardRuntimeManifestBuilder::createMultiProcessorUseCase)
+ .toList();
+ processorDefinition.setMultiProcessorUseCases(multiProcessorUseCases);
+
componentManifestBuilder.addProcessor(processorDefinition);
}
+ private static UseCase createUseCase(final org.apache.nifi.extension.manifest.UseCase extensionUseCase) {
+ final UseCase useCase = new UseCase();
+ useCase.setDescription(extensionUseCase.getDescription());
+ useCase.setConfiguration(extensionUseCase.getConfiguration());
+ useCase.setKeywords(extensionUseCase.getKeywords());
+ useCase.setNotes(extensionUseCase.getNotes());
+ return useCase;
+ }
+
+ private static MultiProcessorUseCase createMultiProcessorUseCase(final org.apache.nifi.extension.manifest.MultiProcessorUseCase extensionUseCase) {
+ final MultiProcessorUseCase useCase = new MultiProcessorUseCase();
+ useCase.setDescription(extensionUseCase.getDescription());
+ useCase.setKeywords(extensionUseCase.getKeywords());
+ useCase.setNotes(extensionUseCase.getNotes());
+
+ final List processorConfigs = new ArrayList<>();
+ for (final org.apache.nifi.extension.manifest.ProcessorConfiguration extensionConfig : extensionUseCase.getProcessorConfigurations()) {
+ final ProcessorConfiguration processorConfig = new ProcessorConfiguration();
+ processorConfig.setConfiguration(extensionConfig.getConfiguration());
+ processorConfig.setProcessorClassName(extensionConfig.getProcessorClassName());
+ processorConfigs.add(processorConfig);
+ }
+ useCase.setConfigurations(processorConfigs);
+ return useCase;
+ }
+
private org.apache.nifi.c2.protocol.component.api.Attribute getAttribute(final Attribute attribute) {
final org.apache.nifi.c2.protocol.component.api.Attribute c2Attribute = new org.apache.nifi.c2.protocol.component.api.Attribute();
c2Attribute.setName(attribute.getName());
@@ -299,16 +338,11 @@ private InputRequirement.Requirement getInputRequirement(final org.apache.nifi.e
return null;
}
- switch (inputRequirement) {
- case INPUT_ALLOWED:
- return InputRequirement.Requirement.INPUT_ALLOWED;
- case INPUT_REQUIRED:
- return InputRequirement.Requirement.INPUT_REQUIRED;
- case INPUT_FORBIDDEN:
- return InputRequirement.Requirement.INPUT_FORBIDDEN;
- default:
- throw new IllegalArgumentException("Unknown input requirement: " + inputRequirement.name());
- }
+ return switch (inputRequirement) {
+ case INPUT_ALLOWED -> InputRequirement.Requirement.INPUT_ALLOWED;
+ case INPUT_REQUIRED -> InputRequirement.Requirement.INPUT_REQUIRED;
+ case INPUT_FORBIDDEN -> InputRequirement.Requirement.INPUT_FORBIDDEN;
+ };
}
private List getSupportedRelationships(final List relationships) {
@@ -460,14 +494,10 @@ private org.apache.nifi.c2.protocol.component.api.SystemResourceConsideration ge
}
private Scope getScope(final org.apache.nifi.extension.manifest.Scope sourceScope) {
- switch (sourceScope) {
- case LOCAL:
- return Scope.LOCAL;
- case CLUSTER:
- return Scope.CLUSTER;
- default:
- throw new IllegalArgumentException("Unknown scope: " + sourceScope);
- }
+ return switch (sourceScope) {
+ case LOCAL -> Scope.LOCAL;
+ case CLUSTER -> Scope.CLUSTER;
+ };
}
private Restriction createRestriction(final org.apache.nifi.extension.manifest.Restriction extensionRestriction) {
@@ -550,8 +580,7 @@ private List getPropertyDependencies(final List
final DependentValues dependentValues = dependency.getDependentValues();
if (dependentValues != null && dependentValues.getValues() != null) {
- final List values = new ArrayList();
- values.addAll(dependentValues.getValues());
+ final List values = new ArrayList<>(dependentValues.getValues());
propertyDependency.setDependentValues(values);
}
propertyDependencies.add(propertyDependency);
@@ -565,18 +594,15 @@ private PropertyResourceDefinition getPropertyResourceDefinition(final ResourceD
}
final PropertyResourceDefinition propertyResourceDefinition = new PropertyResourceDefinition();
- switch (resourceDefinition.getCardinality()) {
- case SINGLE:
- propertyResourceDefinition.setCardinality(ResourceCardinality.SINGLE);
- break;
- case MULTIPLE:
- propertyResourceDefinition.setCardinality(ResourceCardinality.MULTIPLE);
- break;
- }
+ final ResourceCardinality cardinality = switch (resourceDefinition.getCardinality()) {
+ case SINGLE -> ResourceCardinality.SINGLE;
+ case MULTIPLE -> ResourceCardinality.MULTIPLE;
+ };
+ propertyResourceDefinition.setCardinality(cardinality);
propertyResourceDefinition.setResourceTypes(
resourceDefinition.getResourceTypes().stream()
- .map(rt -> getResourceType(rt))
+ .map(this::getResourceType)
.collect(Collectors.toSet())
);
@@ -584,18 +610,12 @@ private PropertyResourceDefinition getPropertyResourceDefinition(final ResourceD
}
private ResourceType getResourceType(final org.apache.nifi.extension.manifest.ResourceType resourceType) {
- switch (resourceType) {
- case URL:
- return ResourceType.URL;
- case FILE:
- return ResourceType.FILE;
- case TEXT:
- return ResourceType.TEXT;
- case DIRECTORY:
- return ResourceType.DIRECTORY;
- default:
- throw new IllegalArgumentException("Unknown resource type: " + resourceType);
- }
+ return switch (resourceType) {
+ case URL -> ResourceType.URL;
+ case FILE -> ResourceType.FILE;
+ case TEXT -> ResourceType.TEXT;
+ case DIRECTORY -> ResourceType.DIRECTORY;
+ };
}
private ExpressionLanguageScope getELScope(final org.apache.nifi.extension.manifest.ExpressionLanguageScope elScope) {
@@ -603,16 +623,11 @@ private ExpressionLanguageScope getELScope(final org.apache.nifi.extension.manif
return null;
}
- switch (elScope) {
- case NONE:
- return ExpressionLanguageScope.NONE;
- case FLOWFILE_ATTRIBUTES:
- return ExpressionLanguageScope.FLOWFILE_ATTRIBUTES;
- case ENVIRONMENT:
- return ExpressionLanguageScope.ENVIRONMENT;
- default:
- throw new IllegalArgumentException("Unknown Expression Language Scope: " + elScope.name());
- }
+ return switch (elScope) {
+ case NONE -> ExpressionLanguageScope.NONE;
+ case FLOWFILE_ATTRIBUTES -> ExpressionLanguageScope.FLOWFILE_ATTRIBUTES;
+ case ENVIRONMENT -> ExpressionLanguageScope.ENVIRONMENT;
+ };
}
private List getPropertyAllowableValues(final List allowableValues) {
diff --git a/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java b/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
index 46ce07a20e81c..eb34a91cea432 100644
--- a/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
+++ b/nifi-manifest/nifi-runtime-manifest-test/src/test/java/org/apache/nifi/runtime/manifest/TestRuntimeManifest.java
@@ -128,8 +128,8 @@ void testRuntimeManifest() throws IOException {
assertEquals(REPORTING_TASK_DEFAULT_SCHEDULE_TIME, prometheusDefaultSchedulingPeriods.get(SchedulingStrategy.TIMER_DRIVEN.name()));
assertEquals(SchedulingStrategy.CRON_DRIVEN.getDefaultSchedulingPeriod(), prometheusDefaultSchedulingPeriods.get(SchedulingStrategy.CRON_DRIVEN.name()));
- final ProcessorDefinition joltTransformDef = getProcessorDefinition(bundles, "nifi-jolt-record-nar",
- "org.apache.nifi.processors.jolt.record.JoltTransformRecord");
+ final ProcessorDefinition joltTransformDef = getProcessorDefinition(bundles, "nifi-jolt-nar",
+ "org.apache.nifi.processors.jolt.JoltTransformRecord");
assertEquals(SchedulingStrategy.TIMER_DRIVEN.name(), joltTransformDef.getDefaultSchedulingStrategy());
final List joltTransformSchedulingStrategies = joltTransformDef.getSupportedSchedulingStrategies();
diff --git a/nifi-maven-archetypes/nifi-processor-bundle-archetype/src/main/resources/archetype-resources/nifi-__artifactBaseName__-nar/pom.xml b/nifi-maven-archetypes/nifi-processor-bundle-archetype/src/main/resources/archetype-resources/nifi-__artifactBaseName__-nar/pom.xml
index c5c63998fe7e2..2737b68a88266 100644
--- a/nifi-maven-archetypes/nifi-processor-bundle-archetype/src/main/resources/archetype-resources/nifi-__artifactBaseName__-nar/pom.xml
+++ b/nifi-maven-archetypes/nifi-processor-bundle-archetype/src/main/resources/archetype-resources/nifi-__artifactBaseName__-nar/pom.xml
@@ -32,6 +32,12 @@
nifi-${artifactBaseName}-processors${version}
+
+ org.apache.nifi
+ nifi-standard-services-api-nar
+ ${version}
+ nar
+
diff --git a/nifi-mock/src/main/java/org/apache/nifi/state/MockStateManager.java b/nifi-mock/src/main/java/org/apache/nifi/state/MockStateManager.java
index 6086191d41822..5ef3116223728 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/state/MockStateManager.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/state/MockStateManager.java
@@ -29,10 +29,20 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import static org.apache.nifi.state.MockStateManager.ExecutionMode.CLUSTERED;
+import static org.apache.nifi.state.MockStateManager.ExecutionMode.STANDALONE;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MockStateManager implements StateManager {
+
+ public enum ExecutionMode {
+ // in CLUSTERED mode separate state maps are used for the CLUSTER and the LOCAL scopes
+ CLUSTERED,
+ // in STANDALONE mode the same state map (the local one) is used for both the CLUSTER and the LOCAL scopes
+ STANDALONE
+ }
+
private final AtomicInteger versionIndex = new AtomicInteger(0);
private StateMap localStateMap = new MockStateMap(null, -1L);
@@ -50,6 +60,8 @@ public class MockStateManager implements StateManager {
private final boolean usesLocalState;
private final boolean usesClusterState;
+ private ExecutionMode executionMode = CLUSTERED;
+
public MockStateManager(final Object component) {
final Stateful stateful = component.getClass().getAnnotation(Stateful.class);
if (stateful == null) {
@@ -78,13 +90,17 @@ public void reset() {
localStateMap = new MockStateMap(null, -1L);
}
+ public void setExecutionMode(ExecutionMode executionMode) {
+ this.executionMode = executionMode;
+ }
+
@Override
public synchronized void setState(final Map state, final Scope scope) throws IOException {
verifyAnnotation(scope);
verifyCanSet(scope);
final StateMap stateMap = new MockStateMap(state, versionIndex.incrementAndGet());
- if (scope == Scope.CLUSTER) {
+ if (scope == Scope.CLUSTER && executionMode == CLUSTERED) {
clusterStateMap = stateMap;
} else {
localStateMap = stateMap;
@@ -102,7 +118,7 @@ private synchronized StateMap retrieveState(final Scope scope) {
verifyAnnotation(scope);
if (scope == Scope.CLUSTER) {
clusterRetrievedCount.incrementAndGet();
- return clusterStateMap;
+ return executionMode == CLUSTERED ? clusterStateMap : localStateMap;
} else {
localRetrievedCount.incrementAndGet();
return localStateMap;
@@ -120,10 +136,14 @@ public long getRetrievalCount(final Scope scope) {
public synchronized boolean replace(final StateMap oldValue, final Map newValue, final Scope scope) throws IOException {
verifyAnnotation(scope);
if (scope == Scope.CLUSTER) {
- if (oldValue == clusterStateMap) {
+ if (executionMode == CLUSTERED && oldValue == clusterStateMap) {
verifyCanSet(scope);
clusterStateMap = new MockStateMap(newValue, versionIndex.incrementAndGet());
return true;
+ } else if (executionMode == STANDALONE && oldValue == localStateMap) {
+ verifyCanSet(scope);
+ localStateMap = new MockStateMap(newValue, versionIndex.incrementAndGet());
+ return true;
}
return false;
@@ -176,7 +196,7 @@ private void verifyAnnotation(final Scope scope) {
private String getValue(final String key, final Scope scope) {
final StateMap stateMap;
- if (scope == Scope.CLUSTER) {
+ if (scope == Scope.CLUSTER && executionMode == CLUSTERED) {
stateMap = clusterStateMap;
} else {
stateMap = localStateMap;
diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
index 226df960f538f..8244802858ea7 100644
--- a/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
+++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockPropertyValue.java
@@ -25,7 +25,6 @@
import org.apache.nifi.attribute.expression.language.Query;
import org.apache.nifi.attribute.expression.language.Query.Range;
import org.apache.nifi.attribute.expression.language.StandardPropertyValue;
-import org.apache.nifi.components.DescribedValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.resource.ResourceContext;
@@ -324,9 +323,9 @@ public ResourceReferences asResources() {
}
@Override
- public & DescribedValue> E asDescribedValue(Class enumType) throws IllegalArgumentException {
+ public > E asAllowableValue(Class enumType) throws IllegalArgumentException {
ensureExpressionsEvaluated();
- return stdPropValue.asDescribedValue(enumType);
+ return stdPropValue.asAllowableValue(enumType);
}
@Override
diff --git a/nifi-nar-bundles/nifi-asana-bundle/nifi-asana-processors/src/main/java/org/apache/nifi/processors/asana/GetAsanaObject.java b/nifi-nar-bundles/nifi-asana-bundle/nifi-asana-processors/src/main/java/org/apache/nifi/processors/asana/GetAsanaObject.java
index 2ab1877ee1a7a..597248e365ee4 100644
--- a/nifi-nar-bundles/nifi-asana-bundle/nifi-asana-processors/src/main/java/org/apache/nifi/processors/asana/GetAsanaObject.java
+++ b/nifi-nar-bundles/nifi-asana-bundle/nifi-asana-processors/src/main/java/org/apache/nifi/processors/asana/GetAsanaObject.java
@@ -341,7 +341,7 @@ private static void transferFlowFileByAsanaObjectState(ProcessSession session, A
}
protected AsanaObjectFetcher createObjectFetcher(final ProcessContext context, AsanaClient client) {
- final AsanaObjectType objectType = context.getProperty(PROP_ASANA_OBJECT_TYPE).asDescribedValue(AsanaObjectType.class);
+ final AsanaObjectType objectType = context.getProperty(PROP_ASANA_OBJECT_TYPE).asAllowableValue(AsanaObjectType.class);
final String projectName = context.getProperty(PROP_ASANA_PROJECT).getValue();
final String sectionName = context.getProperty(PROP_ASANA_SECTION).getValue();
final String teamName = context.getProperty(PROP_ASANA_TEAM_NAME).getValue();
diff --git a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/convert/converters/BerStringConverter.java b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/convert/converters/BerStringConverter.java
index 1b113768548f8..1e0d8530785a3 100644
--- a/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/convert/converters/BerStringConverter.java
+++ b/nifi-nar-bundles/nifi-asn1-bundle/nifi-asn1-services/src/main/java/org/apache/nifi/jasn1/convert/converters/BerStringConverter.java
@@ -16,6 +16,7 @@
*/
package org.apache.nifi.jasn1.convert.converters;
+import com.beanit.asn1bean.ber.types.BerObjectIdentifier;
import com.beanit.asn1bean.ber.types.BerType;
import com.beanit.asn1bean.ber.types.string.BerBMPString;
import com.beanit.asn1bean.ber.types.string.BerGeneralString;
@@ -46,7 +47,8 @@ public class BerStringConverter implements JASN1TypeAndValueConverter {
BerVisibleString.class,
BerVideotexString.class,
BerBMPString.class,
- BerUTF8String.class
+ BerUTF8String.class,
+ BerObjectIdentifier.class
);
@Override
diff --git a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/ListS3.java b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/ListS3.java
index 13e15a4aaecea..94a1c9d127aa1 100644
--- a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/ListS3.java
+++ b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/ListS3.java
@@ -143,12 +143,17 @@ public class ListS3 extends AbstractS3Processor implements VerifiableProcessor {
" However an additional DistributedMapCache controller service is required and more JVM heap memory is used." +
" For more information on how the 'Entity Tracking Time Window' property works, see the description.");
+ public static final AllowableValue NO_TRACKING = new AllowableValue("none", "No Tracking",
+ "This strategy lists all entities without any tracking. The same entities will be listed each time" +
+ " this processor is scheduled. It is recommended to change the default run schedule value." +
+ " Any property that relates to the persisting state will be ignored.");
+
public static final PropertyDescriptor LISTING_STRATEGY = new Builder()
.name("listing-strategy")
.displayName("Listing Strategy")
.description("Specify how to determine new/updated entities. See each strategy descriptions for detail.")
.required(true)
- .allowableValues(BY_TIMESTAMPS, BY_ENTITIES)
+ .allowableValues(BY_TIMESTAMPS, BY_ENTITIES, NO_TRACKING)
.defaultValue(BY_TIMESTAMPS.getValue())
.build();
@@ -161,11 +166,12 @@ public class ListS3 extends AbstractS3Processor implements VerifiableProcessor {
public static final PropertyDescriptor INITIAL_LISTING_TARGET = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(ListedEntityTracker.INITIAL_LISTING_TARGET)
.dependsOn(LISTING_STRATEGY, BY_ENTITIES)
+ .required(true)
.build();
public static final PropertyDescriptor TRACKING_TIME_WINDOW = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(ListedEntityTracker.TRACKING_TIME_WINDOW)
- .dependsOn(INITIAL_LISTING_TARGET, ListedEntityTracker.INITIAL_LISTING_TARGET_WINDOW)
+ .dependsOn(ListedEntityTracker.TRACKING_STATE_CACHE)
.required(true)
.build();
@@ -289,8 +295,8 @@ public class ListS3 extends AbstractS3Processor implements VerifiableProcessor {
AWS_CREDENTIALS_PROVIDER_SERVICE,
LISTING_STRATEGY,
TRACKING_STATE_CACHE,
- INITIAL_LISTING_TARGET,
TRACKING_TIME_WINDOW,
+ INITIAL_LISTING_TARGET,
RECORD_WRITER,
MIN_AGE,
MAX_AGE,
@@ -321,6 +327,8 @@ public class ListS3 extends AbstractS3Processor implements VerifiableProcessor {
private volatile boolean justElectedPrimaryNode = false;
private volatile boolean resetEntityTrackingState = false;
private volatile ListedEntityTracker> listedEntityTracker;
+ private volatile Long minObjectAgeMilliseconds;
+ private volatile Long maxObjectAgeMilliseconds;
@OnPrimaryNodeStateChange
public void onPrimaryNodeChange(final PrimaryNodeState newState) {
@@ -334,7 +342,7 @@ public void initListedEntityTracker(ProcessContext context) {
try {
listedEntityTracker.clearListedEntities();
} catch (IOException e) {
- throw new RuntimeException("Failed to reset previously listed entities due to " + e, e);
+ throw new RuntimeException("Failed to reset previously listed entities", e);
}
}
resetEntityTrackingState = false;
@@ -346,6 +354,9 @@ public void initListedEntityTracker(ProcessContext context) {
} else {
listedEntityTracker = null;
}
+
+ minObjectAgeMilliseconds = context.getProperty(MIN_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
+ maxObjectAgeMilliseconds = context.getProperty(MAX_AGE) != null ? context.getProperty(MAX_AGE).asTimePeriod(TimeUnit.MILLISECONDS) : null;
}
protected ListedEntityTracker> createListedEntityTracker() {
@@ -356,7 +367,7 @@ private static Validator createRequesterPaysValidator() {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
- boolean requesterPays = Boolean.valueOf(input);
+ boolean requesterPays = Boolean.parseBoolean(input);
boolean useVersions = context.getProperty(USE_VERSIONS).asBoolean();
boolean valid = !requesterPays || !useVersions;
return new ValidationResult.Builder()
@@ -407,7 +418,7 @@ private Set extractKeys(final StateMap stateMap) {
private void restoreState(final ProcessSession session) throws IOException {
final StateMap stateMap = session.getState(Scope.CLUSTER);
- if (!stateMap.getStateVersion().isPresent() || stateMap.get(CURRENT_TIMESTAMP) == null || stateMap.get(CURRENT_KEY_PREFIX+"0") == null) {
+ if (stateMap.getStateVersion().isEmpty() || stateMap.get(CURRENT_TIMESTAMP) == null || stateMap.get(CURRENT_KEY_PREFIX + "0") == null) {
forcefullyUpdateListing(0L, Collections.emptySet());
} else {
final long timestamp = Long.parseLong(stateMap.get(CURRENT_TIMESTAMP));
@@ -451,20 +462,14 @@ public void onTrigger(final ProcessContext context, final ProcessSession session
listByTrackingTimestamps(context, session);
} else if (BY_ENTITIES.equals(listingStrategy)) {
listByTrackingEntities(context, session);
+ } else if (NO_TRACKING.equals(listingStrategy)) {
+ listNoTracking(context, session);
} else {
throw new ProcessException("Unknown listing strategy: " + listingStrategy);
}
}
- private void listByTrackingTimestamps(ProcessContext context, ProcessSession session) {
- try {
- restoreState(session);
- } catch (IOException ioe) {
- getLogger().error("Failed to restore processor state; yielding", ioe);
- context.yield();
- return;
- }
-
+ private void listNoTracking(ProcessContext context, ProcessSession session) {
final AmazonS3 client = getClient(context);
S3BucketLister bucketLister = getS3BucketLister(context, client);
@@ -477,15 +482,10 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
final String bucket = context.getProperty(BUCKET_WITHOUT_DEFAULT_VALUE).evaluateAttributeExpressions().getValue();
final int batchSize = context.getProperty(BATCH_SIZE).asInteger();
- final ListingSnapshot currentListing = listing.get();
- final long currentTimestamp = currentListing.getTimestamp();
- final Set currentKeys = currentListing.getKeys();
int listCount = 0;
int totalListCount = 0;
- long latestListedTimestampInThisCycle = currentTimestamp;
- final Set listedKeys = new HashSet<>();
- getLogger().trace("Start listing, listingTimestamp={}, currentTimestamp={}, currentKeys={}", new Object[]{listingTimestamp, currentTimestamp, currentKeys});
+ getLogger().trace("Start listing, listingTimestamp={}", listingTimestamp);
final S3ObjectWriter writer;
final RecordSetWriterFactory writerFactory = context.getProperty(RECORD_WRITER).asControllerService(RecordSetWriterFactory.class);
@@ -502,14 +502,12 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
VersionListing versionListing = bucketLister.listVersions();
for (S3VersionSummary versionSummary : versionListing.getVersionSummaries()) {
long lastModified = versionSummary.getLastModified().getTime();
- if (lastModified < currentTimestamp
- || lastModified == currentTimestamp && currentKeys.contains(versionSummary.getKey())
- || (maxAgeMilliseconds != null && (lastModified < (listingTimestamp - maxAgeMilliseconds)))
+ if ((maxAgeMilliseconds != null && (lastModified < (listingTimestamp - maxAgeMilliseconds)))
|| lastModified > (listingTimestamp - minAgeMilliseconds)) {
continue;
}
- getLogger().trace("Listed key={}, lastModified={}, currentKeys={}", new Object[]{versionSummary.getKey(), lastModified, currentKeys});
+ getLogger().trace("Listed key={}, lastModified={}", versionSummary.getKey(), lastModified);
GetObjectTaggingResult taggingResult = getTaggingResult(context, client, versionSummary);
@@ -518,6 +516,92 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
// Write the entity to the listing
writer.addToListing(versionSummary, taggingResult, objectMetadata, context.getProperty(S3_REGION).getValue());
+ listCount++;
+ }
+ bucketLister.setNextMarker();
+
+ totalListCount += listCount;
+
+ if (listCount >= batchSize && writer.isCheckpoint()) {
+ getLogger().info("Successfully listed {} new files from S3; routing to success", listCount);
+ session.commitAsync();
+ }
+
+ listCount = 0;
+ } while (bucketLister.isTruncated());
+
+ writer.finishListing();
+ } catch (final Exception e) {
+ getLogger().error("Failed to list contents of bucket", e);
+ writer.finishListingExceptionally(e);
+ session.rollback();
+ context.yield();
+ return;
+ }
+
+ final long listMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
+ getLogger().info("Successfully listed S3 bucket {} in {} millis", bucket, listMillis);
+
+ if (totalListCount == 0) {
+ getLogger().debug("No new objects in S3 bucket {} to list. Yielding.", bucket);
+ context.yield();
+ }
+ }
+
+ private void listByTrackingTimestamps(ProcessContext context, ProcessSession session) {
+ try {
+ restoreState(session);
+ } catch (IOException ioe) {
+ getLogger().error("Failed to restore processor state; yielding", ioe);
+ context.yield();
+ return;
+ }
+
+ final AmazonS3 client = getClient(context);
+ final S3BucketLister bucketLister = getS3BucketLister(context, client);
+ final String bucket = context.getProperty(BUCKET_WITHOUT_DEFAULT_VALUE).evaluateAttributeExpressions().getValue();
+ final int batchSize = context.getProperty(BATCH_SIZE).asInteger();
+
+ final ListingSnapshot currentListing = listing.get();
+ final long startNanos = System.nanoTime();
+ final long currentTimestamp = System.currentTimeMillis();
+ final long listingTimestamp = currentListing.getTimestamp();
+ final Set currentKeys = currentListing.getKeys();
+ int listCount = 0;
+ int totalListCount = 0;
+ long latestListedTimestampInThisCycle = listingTimestamp;
+
+ final Set listedKeys = new HashSet<>();
+ getLogger().trace("Start listing, listingTimestamp={}, currentTimestamp={}, currentKeys={}", currentTimestamp, listingTimestamp, currentKeys);
+
+ final S3ObjectWriter writer;
+ final RecordSetWriterFactory writerFactory = context.getProperty(RECORD_WRITER).asControllerService(RecordSetWriterFactory.class);
+ if (writerFactory == null) {
+ writer = new AttributeObjectWriter(session);
+ } else {
+ writer = new RecordObjectWriter(session, writerFactory, getLogger(), context.getProperty(S3_REGION).getValue());
+ }
+
+ try {
+ writer.beginListing();
+
+ do {
+ final VersionListing versionListing = bucketLister.listVersions();
+ for (S3VersionSummary versionSummary : versionListing.getVersionSummaries()) {
+ final long lastModified = versionSummary.getLastModified().getTime();
+ if (lastModified < listingTimestamp
+ || lastModified == listingTimestamp && currentKeys.contains(versionSummary.getKey())
+ || !includeObjectInListing(versionSummary, currentTimestamp)) {
+ continue;
+ }
+
+ getLogger().trace("Listed key={}, lastModified={}, currentKeys={}", versionSummary.getKey(), lastModified, currentKeys);
+
+ // Write the entity to the listing
+ final GetObjectTaggingResult taggingResult = getTaggingResult(context, client, versionSummary);
+ final ObjectMetadata objectMetadata = getObjectMetadata(context, client, versionSummary);
+ writer.addToListing(versionSummary, taggingResult, objectMetadata, context.getProperty(S3_REGION).getValue());
+
// Track the latest lastModified timestamp and keys having that timestamp.
// NOTE: Amazon S3 lists objects in UTF-8 character encoding in lexicographical order. Not ordered by timestamps.
if (lastModified > latestListedTimestampInThisCycle) {
@@ -531,8 +615,8 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
listCount++;
}
- bucketLister.setNextMarker();
+ bucketLister.setNextMarker();
totalListCount += listCount;
if (listCount >= batchSize && writer.isCheckpoint()) {
@@ -545,7 +629,7 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
writer.finishListing();
} catch (final Exception e) {
- getLogger().error("Failed to list contents of bucket due to {}", e, e);
+ getLogger().error("Failed to list contents of bucket", e);
writer.finishListingExceptionally(e);
session.rollback();
context.yield();
@@ -553,7 +637,7 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
}
final Set updatedKeys = new HashSet<>();
- if (latestListedTimestampInThisCycle <= currentTimestamp) {
+ if (latestListedTimestampInThisCycle <= listingTimestamp) {
updatedKeys.addAll(currentKeys);
}
updatedKeys.addAll(listedKeys);
@@ -566,10 +650,10 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
});
final long listMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
- getLogger().info("Successfully listed S3 bucket {} in {} millis", new Object[]{bucket, listMillis});
+ getLogger().info("Successfully listed S3 bucket {} in {} millis", bucket, listMillis);
if (totalListCount == 0) {
- getLogger().debug("No new objects in S3 bucket {} to list. Yielding.", new Object[]{bucket});
+ getLogger().debug("No new objects in S3 bucket {} to list. Yielding.", bucket);
context.yield();
}
}
@@ -577,10 +661,12 @@ private void listByTrackingTimestamps(ProcessContext context, ProcessSession ses
private void listByTrackingEntities(ProcessContext context, ProcessSession session) {
listedEntityTracker.trackEntities(context, session, justElectedPrimaryNode, Scope.CLUSTER, minTimestampToList -> {
S3BucketLister bucketLister = getS3BucketLister(context, getClient(context));
+ final long currentTime = System.currentTimeMillis();
- List> listedEntities = bucketLister.listVersions().getVersionSummaries()
+ return bucketLister.listVersions().getVersionSummaries()
.stream()
- .filter(s3VersionSummary -> s3VersionSummary.getLastModified().getTime() >= minTimestampToList)
+ .filter(s3VersionSummary -> s3VersionSummary.getLastModified().getTime() >= minTimestampToList
+ && includeObjectInListing(s3VersionSummary, currentTime))
.map(s3VersionSummary -> new ListableEntityWrapper(
s3VersionSummary,
S3VersionSummary::getKey,
@@ -589,8 +675,6 @@ private void listByTrackingEntities(ProcessContext context, ProcessSession sessi
S3VersionSummary::getSize
))
.collect(Collectors.toList());
-
- return listedEntities;
}, null);
justElectedPrimaryNode = false;
@@ -606,7 +690,7 @@ protected void createRecordsForEntities(
ProcessContext context,
ProcessSession session,
List> updatedEntities
- ) throws IOException, SchemaNotFoundException {
+ ) {
publishListing(context, session, updatedEntities);
}
@@ -637,16 +721,15 @@ private void publishListing(ProcessContext context, ProcessSession session, List
for (ListableEntityWrapper updatedEntity : updatedEntities) {
S3VersionSummary s3VersionSummary = updatedEntity.getRawEntity();
- AmazonS3Client s3Client = getClient(context);
- GetObjectTaggingResult taggingResult = getTaggingResult(context, s3Client, s3VersionSummary);
- ObjectMetadata objectMetadata = getObjectMetadata(context, s3Client, s3VersionSummary);
-
+ final AmazonS3Client s3Client = getClient(context);
+ final GetObjectTaggingResult taggingResult = getTaggingResult(context, s3Client, s3VersionSummary);
+ final ObjectMetadata objectMetadata = getObjectMetadata(context, s3Client, s3VersionSummary);
writer.addToListing(s3VersionSummary, taggingResult, objectMetadata, context.getProperty(S3_REGION).getValue());
listCount++;
if (listCount >= batchSize && writer.isCheckpoint()) {
- getLogger().info("Successfully listed {} new files from S3; routing to success", new Object[]{listCount});
+ getLogger().info("Successfully listed {} new files from S3; routing to success", listCount);
session.commitAsync();
}
@@ -656,11 +739,10 @@ private void publishListing(ProcessContext context, ProcessSession session, List
writer.finishListing();
} catch (final Exception e) {
- getLogger().error("Failed to list contents of bucket due to {}", e, e);
+ getLogger().error("Failed to list contents of bucket", e);
writer.finishListingExceptionally(e);
session.rollback();
context.yield();
- return;
}
}
}
@@ -732,7 +814,7 @@ private interface S3BucketLister {
}
public class S3ObjectBucketLister implements S3BucketLister {
- private AmazonS3 client;
+ private final AmazonS3 client;
private ListObjectsRequest listObjectsRequest;
private ObjectListing objectListing;
@@ -762,22 +844,11 @@ public void setRequesterPays(boolean requesterPays) {
@Override
public VersionListing listVersions() {
- VersionListing versionListing = new VersionListing();
- this.objectListing = client.listObjects(listObjectsRequest);
- for(S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
- S3VersionSummary versionSummary = new S3VersionSummary();
- versionSummary.setBucketName(objectSummary.getBucketName());
- versionSummary.setETag(objectSummary.getETag());
- versionSummary.setKey(objectSummary.getKey());
- versionSummary.setLastModified(objectSummary.getLastModified());
- versionSummary.setOwner(objectSummary.getOwner());
- versionSummary.setSize(objectSummary.getSize());
- versionSummary.setStorageClass(objectSummary.getStorageClass());
- versionSummary.setIsLatest(true);
-
- versionListing.getVersionSummaries().add(versionSummary);
- }
-
+ objectListing = client.listObjects(listObjectsRequest);
+ final VersionListing versionListing = new VersionListing();
+ versionListing.setVersionSummaries(objectListing.getObjectSummaries().stream()
+ .map(ListS3.this::objectSummaryToVersionSummary)
+ .collect(Collectors.toList()));
return versionListing;
}
@@ -788,12 +859,12 @@ public void setNextMarker() {
@Override
public boolean isTruncated() {
- return (objectListing == null) ? false : objectListing.isTruncated();
+ return objectListing != null && objectListing.isTruncated();
}
}
public class S3ObjectBucketListerVersion2 implements S3BucketLister {
- private AmazonS3 client;
+ private final AmazonS3 client;
private ListObjectsV2Request listObjectsRequest;
private ListObjectsV2Result objectListing;
@@ -823,22 +894,11 @@ public void setRequesterPays(boolean requesterPays) {
@Override
public VersionListing listVersions() {
- VersionListing versionListing = new VersionListing();
- this.objectListing = client.listObjectsV2(listObjectsRequest);
- for(S3ObjectSummary objectSummary : objectListing.getObjectSummaries()) {
- S3VersionSummary versionSummary = new S3VersionSummary();
- versionSummary.setBucketName(objectSummary.getBucketName());
- versionSummary.setETag(objectSummary.getETag());
- versionSummary.setKey(objectSummary.getKey());
- versionSummary.setLastModified(objectSummary.getLastModified());
- versionSummary.setOwner(objectSummary.getOwner());
- versionSummary.setSize(objectSummary.getSize());
- versionSummary.setStorageClass(objectSummary.getStorageClass());
- versionSummary.setIsLatest(true);
-
- versionListing.getVersionSummaries().add(versionSummary);
- }
-
+ objectListing = client.listObjectsV2(listObjectsRequest);
+ final VersionListing versionListing = new VersionListing();
+ versionListing.setVersionSummaries(objectListing.getObjectSummaries().stream()
+ .map(ListS3.this::objectSummaryToVersionSummary)
+ .collect(Collectors.toList()));
return versionListing;
}
@@ -849,12 +909,12 @@ public void setNextMarker() {
@Override
public boolean isTruncated() {
- return (objectListing == null) ? false : objectListing.isTruncated();
+ return objectListing != null && objectListing.isTruncated();
}
}
public class S3VersionBucketLister implements S3BucketLister {
- private AmazonS3 client;
+ private final AmazonS3 client;
private ListVersionsRequest listVersionsRequest;
private VersionListing versionListing;
@@ -896,7 +956,7 @@ public void setNextMarker() {
@Override
public boolean isTruncated() {
- return (versionListing == null) ? false : versionListing.isTruncated();
+ return versionListing != null && versionListing.isTruncated();
}
}
@@ -944,13 +1004,12 @@ static class RecordObjectWriter implements S3ObjectWriter {
RECORD_SCHEMA = new SimpleRecordSchema(fields);
}
-
private final ProcessSession session;
private final RecordSetWriterFactory writerFactory;
private final ComponentLog logger;
+ private final String region;
private RecordSetWriter recordWriter;
private FlowFile flowFile;
- private String region;
public RecordObjectWriter(final ProcessSession session, final RecordSetWriterFactory writerFactory, final ComponentLog logger, final String region) {
this.session = session;
@@ -995,7 +1054,7 @@ public void finishListingExceptionally(final Exception cause) {
try {
recordWriter.close();
} catch (IOException e) {
- logger.error("Failed to write listing as Records due to {}", e, e);
+ logger.error("Failed to write listing as Records", e);
}
session.remove(flowFile);
@@ -1042,8 +1101,6 @@ private Record createRecordForListing(final S3VersionSummary versionSummary, fin
}
}
-
-
static class AttributeObjectWriter implements S3ObjectWriter {
private final ProcessSession session;
@@ -1062,14 +1119,17 @@ public void addToListing(final S3VersionSummary versionSummary, final GetObjectT
attributes.put(CoreAttributes.FILENAME.key(), versionSummary.getKey());
attributes.put("s3.bucket", versionSummary.getBucketName());
attributes.put("s3.region", region);
+
if (versionSummary.getOwner() != null) { // We may not have permission to read the owner
attributes.put("s3.owner", versionSummary.getOwner().getId());
}
+
attributes.put("s3.etag", versionSummary.getETag());
attributes.put("s3.lastModified", String.valueOf(versionSummary.getLastModified().getTime()));
attributes.put("s3.length", String.valueOf(versionSummary.getSize()));
attributes.put("s3.storeClass", versionSummary.getStorageClass());
attributes.put("s3.isLatest", String.valueOf(versionSummary.isLatest()));
+
if (versionSummary.getVersionId() != null) {
attributes.put("s3.version", versionSummary.getVersionId());
}
@@ -1094,7 +1154,7 @@ public void addToListing(final S3VersionSummary versionSummary, final GetObjectT
}
@Override
- public void finishListing() throws IOException {
+ public void finishListing() {
}
@Override
@@ -1131,8 +1191,6 @@ public List verify(final ProcessContext context, final
final List results = new ArrayList<>(super.verify(context, logger, attributes));
final String bucketName = context.getProperty(BUCKET_WITHOUT_DEFAULT_VALUE).evaluateAttributeExpressions(attributes).getValue();
- final long minAgeMilliseconds = context.getProperty(MIN_AGE).asTimePeriod(TimeUnit.MILLISECONDS);
- final Long maxAgeMilliseconds = context.getProperty(MAX_AGE) != null ? context.getProperty(MAX_AGE).asTimePeriod(TimeUnit.MILLISECONDS) : null;
if (bucketName == null || bucketName.trim().isEmpty()) {
results.add(new ConfigVerificationResult.Builder()
@@ -1144,36 +1202,28 @@ public List verify(final ProcessContext context, final
return results;
}
- final S3BucketLister bucketLister = getS3BucketLister(context, client);
- final long listingTimestamp = System.currentTimeMillis();
-
// Attempt to perform a listing of objects in the S3 bucket
try {
- int listCount = 0;
- int totalListCount = 0;
- VersionListing versionListing;
+ final S3BucketLister bucketLister = getS3BucketLister(context, client);
+ int totalItems = 0;
+ int totalMatchingItems = 0;
do {
- versionListing = bucketLister.listVersions();
+ final VersionListing versionListing = bucketLister.listVersions();
+ final long currentTime = System.currentTimeMillis();
for (final S3VersionSummary versionSummary : versionListing.getVersionSummaries()) {
- long lastModified = versionSummary.getLastModified().getTime();
- if ((maxAgeMilliseconds != null && (lastModified < (listingTimestamp - maxAgeMilliseconds)))
- || lastModified > (listingTimestamp - minAgeMilliseconds)) {
- continue;
+ totalItems++;
+ if (includeObjectInListing(versionSummary, currentTime)) {
+ totalMatchingItems++;
}
-
- listCount++;
}
bucketLister.setNextMarker();
-
- totalListCount += listCount;
-
- listCount = 0;
} while (bucketLister.isTruncated());
results.add(new ConfigVerificationResult.Builder()
.verificationStepName("Perform Listing")
.outcome(Outcome.SUCCESSFUL)
- .explanation("Successfully listed contents of bucket '" + bucketName + "', finding " + totalListCount + " objects matching the filter")
+ .explanation("Successfully listed contents of bucket '" + bucketName + "', finding " + totalItems + " total object(s). "
+ + totalMatchingItems + " objects matched the filter.")
.build());
logger.info("Successfully verified configuration");
@@ -1189,4 +1239,27 @@ public List verify(final ProcessContext context, final
return results;
}
+
+ /**
+ * Return whether to include the entity in the listing, based on the minimum and maximum object age (if configured).
+ */
+ private boolean includeObjectInListing(final S3VersionSummary versionSummary, final long currentTimeMillis) {
+ final long lastModifiedTime = versionSummary.getLastModified().getTime();
+
+ return (minObjectAgeMilliseconds == null || currentTimeMillis >= lastModifiedTime + minObjectAgeMilliseconds)
+ && (maxObjectAgeMilliseconds == null || currentTimeMillis <= lastModifiedTime + maxObjectAgeMilliseconds);
+ }
+
+ private S3VersionSummary objectSummaryToVersionSummary(final S3ObjectSummary objectSummary) {
+ final S3VersionSummary versionSummary = new S3VersionSummary();
+ versionSummary.setBucketName(objectSummary.getBucketName());
+ versionSummary.setETag(objectSummary.getETag());
+ versionSummary.setKey(objectSummary.getKey());
+ versionSummary.setLastModified(objectSummary.getLastModified());
+ versionSummary.setOwner(objectSummary.getOwner());
+ versionSummary.setSize(objectSummary.getSize());
+ versionSummary.setStorageClass(objectSummary.getStorageClass());
+ versionSummary.setIsLatest(true);
+ return versionSummary;
+ }
}
diff --git a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/PutS3Object.java b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/PutS3Object.java
index 37fd0b5037353..3247f9a6a8d88 100644
--- a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/PutS3Object.java
+++ b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/main/java/org/apache/nifi/processors/aws/s3/PutS3Object.java
@@ -71,8 +71,6 @@
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -877,7 +875,6 @@ public void process(final InputStream in) throws IOException {
private final Lock s3BucketLock = new ReentrantLock();
private final AtomicLong lastS3AgeOff = new AtomicLong(0L);
- private final DateFormat logFormat = new SimpleDateFormat();
protected void ageoffS3Uploads(final ProcessContext context, final AmazonS3 s3, final long now, String bucket) {
MultipartUploadListing oldUploads = getS3AgeoffListAndAgeoffLocalState(context, s3, now, bucket);
@@ -940,10 +937,10 @@ protected void abortS3MultipartUpload(final AmazonS3 s3, final String bucket, fi
try {
s3.abortMultipartUpload(abortRequest);
getLogger().info("Aborting out of date multipart upload, bucket {} key {} ID {}, initiated {}",
- new Object[]{bucket, uploadKey, uploadId, logFormat.format(upload.getInitiated())});
+ bucket, uploadKey, uploadId, upload.getInitiated());
} catch (AmazonClientException ace) {
getLogger().info("Error trying to abort multipart upload from bucket {} with key {} and ID {}: {}",
- new Object[]{bucket, uploadKey, uploadId, ace.getMessage()});
+ bucket, uploadKey, uploadId, ace.getMessage());
}
}
diff --git a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/s3/TestListS3.java b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/s3/TestListS3.java
index a64574cba2071..1a8259a268544 100644
--- a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/s3/TestListS3.java
+++ b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/src/test/java/org/apache/nifi/processors/aws/s3/TestListS3.java
@@ -49,8 +49,8 @@
import org.mockito.Mockito;
import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
@@ -132,7 +132,7 @@ public void testList() {
.verify(runner.getProcessContext(), runner.getLogger(), Collections.emptyMap());
assertEquals(ConfigVerificationResult.Outcome.SUCCESSFUL, results.get(0).getOutcome());
assertEquals(ConfigVerificationResult.Outcome.SUCCESSFUL, results.get(1).getOutcome());
- assertTrue(results.get(1).getExplanation().contains("finding 3 objects"));
+ assertTrue(results.get(1).getExplanation().contains("finding 3 total object(s)"));
}
@Test
@@ -175,8 +175,8 @@ public void testListWithRecords() throws InitializationException {
runner.assertAllFlowFilesTransferred(ListS3.REL_SUCCESS, 1);
- final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- final String lastModifiedString = dateFormat.format(lastModified);
+ final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ final String lastModifiedString = dateTimeFormatter.format(lastModified.toInstant().atZone(ZoneOffset.systemDefault()));
final MockFlowFile flowFile = runner.getFlowFilesForRelationship(ListS3.REL_SUCCESS).get(0);
flowFile.assertAttributeEquals("record.count", "3");
@@ -568,4 +568,63 @@ public void testWriteUserMetadata() {
Mockito.verify(mockS3Client, Mockito.never()).listVersions(Mockito.any());
}
+
+ @Test
+ public void testNoTrackingList() {
+ runner.setProperty(ListS3.REGION, "eu-west-1");
+ runner.setProperty(ListS3.BUCKET_WITHOUT_DEFAULT_VALUE, "test-bucket");
+ runner.setProperty(ListS3.LISTING_STRATEGY, ListS3.NO_TRACKING);
+
+ Date lastModified = new Date();
+ ObjectListing objectListing = new ObjectListing();
+ S3ObjectSummary objectSummary1 = new S3ObjectSummary();
+ objectSummary1.setBucketName("test-bucket");
+ objectSummary1.setKey("a");
+ objectSummary1.setLastModified(lastModified);
+ objectListing.getObjectSummaries().add(objectSummary1);
+ S3ObjectSummary objectSummary2 = new S3ObjectSummary();
+ objectSummary2.setBucketName("test-bucket");
+ objectSummary2.setKey("b/c");
+ objectSummary2.setLastModified(lastModified);
+ objectListing.getObjectSummaries().add(objectSummary2);
+ S3ObjectSummary objectSummary3 = new S3ObjectSummary();
+ objectSummary3.setBucketName("test-bucket");
+ objectSummary3.setKey("d/e");
+ objectSummary3.setLastModified(lastModified);
+ objectListing.getObjectSummaries().add(objectSummary3);
+ Mockito.when(mockS3Client.listObjects(Mockito.any(ListObjectsRequest.class))).thenReturn(objectListing);
+
+ runner.run();
+
+ ArgumentCaptor captureRequest = ArgumentCaptor.forClass(ListObjectsRequest.class);
+ Mockito.verify(mockS3Client, Mockito.times(1)).listObjects(captureRequest.capture());
+ ListObjectsRequest request = captureRequest.getValue();
+ assertEquals("test-bucket", request.getBucketName());
+ assertFalse(request.isRequesterPays());
+ Mockito.verify(mockS3Client, Mockito.never()).listVersions(Mockito.any());
+
+ runner.assertAllFlowFilesTransferred(ListS3.REL_SUCCESS, 3);
+ List flowFiles = runner.getFlowFilesForRelationship(ListS3.REL_SUCCESS);
+ MockFlowFile ff0 = flowFiles.get(0);
+ ff0.assertAttributeEquals("filename", "a");
+ ff0.assertAttributeEquals("s3.bucket", "test-bucket");
+ ff0.assertAttributeEquals("s3.region", "eu-west-1");
+ String lastModifiedTimestamp = String.valueOf(lastModified.getTime());
+ ff0.assertAttributeEquals("s3.lastModified", lastModifiedTimestamp);
+ flowFiles.get(1).assertAttributeEquals("filename", "b/c");
+ flowFiles.get(2).assertAttributeEquals("filename", "d/e");
+
+ final List results = ((VerifiableProcessor) runner.getProcessor())
+ .verify(runner.getProcessContext(), runner.getLogger(), Collections.emptyMap());
+ assertEquals(ConfigVerificationResult.Outcome.SUCCESSFUL, results.get(0).getOutcome());
+ assertEquals(ConfigVerificationResult.Outcome.SUCCESSFUL, results.get(1).getOutcome());
+ assertTrue(results.get(1).getExplanation().contains("finding 3"));
+
+ runner.clearTransferState();
+
+ runner.run();
+
+ runner.assertAllFlowFilesTransferred(ListS3.REL_SUCCESS, 3);
+ runner.getStateManager().assertStateEquals(ListS3.CURRENT_TIMESTAMP, null, Scope.CLUSTER);
+ }
}
diff --git a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-service-api/pom.xml b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-service-api/pom.xml
index 553fce66ec11e..a7422bebc708a 100644
--- a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-service-api/pom.xml
+++ b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-service-api/pom.xml
@@ -40,6 +40,10 @@
com.amazonawsaws-java-sdk-s3
+
+ com.amazonaws
+ aws-java-sdk-sts
+ org.apache.nifinifi-api
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
index 2902e09206b91..03c7f59f99725 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProvider.java
@@ -28,19 +28,6 @@
import com.microsoft.graph.requests.extensions.IUserCollectionWithReferencesPage;
import com.microsoft.graph.requests.extensions.IUserCollectionWithReferencesRequest;
import com.microsoft.graph.requests.extensions.IUserCollectionWithReferencesRequestBuilder;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
import org.apache.nifi.authorization.AuthorizerConfigurationContext;
import org.apache.nifi.authorization.Group;
import org.apache.nifi.authorization.User;
@@ -58,6 +45,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
/**
* The AzureGraphUserGroupProvider provides support for retrieving users and
* groups from Azure Active Directory (AAD) using graph rest-api & SDK.
@@ -275,7 +275,7 @@ private Set getGroupsWith(String groupFilterList, String prefix, String
Arrays.stream(groupFilterList.split(","))
.map(String::trim)
.filter(s-> !s.isEmpty())
- .collect(Collectors.toList())
+ .toList()
);
}
return Collections.unmodifiableSet(groupDisplayNames);
@@ -309,14 +309,10 @@ private Set queryGroupsWith(String prefix, String suffix, String substri
List currentPage = filterResults.getCurrentPage();
while (currentPage != null) {
for (com.microsoft.graph.models.extensions.Group grp : currentPage) {
- boolean filterEvaluation = true;
- if (!StringUtils.isEmpty(suffix) && !grp.displayName.endsWith(suffix)) {
- filterEvaluation = false;
- }
- if (!StringUtils.isEmpty(substring) && !grp.displayName.contains(substring)) {
- filterEvaluation = false;
- }
- if (filterEvaluation) {
+ boolean suffixMatches = StringUtils.isEmpty(suffix) || grp.displayName.endsWith(suffix);
+ boolean substringMatches = StringUtils.isEmpty(substring) || grp.displayName.contains(substring);
+
+ if (suffixMatches && substringMatches) {
groups.add(grp.displayName);
}
}
@@ -345,7 +341,7 @@ private UserGroupQueryResult getUsersFrom(String groupName, int pageSize) throws
final List currentPage = results.getCurrentPage();
if (currentPage != null && !currentPage.isEmpty()) {
- final com.microsoft.graph.models.extensions.Group graphGroup = results.getCurrentPage().get(0);
+ final com.microsoft.graph.models.extensions.Group graphGroup = results.getCurrentPage().getFirst();
final Group.Builder groupBuilder =
new Group.Builder()
.identifier(graphGroup.id)
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ImmutableAzureGraphUserGroup.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ImmutableAzureGraphUserGroup.java
index 0aab27455973c..743623e389798 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ImmutableAzureGraphUserGroup.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/main/java/org/apache/nifi/authorization/azure/ImmutableAzureGraphUserGroup.java
@@ -17,7 +17,9 @@
package org.apache.nifi.authorization.azure;
-import static java.util.stream.Collectors.toMap;
+import org.apache.nifi.authorization.Group;
+import org.apache.nifi.authorization.User;
+import org.apache.nifi.authorization.UserAndGroups;
import java.util.Collections;
import java.util.HashMap;
@@ -25,9 +27,7 @@
import java.util.Map;
import java.util.Set;
-import org.apache.nifi.authorization.Group;
-import org.apache.nifi.authorization.User;
-import org.apache.nifi.authorization.UserAndGroups;
+import static java.util.stream.Collectors.toMap;
public class ImmutableAzureGraphUserGroup {
private final Set users;
@@ -119,9 +119,7 @@ public static ImmutableAzureGraphUserGroup newInstance(final Set users, fi
final Map groupsByObjectId = new HashMap<>();
final Map groupsByDisplayName = new HashMap<>();
final Map> groupsByUserObjectId =
- users.stream().collect(toMap(User::getIdentifier, user -> {
- return new HashSet();
- }));
+ users.stream().collect(toMap(User::getIdentifier, user -> new HashSet<>()));
groups.forEach(group -> {
groupsByObjectId.put(group.getIdentifier(), group);
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/test/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProviderIT.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/test/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProviderIT.java
index 7049bc4cd549d..f866971417d06 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/test/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProviderIT.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-graph-authorizer/src/test/java/org/apache/nifi/authorization/azure/AzureGraphUserGroupProviderIT.java
@@ -31,16 +31,15 @@
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
-import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
-import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
public class AzureGraphUserGroupProviderIT {
private static final Logger logger = LoggerFactory.getLogger(AzureGraphUserGroupProviderIT.class);
@@ -92,7 +91,7 @@ protected static String getGroupListInclusion() {
private UserGroupProviderInitializationContext initContext;
@BeforeEach
- public void setup() throws IOException {
+ public void setup() {
authContext = Mockito.mock(AuthorizerConfigurationContext.class);
initContext = Mockito.mock(UserGroupProviderInitializationContext.class);
@@ -115,7 +114,6 @@ private void setupTestingProvider() {
testingProvider.onConfigured(authContext);
} catch (final Exception exc) {
logger.error("Error during setup; tests cannot run on this system.");
- return;
}
}
@@ -132,11 +130,11 @@ public void testWithGroupListFilter(){
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- assertTrue(testingProvider.getUsers().size() > 0);
+ assertFalse(testingProvider.getGroups().isEmpty());
+ assertFalse(testingProvider.getUsers().isEmpty());
UserAndGroups uag = testingProvider.getUserAndGroups(getKnownTestUserName());
assertNotNull(uag.getUser());
- assertTrue(uag.getGroups().size() > 0);
+ assertFalse(uag.getGroups().isEmpty());
}
@@ -149,15 +147,15 @@ public void testWithPaging(){
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- assertTrue(testingProvider.getUsers().size() > 0);
+ assertFalse(testingProvider.getGroups().isEmpty());
+ assertFalse(testingProvider.getUsers().isEmpty());
UserAndGroups uag = testingProvider.getUserAndGroups(getKnownTestUserName());
assertNotNull(uag.getUser());
- assertTrue(uag.getGroups().size() > 0);
+ assertFalse(uag.getGroups().isEmpty());
String knownGroupName = getKnownTestGroupName();
- List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).collect(Collectors.toList());
- assertTrue(search.size() > 0);
+ List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).toList();
+ assertFalse(search.isEmpty());
}
@Test
@@ -169,9 +167,9 @@ public void testWithGroupFilterPrefix(){
.thenReturn(new MockPropertyValue(prefix));
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).collect(Collectors.toList());
- assertTrue(search.size() > 0);
+ assertFalse(testingProvider.getGroups().isEmpty());
+ List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).toList();
+ assertFalse(search.isEmpty());
}
@Test
@@ -183,9 +181,9 @@ public void testWithGroupFilterSuffix(){
.thenReturn(new MockPropertyValue(suffix));
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).collect(Collectors.toList());
- assertTrue(search.size() > 0);
+ assertFalse(testingProvider.getGroups().isEmpty());
+ List search = testingProvider.getGroups().stream().filter(g-> g.getName().equals(knownGroupName)).toList();
+ assertFalse(search.isEmpty());
}
@Test
@@ -197,9 +195,9 @@ public void testWithGroupFilterSubstring(){
.thenReturn(new MockPropertyValue(substring));
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- List search = testingProvider.getGroups().stream().filter( g-> g.getName().equals(knownGroupName)).collect(Collectors.toList());
- assertTrue(search.size() > 0);
+ assertFalse(testingProvider.getGroups().isEmpty());
+ List search = testingProvider.getGroups().stream().filter( g-> g.getName().equals(knownGroupName)).toList();
+ assertFalse(search.isEmpty());
}
@Test
@@ -213,8 +211,8 @@ public void testWithGroupFilterOperatorAndListInclusion(){
.thenReturn(new MockPropertyValue(getGroupListInclusion()));
setupTestingProvider();
- assertTrue(testingProvider.getGroups().size() > 0);
- Set search = testingProvider.getGroups().stream().collect(Collectors.toSet());
+ assertFalse(testingProvider.getGroups().isEmpty());
+ Set search = new HashSet<>(testingProvider.getGroups());
// check there is no duplicate group
assertEquals(search.size(), testingProvider.getGroups().size());
}
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/main/java/org/apache/nifi/parameter/azure/AzureKeyVaultSecretsParameterProvider.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/main/java/org/apache/nifi/parameter/azure/AzureKeyVaultSecretsParameterProvider.java
index 568dc49384c82..45bc98c4d2c2b 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/main/java/org/apache/nifi/parameter/azure/AzureKeyVaultSecretsParameterProvider.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/main/java/org/apache/nifi/parameter/azure/AzureKeyVaultSecretsParameterProvider.java
@@ -34,9 +34,7 @@
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.services.azure.AzureCredentialsService;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -78,15 +76,11 @@ public class AzureKeyVaultSecretsParameterProvider extends AbstractParameterProv
static final String GROUP_NAME_TAG = "group-name";
- private static final List PROPERTIES;
-
- static {
- final List props = new ArrayList<>();
- props.add(AZURE_CREDENTIALS_SERVICE);
- props.add(KEY_VAULT_URI);
- props.add(GROUP_NAME_PATTERN);
- PROPERTIES = Collections.unmodifiableList(props);
- }
+ private static final List PROPERTIES = List.of(
+ AZURE_CREDENTIALS_SERVICE,
+ KEY_VAULT_URI,
+ GROUP_NAME_PATTERN
+ );
@Override
protected List getSupportedPropertyDescriptors() {
@@ -94,11 +88,10 @@ protected List getSupportedPropertyDescriptors() {
}
@Override
- public List fetchParameters(final ConfigurationContext context) throws IOException {
+ public List fetchParameters(final ConfigurationContext context) {
final SecretClient secretClient = configureSecretClient(context);
final List secrets = getAllSecrets(secretClient);
- final List groups = getParameterGroupsFromSecrets(context, secrets);
- return groups;
+ return getParameterGroupsFromSecrets(context, secrets);
}
@Override
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/test/java/org/apache/nifi/parameter/azure/TestAzureKeyVaultSecretsParameterProvider.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/test/java/org/apache/nifi/parameter/azure/TestAzureKeyVaultSecretsParameterProvider.java
index 6e821d9b3e762..9d332037516ae 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/test/java/org/apache/nifi/parameter/azure/TestAzureKeyVaultSecretsParameterProvider.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-parameter-providers/src/test/java/org/apache/nifi/parameter/azure/TestAzureKeyVaultSecretsParameterProvider.java
@@ -18,6 +18,7 @@
import com.azure.core.http.rest.PagedIterable;
import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.security.keyvault.secrets.implementation.SecretPropertiesHelper;
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
import com.azure.security.keyvault.secrets.models.SecretProperties;
import org.apache.nifi.components.ConfigVerificationResult;
@@ -37,13 +38,12 @@
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -61,7 +61,7 @@ public class TestAzureKeyVaultSecretsParameterProvider {
@Spy
private AzureKeyVaultSecretsParameterProvider parameterProvider;
- private final List mySecretParameters = Arrays.asList(
+ private final List mySecretParameters = List.of(
parameter("paramA", "valueA"),
parameter("paramB", "valueB"),
parameter("otherC", "valueOther"),
@@ -69,11 +69,11 @@ public class TestAzureKeyVaultSecretsParameterProvider {
parameter("nonSensitiveE", "valueE"),
parameter("otherF", "valueF")
);
- private final List otherSecretParameters = Arrays.asList(
+ private final List otherSecretParameters = List.of(
parameter("paramG", "valueG"),
parameter("otherH", "valueOther")
);
- private final List mockParameterGroups = Arrays.asList(
+ private final List mockParameterGroups = List.of(
new ParameterGroup("MySecret", mySecretParameters),
new ParameterGroup("OtherSecret", otherSecretParameters)
);
@@ -84,79 +84,61 @@ public void setup() {
}
@Test
- public void testFetchParametersWithNoSecrets() throws IOException, InitializationException {
- final List parameterGroups = Collections.singletonList(new ParameterGroup("MySecret", Collections.emptyList()));
+ public void testFetchParametersWithNoSecrets() throws InitializationException {
+ final List parameterGroups = List.of(new ParameterGroup("MySecret", Collections.emptyList()));
mockSecretClient(parameterGroups);
runProviderTest(0, ConfigVerificationResult.Outcome.SUCCESSFUL);
}
@Test
- public void testFetchParameters() throws IOException, InitializationException {
+ public void testFetchParameters() throws InitializationException {
mockSecretClient(mockParameterGroups);
- runProviderTest( 8, ConfigVerificationResult.Outcome.SUCCESSFUL);
+ runProviderTest(8, ConfigVerificationResult.Outcome.SUCCESSFUL);
}
@Test
- public void testFetchDisabledParameters() throws IOException, InitializationException {
- final List secretPropertiesList = new ArrayList<>();
- for (final ParameterGroup group : mockParameterGroups) {
- for (final Parameter parameter : group.getParameters()) {
- final SecretProperties secretProperties = mock(SecretProperties.class);
-
- when(secretProperties.isEnabled()).thenReturn(false);
-
- secretPropertiesList.add(secretProperties);
- }
+ public void testFetchDisabledParameters() throws InitializationException {
+ final List secretPropertiesList = Stream
+ .generate(() -> new SecretProperties().setEnabled(false))
+ .limit(mockParameterGroups.stream().mapToInt(group -> group.getParameters().size()).sum())
+ .toList();
- }
-
- final PagedIterable mockIterable = mock(PagedIterable.class);
- when(secretClient.listPropertiesOfSecrets()).thenReturn(mockIterable);
- when(mockIterable.iterator()).thenReturn(secretPropertiesList.iterator());
- runProviderTest( 0, ConfigVerificationResult.Outcome.SUCCESSFUL);
+ mockListPropertiesOfSecrets(secretPropertiesList);
+ runProviderTest(0, ConfigVerificationResult.Outcome.SUCCESSFUL);
}
@Test
- public void testFetchParametersWithNullTagsShouldNotThrowError() throws IOException, InitializationException {
+ public void testFetchParametersWithNullTagsShouldNotThrowError() throws InitializationException {
final List secretPropertiesList = new ArrayList<>();
for (final ParameterGroup group : mockParameterGroups) {
for (final Parameter parameter : group.getParameters()) {
final String parameterName = parameter.getDescriptor().getName();
final String parameterValue = parameter.getValue();
- final KeyVaultSecret secret = mock(KeyVaultSecret.class);
- when(secret.getName()).thenReturn(parameterName);
- when(secret.getValue()).thenReturn(parameterValue);
- final SecretProperties secretProperties = mock(SecretProperties.class);
- when(secret.getProperties()).thenReturn(secretProperties);
+ final SecretProperties secretProperties = new SecretProperties();
+ SecretPropertiesHelper.setName(secretProperties, parameterName);
+ secretProperties.setEnabled(true);
- final Map tags = null;
- when(secretProperties.getTags()).thenReturn(tags);
+ final KeyVaultSecret secret = new KeyVaultSecret(parameterName, parameterValue);
+ secret.setProperties(secretProperties);
- when(secretProperties.getName()).thenReturn(parameterName);
- when(secretProperties.getVersion()).thenReturn(null);
- when(secretProperties.isEnabled()).thenReturn(true);
when(secretClient.getSecret(eq(parameterName), any())).thenReturn(secret);
-
secretPropertiesList.add(secretProperties);
}
-
}
- final PagedIterable mockIterable = mock(PagedIterable.class);
- when(secretClient.listPropertiesOfSecrets()).thenReturn(mockIterable);
- when(mockIterable.iterator()).thenReturn(secretPropertiesList.iterator());
- runProviderTest( 0, ConfigVerificationResult.Outcome.SUCCESSFUL);
+ mockListPropertiesOfSecrets(secretPropertiesList);
+ runProviderTest(0, ConfigVerificationResult.Outcome.SUCCESSFUL);
}
@Test
- public void testFetchParametersListFailure() throws IOException, InitializationException {
+ public void testFetchParametersListFailure() throws InitializationException {
when(secretClient.listPropertiesOfSecrets()).thenThrow(new RuntimeException("Fake RuntimeException"));
runProviderTest(0, ConfigVerificationResult.Outcome.FAILED);
}
@Test
- public void testFetchParametersWithGroupNameRegex() throws IOException, InitializationException {
+ public void testFetchParametersWithGroupNameRegex() throws InitializationException {
mockSecretClient(mockParameterGroups);
final Map properties = new HashMap<>();
properties.put(AzureKeyVaultSecretsParameterProvider.GROUP_NAME_PATTERN, "MySecret");
@@ -169,44 +151,43 @@ private void mockSecretClient(final List mockGroups) {
for (final Parameter parameter : group.getParameters()) {
final String parameterName = parameter.getDescriptor().getName();
final String parameterValue = parameter.getValue();
- final KeyVaultSecret secret = mock(KeyVaultSecret.class);
- when(secret.getName()).thenReturn(parameterName);
- when(secret.getValue()).thenReturn(parameterValue);
- final SecretProperties secretProperties = mock(SecretProperties.class);
- when(secret.getProperties()).thenReturn(secretProperties);
+ final SecretProperties secretProperties = new SecretProperties();
+ SecretPropertiesHelper.setName(secretProperties, parameterName);
+ secretProperties.setTags(
+ Map.of(AzureKeyVaultSecretsParameterProvider.GROUP_NAME_TAG, group.getGroupName())
+ );
+ secretProperties.setEnabled(true);
- final Map tags = new HashMap<>();
- tags.put(AzureKeyVaultSecretsParameterProvider.GROUP_NAME_TAG, group.getGroupName());
- when(secretProperties.getTags()).thenReturn(tags);
+ final KeyVaultSecret secret = new KeyVaultSecret(parameterName, parameterValue);
+ secret.setProperties(secretProperties);
- when(secretProperties.getName()).thenReturn(parameterName);
- when(secretProperties.getVersion()).thenReturn(null);
- when(secretProperties.isEnabled()).thenReturn(true);
when(secretClient.getSecret(eq(parameterName), any())).thenReturn(secret);
-
secretPropertiesList.add(secretProperties);
}
-
}
+ mockListPropertiesOfSecrets(secretPropertiesList);
+ }
+
+ private void mockListPropertiesOfSecrets(final List secretPropertiesList) {
final PagedIterable mockIterable = mock(PagedIterable.class);
- when(secretClient.listPropertiesOfSecrets()).thenReturn(mockIterable);
when(mockIterable.iterator()).thenReturn(secretPropertiesList.iterator());
+ when(secretClient.listPropertiesOfSecrets()).thenReturn(mockIterable);
}
- private List runProviderTest(final int expectedCount,
- final ConfigVerificationResult.Outcome expectedOutcome)
- throws IOException, InitializationException {
- final Map properties = new HashMap<>();
- properties.put(AzureKeyVaultSecretsParameterProvider.GROUP_NAME_PATTERN, ".*");
- return runProviderTestWithProperties(expectedCount, expectedOutcome, properties);
+ private void runProviderTest(final int expectedCount, final ConfigVerificationResult.Outcome expectedOutcome)
+ throws InitializationException {
+ runProviderTestWithProperties(
+ expectedCount, expectedOutcome,
+ Map.of(AzureKeyVaultSecretsParameterProvider.GROUP_NAME_PATTERN, ".*")
+ );
}
- private List runProviderTestWithProperties(final int expectedCount,
- final ConfigVerificationResult.Outcome expectedOutcome,
- final Map properties)
- throws InitializationException, IOException {
+ private void runProviderTestWithProperties(final int expectedCount,
+ final ConfigVerificationResult.Outcome expectedOutcome,
+ final Map properties)
+ throws InitializationException {
final MockParameterProviderInitializationContext initContext = new MockParameterProviderInitializationContext("id", "name",
new MockComponentLog("providerId", parameterProvider));
parameterProvider.initialize(initContext);
@@ -219,9 +200,9 @@ private List runProviderTestWithProperties(final int expectedCou
assertThrows(RuntimeException.class, () -> parameterProvider.fetchParameters(mockConfigurationContext));
} else {
parameterGroups = parameterProvider.fetchParameters(mockConfigurationContext);
- final int parameterCount = (int) parameterGroups.stream()
- .flatMap(group -> group.getParameters().stream())
- .count();
+ final int parameterCount = parameterGroups.stream()
+ .mapToInt(group -> group.getParameters().size())
+ .sum();
assertEquals(expectedCount, parameterCount);
}
@@ -229,9 +210,7 @@ private List runProviderTestWithProperties(final int expectedCou
final List results = ((VerifiableParameterProvider) parameterProvider).verify(mockConfigurationContext, initContext.getLogger());
assertEquals(1, results.size());
- assertEquals(expectedOutcome, results.get(0).getOutcome());
-
- return parameterGroups;
+ assertEquals(expectedOutcome, results.getFirst().getOutcome());
}
private static Parameter parameter(final String name, final String value) {
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
index 5885515aa1026..202e427b5521a 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
@@ -111,7 +111,12 @@
com.microsoft.azure.kustokusto-data
- 4.0.4
+ 5.0.3
+
+
+ com.microsoft.azure.kusto
+ kusto-ingest
+ 5.0.3org.slf4j
@@ -194,6 +199,12 @@
2.0.0-SNAPSHOTtest
+
+ io.projectreactor
+ reactor-test
+ 3.5.11
+ test
+
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureBlobProcessor_v12.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureBlobProcessor_v12.java
index 1e2692922ed33..89fee5701c257 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureBlobProcessor_v12.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureBlobProcessor_v12.java
@@ -33,10 +33,8 @@
import org.apache.nifi.services.azure.storage.AzureStorageCredentialsDetails_v12;
import org.apache.nifi.services.azure.storage.AzureStorageCredentialsService_v12;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
@@ -80,10 +78,7 @@ public abstract class AbstractAzureBlobProcessor_v12 extends AbstractProcessor {
.description("Unsuccessful operations will be transferred to the failure relationship.")
.build();
- private static final Set RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- REL_SUCCESS,
- REL_FAILURE
- )));
+ private static final Set RELATIONSHIPS = Set.of(REL_SUCCESS, REL_FAILURE);
private volatile BlobServiceClientFactory clientFactory;
@@ -112,9 +107,7 @@ protected BlobServiceClient getStorageClient(PropertyContext context, PropertyDe
final AzureStorageCredentialsService_v12 credentialsService = context.getProperty(storageCredentialsServiceProperty).asControllerService(AzureStorageCredentialsService_v12.class);
final AzureStorageCredentialsDetails_v12 credentialsDetails = credentialsService.getCredentialsDetails(attributes);
- final BlobServiceClient storageClient = clientFactory.getStorageClient(credentialsDetails);
-
- return storageClient;
+ return clientFactory.getStorageClient(credentialsDetails);
}
protected Map createBlobAttributesMap(BlobClient blobClient) {
@@ -132,7 +125,7 @@ protected void applyStandardBlobAttributes(Map attributes, BlobC
}
protected void applyBlobMetadata(Map attributes, BlobClient blobClient) {
- Supplier props = new Supplier() {
+ Supplier props = new Supplier<>() {
BlobProperties properties;
public BlobProperties get() {
if (properties == null) {
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java
index 8a958e5880caf..bb62bd54006d8 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java
@@ -37,9 +37,7 @@
import org.apache.nifi.services.azure.storage.ADLSCredentialsDetails;
import org.apache.nifi.services.azure.storage.ADLSCredentialsService;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -91,10 +89,7 @@ public abstract class AbstractAzureDataLakeStorageProcessor extends AbstractProc
.description("Files that could not be written to Azure storage for some reason are transferred to this relationship")
.build();
- private static final Set RELATIONSHIPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- REL_SUCCESS,
- REL_FAILURE
- )));
+ private static final Set RELATIONSHIPS = Set.of(REL_SUCCESS, REL_FAILURE);
public static final String TEMP_FILE_DIRECTORY = "_nifitempdirectory";
@@ -119,12 +114,9 @@ public DataLakeServiceClient getStorageClient(PropertyContext context, FlowFile
final Map attributes = flowFile != null ? flowFile.getAttributes() : Collections.emptyMap();
final ADLSCredentialsService credentialsService = context.getProperty(ADLS_CREDENTIALS_SERVICE).asControllerService(ADLSCredentialsService.class);
-
final ADLSCredentialsDetails credentialsDetails = credentialsService.getCredentialsDetails(attributes);
- final DataLakeServiceClient storageClient = clientFactory.getStorageClient(credentialsDetails);
-
- return storageClient;
+ return clientFactory.getStorageClient(credentialsDetails);
}
public static String evaluateFileSystemProperty(ProcessContext context, FlowFile flowFile) {
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/ClientSideEncryptionSupport.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/ClientSideEncryptionSupport.java
index af3c90e3ce915..e451cb026741c 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/ClientSideEncryptionSupport.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/ClientSideEncryptionSupport.java
@@ -38,20 +38,19 @@
import javax.crypto.spec.SecretKeySpec;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
public interface ClientSideEncryptionSupport {
- List KEY_OPERATIONS = Arrays.asList(KeyOperation.WRAP_KEY, KeyOperation.UNWRAP_KEY);
+ List KEY_OPERATIONS = List.of(KeyOperation.WRAP_KEY, KeyOperation.UNWRAP_KEY);
PropertyDescriptor CSE_KEY_TYPE = new PropertyDescriptor.Builder()
.name("Client-Side Encryption Key Type")
.displayName("Client-Side Encryption Key Type")
.required(true)
.allowableValues(ClientSideEncryptionMethod.class)
- .defaultValue(ClientSideEncryptionMethod.NONE.getValue())
+ .defaultValue(ClientSideEncryptionMethod.NONE)
.description("Specifies the key type to use for client-side encryption.")
.build();
@@ -77,8 +76,7 @@ public interface ClientSideEncryptionSupport {
default Collection validateClientSideEncryptionProperties(ValidationContext validationContext) {
final List validationResults = new ArrayList<>();
- final String cseKeyTypeValue = validationContext.getProperty(CSE_KEY_TYPE).getValue();
- final ClientSideEncryptionMethod cseKeyType = ClientSideEncryptionMethod.valueOf(cseKeyTypeValue);
+ final ClientSideEncryptionMethod cseKeyType = validationContext.getProperty(CSE_KEY_TYPE).asAllowableValue(ClientSideEncryptionMethod.class);
final String cseKeyId = validationContext.getProperty(CSE_KEY_ID).getValue();
final String cseLocalKey = validationContext.getProperty(CSE_LOCAL_KEY).getValue();
if (cseKeyType != ClientSideEncryptionMethod.NONE && StringUtils.isBlank(cseKeyId)) {
@@ -116,8 +114,7 @@ default List validateLocalKey(String keyHex) {
}
default boolean isClientSideEncryptionEnabled(PropertyContext context) {
- final String cseKeyTypeValue = context.getProperty(CSE_KEY_TYPE).getValue();
- final ClientSideEncryptionMethod cseKeyType = ClientSideEncryptionMethod.valueOf(cseKeyTypeValue);
+ final ClientSideEncryptionMethod cseKeyType = context.getProperty(CSE_KEY_TYPE).asAllowableValue(ClientSideEncryptionMethod.class);
return cseKeyType != ClientSideEncryptionMethod.NONE;
}
@@ -144,18 +141,14 @@ default Optional getKeyWrapAlgorithm(byte[] keyBytes) {
final int keySize256 = 32;
final int keySize384 = 48;
final int keySize512 = 64;
- switch (keyBytes.length) {
- case keySize128:
- return Optional.of(KeyWrapAlgorithm.A128KW.toString());
- case keySize192:
- return Optional.of(KeyWrapAlgorithm.A192KW.toString());
- case keySize256:
- case keySize384:
- case keySize512:
- // Default to longest allowed key length for wrap
- return Optional.of(KeyWrapAlgorithm.A256KW.toString());
- default:
- return Optional.empty();
- }
+
+ return switch (keyBytes.length) {
+ case keySize128 -> Optional.of(KeyWrapAlgorithm.A128KW.toString());
+ case keySize192 -> Optional.of(KeyWrapAlgorithm.A192KW.toString());
+ case keySize256, keySize384, keySize512 ->
+ // Default to the longest allowed key length for wrap
+ Optional.of(KeyWrapAlgorithm.A256KW.toString());
+ default -> Optional.empty();
+ };
}
}
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AbstractAzureCosmosDBProcessor.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AbstractAzureCosmosDBProcessor.java
index fcc9d0c7a4138..b9c1a3b3a500e 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AbstractAzureCosmosDBProcessor.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AbstractAzureCosmosDBProcessor.java
@@ -27,12 +27,6 @@
import com.azure.cosmos.models.CosmosContainerProperties;
import com.azure.cosmos.models.CosmosContainerResponse;
import com.azure.cosmos.models.CosmosDatabaseResponse;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
import org.apache.nifi.annotation.lifecycle.OnScheduled;
import org.apache.nifi.annotation.lifecycle.OnStopped;
import org.apache.nifi.components.PropertyDescriptor;
@@ -45,6 +39,10 @@
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.services.azure.cosmos.AzureCosmosDBConnectionService;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
public abstract class AbstractAzureCosmosDBProcessor extends AbstractProcessor {
static final Relationship REL_SUCCESS = new Relationship.Builder()
@@ -57,11 +55,6 @@ public abstract class AbstractAzureCosmosDBProcessor extends AbstractProcessor {
.description("All FlowFiles that cannot be written to Cosmos DB are routed to this relationship")
.build();
- static final Relationship REL_ORIGINAL = new Relationship.Builder()
- .name("original")
- .description("All input FlowFiles that are part of a successful are routed to this relationship")
- .build();
-
static final PropertyDescriptor CONNECTION_SERVICE = new PropertyDescriptor.Builder()
.name("azure-cosmos-db-connection-service")
.displayName("Cosmos DB Connection Service")
@@ -94,28 +87,15 @@ public abstract class AbstractAzureCosmosDBProcessor extends AbstractProcessor {
.addValidator(StandardValidators.NON_BLANK_VALIDATOR)
.build();
- static final PropertyDescriptor CHARACTER_SET = new PropertyDescriptor.Builder()
- .name("charactor-set")
- .displayName("Charactor Set")
- .description("The Character Set in which the data is encoded")
- .required(false)
- .addValidator(StandardValidators.CHARACTER_SET_VALIDATOR)
- .defaultValue("UTF-8")
- .build();
-
- static final List descriptors;
-
- static {
- List _temp = new ArrayList<>();
- _temp.add(CONNECTION_SERVICE);
- _temp.add(AzureCosmosDBUtils.URI);
- _temp.add(AzureCosmosDBUtils.DB_ACCESS_KEY);
- _temp.add(AzureCosmosDBUtils.CONSISTENCY);
- _temp.add(DATABASE_NAME);
- _temp.add(CONTAINER_ID);
- _temp.add(PARTITION_KEY);
- descriptors = Collections.unmodifiableList(_temp);
- }
+ static final List descriptors = List.of(
+ CONNECTION_SERVICE,
+ AzureCosmosDBUtils.URI,
+ AzureCosmosDBUtils.DB_ACCESS_KEY,
+ AzureCosmosDBUtils.CONSISTENCY,
+ DATABASE_NAME,
+ CONTAINER_ID,
+ PARTITION_KEY
+ );
private CosmosClient cosmosClient;
private CosmosContainer container;
@@ -132,43 +112,24 @@ public void onScheduled(final ProcessContext context) throws CosmosException {
final String uri = context.getProperty(AzureCosmosDBUtils.URI).getValue();
final String accessKey = context.getProperty(AzureCosmosDBUtils.DB_ACCESS_KEY).getValue();
final String selectedConsistency = context.getProperty(AzureCosmosDBUtils.CONSISTENCY).getValue();
- final ConsistencyLevel clevel;
- switch (selectedConsistency) {
- case AzureCosmosDBUtils.CONSISTENCY_STRONG:
- clevel = ConsistencyLevel.STRONG;
- break;
- case AzureCosmosDBUtils.CONSISTENCY_CONSISTENT_PREFIX:
- clevel = ConsistencyLevel.CONSISTENT_PREFIX;
- break;
- case AzureCosmosDBUtils.CONSISTENCY_SESSION:
- clevel = ConsistencyLevel.SESSION;
- break;
- case AzureCosmosDBUtils.CONSISTENCY_BOUNDED_STALENESS:
- clevel = ConsistencyLevel.BOUNDED_STALENESS;
- break;
- case AzureCosmosDBUtils.CONSISTENCY_EVENTUAL:
- clevel = ConsistencyLevel.EVENTUAL;
- break;
- default:
- clevel = ConsistencyLevel.SESSION;
- }
+ final ConsistencyLevel consistencyLevel = AzureCosmosDBUtils.determineConsistencyLevel(selectedConsistency);
if (cosmosClient != null) {
onStopped();
}
if (logger.isDebugEnabled()) {
logger.debug("Creating CosmosClient");
}
- createCosmosClient(uri, accessKey, clevel);
+ createCosmosClient(uri, accessKey, consistencyLevel);
}
getCosmosDocumentContainer(context);
doPostActionOnSchedule(context);
}
- protected void createCosmosClient(final String uri, final String accessKey, final ConsistencyLevel clevel) {
+ protected void createCosmosClient(final String uri, final String accessKey, final ConsistencyLevel consistencyLevel) {
this.cosmosClient = new CosmosClientBuilder()
.endpoint(uri)
.key(accessKey)
- .consistencyLevel(clevel)
+ .consistencyLevel(consistencyLevel)
.buildClient();
}
@@ -194,7 +155,7 @@ protected void getCosmosDocumentContainer(final ProcessContext context) throws C
public final void onStopped() {
final ComponentLog logger = getLogger();
if (connectionService == null && cosmosClient != null) {
- // close client only when cosmoclient is created in Processor.
+ // close client only when cosmoClient is created in Processor.
if(logger.isDebugEnabled()) {
logger.debug("Closing CosmosClient");
}
@@ -235,7 +196,7 @@ protected String getConsistencyLevel(final ProcessContext context) {
@Override
protected Collection customValidate(ValidationContext context) {
- List retVal = new ArrayList<>();
+ List validationResults = new ArrayList<>();
boolean connectionServiceIsSet = context.getProperty(CONNECTION_SERVICE).isSet();
boolean uriIsSet = context.getProperty(AzureCosmosDBUtils.URI).isSet();
@@ -252,7 +213,7 @@ protected Collection customValidate(ValidationContext context)
AzureCosmosDBUtils.URI.getDisplayName(),
AzureCosmosDBUtils.DB_ACCESS_KEY.getDisplayName()
);
- retVal.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
+ validationResults.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
} else if (!connectionServiceIsSet && (!uriIsSet || !accessKeyIsSet)) {
// If connection Service is not set, Both of the Processor variable URI and accessKey
// should be set.
@@ -261,21 +222,21 @@ protected Collection customValidate(ValidationContext context)
AzureCosmosDBUtils.URI.getDisplayName(),
AzureCosmosDBUtils.DB_ACCESS_KEY.getDisplayName()
);
- retVal.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
+ validationResults.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
}
if (!databaseIsSet) {
final String msg = AbstractAzureCosmosDBProcessor.DATABASE_NAME.getDisplayName() + " must be set.";
- retVal.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
+ validationResults.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
}
if (!collectionIsSet) {
final String msg = AbstractAzureCosmosDBProcessor.CONTAINER_ID.getDisplayName() + " must be set.";
- retVal.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
+ validationResults.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
}
if (!partitionIsSet) {
final String msg = AbstractAzureCosmosDBProcessor.PARTITION_KEY.getDisplayName() + " must be set.";
- retVal.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
+ validationResults.add(new ValidationResult.Builder().valid(false).explanation(msg).build());
}
- return retVal;
+ return validationResults;
}
protected CosmosClient getCosmosClient() {
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AzureCosmosDBUtils.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AzureCosmosDBUtils.java
index 3b4fe220d5dca..5e34eecf2c7eb 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AzureCosmosDBUtils.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/AzureCosmosDBUtils.java
@@ -18,6 +18,7 @@
*/
package org.apache.nifi.processors.azure.cosmos.document;
+import com.azure.cosmos.ConsistencyLevel;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.processor.util.StandardValidators;
@@ -60,4 +61,15 @@ public final class AzureCosmosDBUtils {
CONSISTENCY_CONSISTENT_PREFIX, CONSISTENCY_EVENTUAL)
.addValidator(StandardValidators.NON_BLANK_VALIDATOR)
.build();
+
+ public static ConsistencyLevel determineConsistencyLevel(final String consistency) {
+ return switch (consistency) {
+ case CONSISTENCY_STRONG -> ConsistencyLevel.STRONG;
+ case CONSISTENCY_CONSISTENT_PREFIX -> ConsistencyLevel.CONSISTENT_PREFIX;
+ case CONSISTENCY_BOUNDED_STALENESS -> ConsistencyLevel.BOUNDED_STALENESS;
+ case CONSISTENCY_EVENTUAL -> ConsistencyLevel.EVENTUAL;
+ case CONSISTENCY_SESSION -> ConsistencyLevel.SESSION;
+ default -> ConsistencyLevel.SESSION;
+ };
+ }
}
diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/PutAzureCosmosDBRecord.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/PutAzureCosmosDBRecord.java
index d258f8786fb67..bed246b966c41 100644
--- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/PutAzureCosmosDBRecord.java
+++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/cosmos/document/PutAzureCosmosDBRecord.java
@@ -47,14 +47,13 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.stream.Stream;
-@Tags({ "azure", "cosmos", "insert", "record", "put" })
+@Tags({"azure", "cosmos", "insert", "record", "put"})
@InputRequirement(Requirement.INPUT_REQUIRED)
@CapabilityDescription("This processor is a record-aware processor for inserting data into Cosmos DB with Core SQL API. It uses a configured record reader and " +
"schema to read an incoming record set from the body of a Flowfile and then inserts those records into " +
@@ -67,49 +66,37 @@ public class PutAzureCosmosDBRecord extends AbstractAzureCosmosDBProcessor {
static final AllowableValue UPSERT_CONFLICT = new AllowableValue("UPSERT", "Upsert", "Conflicting records will be upserted, and FlowFile will not be routed to failure");
static final PropertyDescriptor RECORD_READER_FACTORY = new PropertyDescriptor.Builder()
- .name("record-reader")
- .displayName("Record Reader")
- .description("Specifies the Controller Service to use for parsing incoming data and determining the data's schema")
- .identifiesControllerService(RecordReaderFactory.class)
- .required(true)
- .build();
+ .name("record-reader")
+ .displayName("Record Reader")
+ .description("Specifies the Controller Service to use for parsing incoming data and determining the data's schema")
+ .identifiesControllerService(RecordReaderFactory.class)
+ .required(true)
+ .build();
static final PropertyDescriptor INSERT_BATCH_SIZE = new PropertyDescriptor.Builder()
- .name("insert-batch-size")
- .displayName("Insert Batch Size")
- .description("The number of records to group together for one single insert operation against Cosmos DB")
- .defaultValue("20")
- .required(false)
- .addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
- .build();
+ .name("insert-batch-size")
+ .displayName("Insert Batch Size")
+ .description("The number of records to group together for one single insert operation against Cosmos DB")
+ .defaultValue("20")
+ .required(false)
+ .addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
+ .build();
static final PropertyDescriptor CONFLICT_HANDLE_STRATEGY = new PropertyDescriptor.Builder()
- .name("azure-cosmos-db-conflict-handling-strategy")
- .displayName("Cosmos DB Conflict Handling Strategy")
- .description("Choose whether to ignore or upsert when conflict error occurs during insertion")
- .required(false)
- .allowableValues(IGNORE_CONFLICT, UPSERT_CONFLICT)
- .defaultValue(IGNORE_CONFLICT.getValue())
- .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
- .build();
-
-
- private final static Set relationships;
- private final static List propertyDescriptors;
-
- static {
- List _propertyDescriptors = new ArrayList<>();
- _propertyDescriptors.addAll(descriptors);
- _propertyDescriptors.add(RECORD_READER_FACTORY);
- _propertyDescriptors.add(INSERT_BATCH_SIZE);
- _propertyDescriptors.add(CONFLICT_HANDLE_STRATEGY);
- propertyDescriptors = Collections.unmodifiableList(_propertyDescriptors);
-
- final Set _relationships = new HashSet<>();
- _relationships.add(REL_SUCCESS);
- _relationships.add(REL_FAILURE);
- relationships = Collections.unmodifiableSet(_relationships);
- }
+ .name("azure-cosmos-db-conflict-handling-strategy")
+ .displayName("Cosmos DB Conflict Handling Strategy")
+ .description("Choose whether to ignore or upsert when conflict error occurs during insertion")
+ .required(false)
+ .allowableValues(IGNORE_CONFLICT, UPSERT_CONFLICT)
+ .defaultValue(IGNORE_CONFLICT)
+ .addValidator(StandardValidators.NON_BLANK_VALIDATOR)
+ .build();
+
+ private final static Set relationships = Set.of(REL_SUCCESS, REL_FAILURE);
+ private final static List propertyDescriptors = Stream.concat(
+ descriptors.stream(),
+ Stream.of(RECORD_READER_FACTORY, INSERT_BATCH_SIZE, CONFLICT_HANDLE_STRATEGY)
+ ).toList();
@Override
public Set getRelationships() {
@@ -121,19 +108,19 @@ public List getSupportedPropertyDescriptors() {
return propertyDescriptors;
}
- protected void bulkInsert(final List