You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ex...@apache.org on 2024/09/16 18:29:50 UTC
(nifi-api) branch main created (now 42a3500)
This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a change to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-api.git
at 42a3500 NIFI-13732 Initial commit of nifi-api 2.0.0-SNAPSHOT
This branch includes the following new commits:
new 42a3500 NIFI-13732 Initial commit of nifi-api 2.0.0-SNAPSHOT
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(nifi-api) 01/01: NIFI-13732 Initial commit of nifi-api 2.0.0-SNAPSHOT
Posted by ex...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-api.git
commit 42a35005c5d65d3989b2e452e20199eed75fec0e
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Mon Sep 16 13:24:36 2024 -0500
NIFI-13732 Initial commit of nifi-api 2.0.0-SNAPSHOT
---
.asf.yaml | 26 +
.github/PULL_REQUEST_TEMPLATE.md | 23 +
.github/workflows/build.yml | 28 +
.gitignore | 35 +
.mvn/wrapper/maven-wrapper.properties | 19 +
LICENSE | 201 +++++
NOTICE | 5 +
README.md | 59 ++
checkstyle.xml | 67 ++
mvnw | 259 ++++++
mvnw.cmd | 149 ++++
pom.xml | 281 ++++++
src/main/java/org/apache/nifi/action/Action.java | 47 +
.../java/org/apache/nifi/action/Component.java | 42 +
.../java/org/apache/nifi/action/Operation.java | 55 ++
.../action/component/details/ComponentDetails.java | 26 +
.../apache/nifi/action/details/ActionDetails.java | 26 +
.../annotation/behavior/DefaultRunDuration.java | 40 +
.../annotation/behavior/DynamicProperties.java | 37 +
.../nifi/annotation/behavior/DynamicProperty.java | 47 +
.../annotation/behavior/DynamicRelationship.java | 44 +
.../nifi/annotation/behavior/InputRequirement.java | 67 ++
.../nifi/annotation/behavior/PrimaryNodeOnly.java | 36 +
.../nifi/annotation/behavior/ReadsAttribute.java | 41 +
.../nifi/annotation/behavior/ReadsAttributes.java | 39 +
.../behavior/RequiresInstanceClassLoading.java | 54 ++
.../nifi/annotation/behavior/Restricted.java | 63 ++
.../nifi/annotation/behavior/Restriction.java | 50 ++
.../nifi/annotation/behavior/SideEffectFree.java | 45 +
.../apache/nifi/annotation/behavior/Stateful.java | 57 ++
.../nifi/annotation/behavior/SupportsBatching.java | 53 ++
.../SupportsSensitiveDynamicProperties.java | 36 +
.../nifi/annotation/behavior/SystemResource.java | 26 +
.../behavior/SystemResourceConsideration.java | 55 ++
.../behavior/SystemResourceConsiderations.java | 37 +
.../nifi/annotation/behavior/TriggerSerially.java | 38 +
.../TriggerWhenAnyDestinationAvailable.java | 40 +
.../nifi/annotation/behavior/TriggerWhenEmpty.java | 57 ++
.../nifi/annotation/behavior/WritesAttribute.java | 41 +
.../nifi/annotation/behavior/WritesAttributes.java | 39 +
.../annotation/configuration/DefaultSchedule.java | 45 +
.../annotation/configuration/DefaultSettings.java | 42 +
.../documentation/CapabilityDescription.java | 43 +
.../documentation/DeprecationNotice.java | 46 +
.../documentation/MultiProcessorUseCase.java | 77 ++
.../documentation/MultiProcessorUseCases.java | 36 +
.../documentation/ProcessorConfiguration.java | 57 ++
.../nifi/annotation/documentation/SeeAlso.java | 45 +
.../apache/nifi/annotation/documentation/Tags.java | 44 +
.../nifi/annotation/documentation/UseCase.java | 89 ++
.../nifi/annotation/documentation/UseCases.java | 36 +
.../apache/nifi/annotation/lifecycle/OnAdded.java | 49 ++
.../lifecycle/OnConfigurationRestored.java | 59 ++
.../nifi/annotation/lifecycle/OnDisabled.java | 61 ++
.../nifi/annotation/lifecycle/OnEnabled.java | 69 ++
.../nifi/annotation/lifecycle/OnRemoved.java | 57 ++
.../nifi/annotation/lifecycle/OnScheduled.java | 78 ++
.../nifi/annotation/lifecycle/OnShutdown.java | 53 ++
.../nifi/annotation/lifecycle/OnStopped.java | 75 ++
.../nifi/annotation/lifecycle/OnUnscheduled.java | 69 ++
.../notification/OnPrimaryNodeStateChange.java | 44 +
.../annotation/notification/PrimaryNodeState.java | 33 +
src/main/java/org/apache/nifi/asset/Asset.java | 64 ++
.../components/AbstractConfigurableComponent.java | 261 ++++++
.../org/apache/nifi/components/AllowableValue.java | 136 +++
.../ClassloaderIsolationKeyProvider.java | 60 ++
.../nifi/components/ConfigVerificationResult.java | 83 ++
.../nifi/components/ConfigurableComponent.java | 83 ++
.../org/apache/nifi/components/DescribedValue.java | 38 +
.../org/apache/nifi/components/PortFunction.java | 31 +
.../apache/nifi/components/PropertyDependency.java | 98 +++
.../apache/nifi/components/PropertyDescriptor.java | 911 +++++++++++++++++++
.../org/apache/nifi/components/PropertyValue.java | 371 ++++++++
.../apache/nifi/components/RequiredPermission.java | 57 ++
.../apache/nifi/components/ValidationContext.java | 203 +++++
.../apache/nifi/components/ValidationResult.java | 170 ++++
.../java/org/apache/nifi/components/Validator.java | 51 ++
.../apache/nifi/components/VersionedComponent.java | 40 +
.../components/resource/FileResourceReference.java | 100 +++
.../components/resource/ResourceCardinality.java | 34 +
.../nifi/components/resource/ResourceContext.java | 35 +
.../components/resource/ResourceDefinition.java | 37 +
.../components/resource/ResourceReference.java | 66 ++
.../resource/ResourceReferenceFactory.java | 24 +
.../components/resource/ResourceReferences.java | 73 ++
.../nifi/components/resource/ResourceType.java | 51 ++
.../resource/StandardResourceContext.java | 40 +
.../resource/StandardResourceDefinition.java | 40 +
.../resource/StandardResourceReferenceFactory.java | 131 +++
.../resource/StandardResourceReferences.java | 154 ++++
.../components/resource/URLResourceReference.java | 67 ++
.../nifi/components/resource/Utf8TextResource.java | 68 ++
.../org/apache/nifi/components/state/Scope.java | 41 +
.../apache/nifi/components/state/StateManager.java | 101 +++
.../org/apache/nifi/components/state/StateMap.java | 51 ++
.../state/exception/StateTooLargeException.java | 33 +
.../org/apache/nifi/context/ClusterContext.java | 30 +
.../org/apache/nifi/context/PropertyContext.java | 40 +
.../nifi/controller/AbstractControllerService.java | 130 +++
.../nifi/controller/ConfigurationContext.java | 66 ++
.../apache/nifi/controller/ControllerService.java | 206 +++++
.../ControllerServiceInitializationContext.java | 53 ++
.../nifi/controller/ControllerServiceLookup.java | 72 ++
.../apache/nifi/controller/NodeTypeProvider.java | 78 ++
.../org/apache/nifi/controller/ScheduledState.java | 43 +
.../controller/VerifiableControllerService.java | 66 ++
.../VersionedControllerServiceLookup.java | 29 +
.../apache/nifi/controller/queue/QueueSize.java | 94 ++
.../nifi/controller/status/ConnectionStatus.java | 313 +++++++
.../controller/status/FlowFileAvailability.java | 26 +
.../nifi/controller/status/LoadBalanceStatus.java | 26 +
.../apache/nifi/controller/status/NodeStatus.java | 207 +++++
.../apache/nifi/controller/status/PortStatus.java | 201 +++++
.../nifi/controller/status/ProcessGroupStatus.java | 694 +++++++++++++++
.../status/ProcessingPerformanceStatus.java | 107 +++
.../nifi/controller/status/ProcessorStatus.java | 323 +++++++
.../status/RemoteProcessGroupStatus.java | 236 +++++
.../apache/nifi/controller/status/RunStatus.java | 26 +
.../nifi/controller/status/StorageStatus.java | 69 ++
.../nifi/controller/status/TransmissionStatus.java | 23 +
.../analytics/ConnectionStatusPredictions.java | 115 +++
.../controller/status/analytics/QueryWindow.java | 62 ++
.../status/analytics/StatusAnalytics.java | 46 +
.../status/analytics/StatusAnalyticsEngine.java | 28 +
.../status/analytics/StatusAnalyticsModel.java | 64 ++
.../org/apache/nifi/diagnostics/StorageUsage.java | 71 ++
.../documentation/AbstractDocumentationWriter.java | 366 ++++++++
.../ExtensionDocumentationWriter.java | 63 ++
.../apache/nifi/documentation/ExtensionType.java | 29 +
.../org/apache/nifi/documentation/ServiceAPI.java | 51 ++
.../nifi/documentation/StandardServiceAPI.java | 51 ++
...tionControllerServiceInitializationContext.java | 71 ++
...ationFlowAnalysisRuleInitializationContext.java | 60 ++
...tionParameterProviderInitializationContext.java | 66 ++
...ocumentationProcessorInitializationContext.java | 64 ++
...ocumentationReportingInitializationContext.java | 88 ++
.../init/EmptyControllerServiceLookup.java | 53 ++
.../nifi/documentation/init/NopComponentLog.java | 174 ++++
.../nifi/documentation/init/NopStateManager.java | 42 +
.../init/StandaloneNodeTypeProvider.java | 31 +
.../documentation/xml/XmlDocumentationWriter.java | 670 ++++++++++++++
.../nifi/expression/AttributeExpression.java | 81 ++
.../nifi/expression/AttributeValueDecorator.java | 28 +
.../expression/ExpressionLanguageCompiler.java | 69 ++
.../nifi/expression/ExpressionLanguageScope.java | 63 ++
src/main/java/org/apache/nifi/flow/BatchSize.java | 76 ++
src/main/java/org/apache/nifi/flow/Bundle.java | 92 ++
.../java/org/apache/nifi/flow/ComponentType.java | 54 ++
.../org/apache/nifi/flow/ConnectableComponent.java | 116 +++
.../apache/nifi/flow/ConnectableComponentType.java | 27 +
.../org/apache/nifi/flow/ControllerServiceAPI.java | 65 ++
.../java/org/apache/nifi/flow/ExecutionEngine.java | 35 +
.../flow/ExternalControllerServiceReference.java | 44 +
.../nifi/flow/ParameterProviderReference.java | 64 ++
src/main/java/org/apache/nifi/flow/PortType.java | 23 +
src/main/java/org/apache/nifi/flow/Position.java | 76 ++
.../java/org/apache/nifi/flow/ScheduledState.java | 23 +
.../java/org/apache/nifi/flow/VersionedAsset.java | 44 +
.../org/apache/nifi/flow/VersionedComponent.java | 113 +++
.../nifi/flow/VersionedConfigurableComponent.java | 34 +
.../nifi/flow/VersionedConfigurableExtension.java | 77 ++
.../org/apache/nifi/flow/VersionedConnection.java | 174 ++++
.../nifi/flow/VersionedControllerService.java | 72 ++
.../nifi/flow/VersionedExtensionComponent.java | 32 +
.../apache/nifi/flow/VersionedExternalFlow.java | 68 ++
.../nifi/flow/VersionedExternalFlowMetadata.java | 84 ++
.../nifi/flow/VersionedFlowAnalysisRule.java | 50 ++
.../apache/nifi/flow/VersionedFlowCoordinates.java | 124 +++
.../nifi/flow/VersionedFlowRegistryClient.java | 48 +
.../java/org/apache/nifi/flow/VersionedFunnel.java | 25 +
.../java/org/apache/nifi/flow/VersionedLabel.java | 85 ++
.../org/apache/nifi/flow/VersionedParameter.java | 103 +++
.../nifi/flow/VersionedParameterContext.java | 90 ++
.../nifi/flow/VersionedParameterProvider.java | 89 ++
.../java/org/apache/nifi/flow/VersionedPort.java | 87 ++
.../apache/nifi/flow/VersionedProcessGroup.java | 246 ++++++
.../org/apache/nifi/flow/VersionedProcessor.java | 200 +++++
.../nifi/flow/VersionedPropertyDescriptor.java | 83 ++
.../apache/nifi/flow/VersionedRemoteGroupPort.java | 119 +++
.../nifi/flow/VersionedRemoteProcessGroup.java | 146 ++++
.../apache/nifi/flow/VersionedReportingTask.java | 69 ++
.../nifi/flow/VersionedReportingTaskSnapshot.java | 47 +
.../nifi/flow/VersionedResourceCardinality.java | 32 +
.../nifi/flow/VersionedResourceDefinition.java | 45 +
.../apache/nifi/flow/VersionedResourceType.java | 42 +
...ExternalResourceConflictResolutionStrategy.java | 32 +
.../flow/resource/ExternalResourceDescriptor.java | 33 +
.../flow/resource/ExternalResourceProvider.java | 53 ++
...ernalResourceProviderInitializationContext.java | 45 +
.../ImmutableExternalResourceDescriptor.java | 43 +
.../nifi/flowanalysis/AbstractAnalysisResult.java | 73 ++
.../flowanalysis/AbstractFlowAnalysisRule.java | 53 ++
.../nifi/flowanalysis/ComponentAnalysisResult.java | 45 +
.../nifi/flowanalysis/EnforcementPolicy.java | 29 +
.../nifi/flowanalysis/FlowAnalysisContext.java | 50 ++
.../apache/nifi/flowanalysis/FlowAnalysisRule.java | 69 ++
.../nifi/flowanalysis/FlowAnalysisRuleContext.java | 53 ++
.../FlowAnalysisRuleInitializationContext.java | 46 +
.../nifi/flowanalysis/FlowAnalysisRuleState.java | 22 +
.../nifi/flowanalysis/GroupAnalysisResult.java | 112 +++
.../flowanalysis/VerifiableFlowAnalysisRule.java | 66 ++
.../java/org/apache/nifi/flowfile/FlowFile.java | 133 +++
.../nifi/flowfile/attributes/CoreAttributes.java | 65 ++
.../flowfile/attributes/FlowFileAttributeKey.java | 22 +
.../org/apache/nifi/kerberos/KerberosContext.java | 51 ++
.../java/org/apache/nifi/logging/ComponentLog.java | 220 +++++
.../java/org/apache/nifi/logging/LogLevel.java | 28 +
.../java/org/apache/nifi/logging/LogMessage.java | 123 +++
.../nifi/migration/PropertyConfiguration.java | 210 +++++
.../nifi/migration/RelationshipConfiguration.java | 105 +++
.../nifi/parameter/AbstractParameterProvider.java | 79 ++
.../java/org/apache/nifi/parameter/Parameter.java | 179 ++++
.../apache/nifi/parameter/ParameterDescriptor.java | 115 +++
.../org/apache/nifi/parameter/ParameterGroup.java | 54 ++
.../parameter/ParameterGroupConfiguration.java | 100 +++
.../apache/nifi/parameter/ParameterProvider.java | 87 ++
.../ParameterProviderInitializationContext.java | 47 +
.../nifi/parameter/ParameterSensitivity.java | 35 +
.../parameter/VerifiableParameterProvider.java | 63 ++
.../apache/nifi/processor/AbstractProcessor.java | 36 +
.../processor/AbstractSessionFactoryProcessor.java | 145 +++
.../java/org/apache/nifi/processor/DataUnit.java | 245 ++++++
.../org/apache/nifi/processor/FlowFileFilter.java | 93 ++
.../org/apache/nifi/processor/ProcessContext.java | 167 ++++
.../org/apache/nifi/processor/ProcessSession.java | 901 +++++++++++++++++++
.../nifi/processor/ProcessSessionFactory.java | 30 +
.../java/org/apache/nifi/processor/Processor.java | 130 +++
.../processor/ProcessorInitializationContext.java | 55 ++
.../org/apache/nifi/processor/Relationship.java | 144 +++
.../apache/nifi/processor/VerifiableProcessor.java | 65 ++
.../exception/FlowFileAccessException.java | 36 +
.../exception/FlowFileHandlingException.java | 38 +
.../exception/MissingFlowFileException.java | 36 +
.../nifi/processor/exception/ProcessException.java | 43 +
.../exception/TerminatedTaskException.java | 37 +
.../nifi/processor/io/InputStreamCallback.java | 34 +
.../nifi/processor/io/OutputStreamCallback.java | 35 +
.../apache/nifi/processor/io/StreamCallback.java | 37 +
.../nifi/processor/util/StandardValidators.java | 969 +++++++++++++++++++++
.../apache/nifi/processor/util/URLValidator.java | 100 +++
.../nifi/provenance/ProvenanceEventBuilder.java | 326 +++++++
.../nifi/provenance/ProvenanceEventRecord.java | 248 ++++++
.../nifi/provenance/ProvenanceEventRepository.java | 97 +++
.../nifi/provenance/ProvenanceEventType.java | 139 +++
.../apache/nifi/provenance/ProvenanceReporter.java | 749 ++++++++++++++++
.../apache/nifi/registry/EnvironmentVariables.java | 122 +++
.../apache/nifi/registry/VariableDescriptor.java | 153 ++++
.../registry/flow/AbstractFlowRegistryClient.java | 50 ++
.../registry/flow/AuthenticationException.java | 28 +
.../nifi/registry/flow/AuthorizationException.java | 28 +
.../apache/nifi/registry/flow/BucketLocation.java | 54 ++
.../registry/flow/FlowAlreadyExistsException.java | 29 +
.../apache/nifi/registry/flow/FlowLocation.java | 45 +
.../nifi/registry/flow/FlowRegistryBranch.java | 52 ++
.../nifi/registry/flow/FlowRegistryBucket.java | 66 ++
.../nifi/registry/flow/FlowRegistryClient.java | 266 ++++++
.../FlowRegistryClientConfigurationContext.java | 25 +
.../FlowRegistryClientInitializationContext.java | 31 +
.../nifi/registry/flow/FlowRegistryException.java | 31 +
.../flow/FlowRegistryInvalidException.java | 25 +
.../registry/flow/FlowRegistryPermissions.java | 47 +
.../flow/FlowRegistryPreCommitException.java | 23 +
.../nifi/registry/flow/FlowVersionLocation.java | 67 ++
.../nifi/registry/flow/NoSuchBucketException.java | 28 +
.../nifi/registry/flow/NoSuchFlowException.java | 28 +
.../registry/flow/NoSuchFlowVersionException.java | 28 +
.../apache/nifi/registry/flow/RegisterAction.java | 25 +
.../apache/nifi/registry/flow/RegisteredFlow.java | 120 +++
.../nifi/registry/flow/RegisteredFlowSnapshot.java | 108 +++
.../flow/RegisteredFlowSnapshotMetadata.java | 83 ++
.../registry/flow/RegisteredFlowVersionInfo.java | 31 +
.../nifi/registry/flow/VersionedFlowState.java | 63 ++
.../nifi/reporting/AbstractReportingTask.java | 117 +++
.../java/org/apache/nifi/reporting/Bulletin.java | 156 ++++
.../org/apache/nifi/reporting/BulletinQuery.java | 118 +++
.../apache/nifi/reporting/BulletinRepository.java | 109 +++
.../org/apache/nifi/reporting/ComponentType.java | 78 ++
.../org/apache/nifi/reporting/EventAccess.java | 116 +++
.../nifi/reporting/InitializationException.java | 32 +
.../apache/nifi/reporting/ReportingContext.java | 107 +++
.../reporting/ReportingInitializationContext.java | 86 ++
.../org/apache/nifi/reporting/ReportingTask.java | 104 +++
.../java/org/apache/nifi/reporting/Severity.java | 24 +
.../nifi/reporting/VerifiableReportingTask.java | 66 ++
.../org/apache/nifi/scheduling/ExecutionNode.java | 31 +
.../apache/nifi/scheduling/SchedulingStrategy.java | 52 ++
.../java/org/apache/nifi/search/SearchContext.java | 46 +
.../java/org/apache/nifi/search/SearchResult.java | 75 ++
.../java/org/apache/nifi/search/Searchable.java | 24 +
.../java/org/apache/nifi/time/DurationFormat.java | 236 +++++
.../apache/nifi/components/EnumDescribedValue.java | 61 ++
.../nifi/components/EnumNotDescribedValue.java | 21 +
.../nifi/components/TestPropertyDescriptor.java | 345 ++++++++
.../TestStandardResourceReferenceFactory.java | 94 ++
.../resource/TestStandardResourceReferences.java | 163 ++++
.../org/apache/nifi/parameter/TestParameter.java | 218 +++++
.../org/apache/nifi/processor/TestDataUnit.java | 41 +
.../org/apache/nifi/time/TestDurationFormat.java | 278 ++++++
.../org/apache/nifi/util/TestURLValidator.java | 41 +
299 files changed, 28157 insertions(+)
diff --git a/.asf.yaml b/.asf.yaml
new file mode 100644
index 0000000..6022a38
--- /dev/null
+++ b/.asf.yaml
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: Apache-2.0
+
+github:
+ description: "Apache NiFi API"
+ homepage: https://nifi.apache.org
+ labels:
+ - apache
+ - nifi
+ features:
+ wiki: false
+ issues: false
+ projects: false
+ enabled_merge_buttons:
+ squash: true
+ autolink_jira:
+ - NIFI
+ - NIP
+ protected_branches:
+ main:
+ required_signatures: true
+ required_linear_history: true
+notifications:
+ commits: commits@nifi.apache.org
+ issues: issues@nifi.apache.org
+ pullrequests: issues@nifi.apache.org
+ jira_options: link worklog
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..2a1ef8e
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,23 @@
+<!-- SPDX-License-Identifier: Apache-2.0 -->
+
+# Summary
+
+[NIFI-00000](https://issues.apache.org/jira/browse/NIFI-00000)
+
+# Tracking
+
+Please complete the following tracking steps prior to pull request creation.
+
+### Issue Tracking
+
+- [ ] [Apache NiFi Jira](https://issues.apache.org/jira/browse/NIFI) issue created
+
+### Pull Request Tracking
+
+- [ ] Pull Request title starts with Apache NiFi Jira issue number, such as `NIFI-00000`
+- [ ] Pull Request commit message starts with Apache NiFi Jira issue number, as such `NIFI-00000`
+
+### Pull Request Formatting
+
+- [ ] Pull Request based on current revision of the `main` branch
+- [ ] Pull Request refers to a feature branch with one commit containing changes
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..b9f7518
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: Apache-2.0
+
+name: build
+
+on:
+ push:
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ contents: read
+
+jobs:
+ build-java-21:
+ name: Java 21
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Sources
+ uses: actions/checkout@v4
+ - name: Setup Java JDK
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'liberica'
+ cache: 'maven'
+ - name: Build
+ run: ./mvnw --batch-mode verify
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c1606a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: Apache-2.0
+
+# Editors
+.idea/
+.vscode/
+.java-version
+.project
+.settings
+.classpath
+nbactions.xml
+nb-configuration.xml
+.DS_Store
+.metadata
+.recommenders
+*.iml
+*.iws
+*.ipr
+*~
+
+# Binaries
+*.class
+target/
+
+# Packages
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# Logs
+*.log
+hs_err_pid*
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..d58dfb7
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.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.
+wrapperVersion=3.3.2
+distributionType=only-script
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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/NOTICE b/NOTICE
new file mode 100644
index 0000000..bc8e448
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,5 @@
+Apache NiFi API
+Copyright 2024 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7a4ae23
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+<!-- SPDX-License-Identifier: Apache-2.0 -->
+
+# Apache NiFi API
+
+[](https://github.com/apache/nifi-api/blob/main/LICENSE)
+[](https://github.com/apache/nifi-api/actions/workflows/build.yml)
+
+The [Apache NiFi](https://nifi.apache.org) API repository contains public interfaces and classes for building extension components.
+
+## Requirements
+
+- Java 21
+- Maven 3.9
+
+## Versioning
+
+The project follows [Semantic Versioning 2.0.0](https://semver.org).
+
+## Building
+
+This project uses [Maven](https://maven.apache.org) to build distribution packages.
+
+This project includes the [Maven Wrapper](https://maven.apache.org/wrapper/) to build with required Maven versions.
+
+```
+./mvnw install
+```
+
+## Verifying
+
+Code and documentation changes must pass build verification to be eligible for review.
+
+```
+./mvnw verify
+```
+
+The project uses several build plugins for static code analysis and license evaluation.
+
+- [Apache Maven Checkstyle Plugin](https://maven.apache.org/plugins/maven-checkstyle-plugin/)
+- [Apache Maven PMD Plugin](https://maven.apache.org/plugins/maven-pmd-plugin/)
+- [Apache Release Audit Tool Plugin](https://creadur.apache.org/rat/apache-rat-plugin/)
+- [JaCoCo Maven Plugin](https://www.jacoco.org/jacoco/trunk/doc/maven.html)
+
+## Documentation
+
+The [Apache NiFi Documentation](https://nifi.apache.org/documentation/) includes reference information for project capabilities.
+
+## Contributing
+
+The [Apache NiFi Contributor Guide](https://cwiki.apache.org/confluence/display/NIFI/Contributor+Guide)
+describes the process for getting involved in the development of this project.
+
+## Issues
+
+This project uses [Jira](https://issues.apache.org/jira/browse/NIFI) for tracking bugs and features.
+
+## Licensing
+
+This project is released under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..7c87426
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!-- SPDX-License-Identifier: Apache-2.0 -->
+<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
+<module name="Checker">
+ <module name="NewlineAtEndOfFile"/>
+ <module name="FileLength"/>
+ <module name="LineLength">
+ <property name="max" value="200"/>
+ <property name="fileExtensions" value="java"/>
+ </module>
+ <module name="FileTabCharacter"/>
+ <module name="RegexpSingleline">
+ <property name="format" value="\s+$"/>
+ <property name="minimum" value="0"/>
+ <property name="maximum" value="0"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+ <module name="TreeWalker">
+ <module name="MethodName"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InvalidJavadocPosition"/>
+ <module name="JavadocMethod"/>
+ <module name="JavadocType"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+ <module name="AvoidStarImport"/>
+ <module name="IllegalImport"/>
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+ <module name="MethodLength">
+ <property name="max" value="250"/>
+ </module>
+ <module name="EmptyForIteratorPad"/>
+ <module name="GenericWhitespace"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+ <module name="ModifierOrder"/>
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock"/>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="MultipleVariableDeclarations"/>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+ <module name="InterfaceIsType"/>
+ <module name="ArrayTypeStyle"/>
+ <module name="TodoComment"/>
+ <module name="UpperEll"/>
+ <module name="UnusedLocalVariable"/>
+ </module>
+</module>
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..19529dd
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,259 @@
+#!/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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+# JAVA_HOME - location of a JDK home dir, required when download maven via java source
+# MVNW_REPOURL - repo url base for downloading maven distribution
+# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+ [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+ native_path() { cygpath --path --windows "$1"; }
+ ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+ # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+ if [ -n "${JAVA_HOME-}" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACCMD="$JAVA_HOME/jre/sh/javac"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ JAVACCMD="$JAVA_HOME/bin/javac"
+
+ if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+ echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+ return 1
+ fi
+ fi
+ else
+ JAVACMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v java
+ )" || :
+ JAVACCMD="$(
+ 'set' +e
+ 'unset' -f command 2>/dev/null
+ 'command' -v javac
+ )" || :
+
+ if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+ echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+ return 1
+ fi
+ fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+ str="${1:-}" h=0
+ while [ -n "$str" ]; do
+ char="${str%"${str#?}"}"
+ h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+ str="${str#?}"
+ done
+ printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+ printf %s\\n "$1" >&2
+ exit 1
+}
+
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+ case "${key-}" in
+ distributionUrl) distributionUrl=$(trim "${value-}") ;;
+ distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+ esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+ MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+ case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+ *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+ :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+ :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+ :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+ *)
+ echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+ distributionPlatform=linux-amd64
+ ;;
+ esac
+ distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+ ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+ unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+ exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+ verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+ clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+ trap clean HUP INT TERM EXIT
+else
+ die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+ distributionUrl="${distributionUrl%.zip}.tar.gz"
+ distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+ verbose "Found wget ... using wget"
+ wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+ verbose "Found curl ... using curl"
+ curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+ verbose "Falling back to use Java to download"
+ javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+ targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+ cat >"$javaSource" <<-END
+ public class Downloader extends java.net.Authenticator
+ {
+ protected java.net.PasswordAuthentication getPasswordAuthentication()
+ {
+ return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+ }
+ public static void main( String[] args ) throws Exception
+ {
+ setDefault( new Downloader() );
+ java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+ }
+ }
+ END
+ # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+ verbose " - Compiling Downloader.java ..."
+ "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+ verbose " - Running Downloader.java ..."
+ "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+ distributionSha256Result=false
+ if [ "$MVN_CMD" = mvnd.sh ]; then
+ echo "Checksum validation is not supported for maven-mvnd." >&2
+ echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ elif command -v sha256sum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ elif command -v shasum >/dev/null; then
+ if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+ distributionSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+ exit 1
+ fi
+ if [ $distributionSha256Result = false ]; then
+ echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+ echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+ unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+ tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..249bdf3
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with 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,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM MVNW_REPOURL - repo url base for downloading maven distribution
+@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+ IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+ $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+ Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+ "maven-mvnd-*" {
+ $USE_MVND = $true
+ $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+ $MVN_CMD = "mvnd.cmd"
+ break
+ }
+ default {
+ $USE_MVND = $false
+ $MVN_CMD = $script -replace '^mvnw','mvn'
+ break
+ }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+if ($env:MVNW_REPOURL) {
+ $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+ $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+ $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+ Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+ Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+ exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+ Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+ if ($TMP_DOWNLOAD_DIR.Exists) {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+ }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+ $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+ if ($USE_MVND) {
+ Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+ }
+ Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+ if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+ Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+ }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+ Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+ if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+ Write-Error "fail to move MAVEN_HOME"
+ }
+} finally {
+ try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+ catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c6eeb46
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,281 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>33</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-api</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>nifi-api</name>
+ <description>Apache NiFi API</description>
+ <url>https://nifi.apache.org</url>
+ <inceptionYear>2014</inceptionYear>
+ <organization>
+ <name>Apache NiFi Project</name>
+ <url>https://nifi.apache.org</url>
+ </organization>
+ <licenses>
+ <license>
+ <name>Apache License 2.0</name>
+ <url>https://www.apache.org/licenses/LICENSE-2.0</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <developers>
+ <developer>
+ <name>Apache NiFi Developers</name>
+ <email>dev@nifi.apache.org</email>
+ <organization>Apache NiFi Project</organization>
+ <organizationUrl>https://nifi.apache.org</organizationUrl>
+ </developer>
+ </developers>
+ <mailingLists>
+ <mailingList>
+ <name>Developers</name>
+ <subscribe>dev-subscribe@nifi.apache.org</subscribe>
+ <unsubscribe>dev-unsubscribe@nifi.apache.org</unsubscribe>
+ <post>dev@nifi.apache.org</post>
+ <archive>https://lists.apache.org/list.html?dev@nifi.apache.org</archive>
+ </mailingList>
+ <mailingList>
+ <name>Users</name>
+ <subscribe>users-subscribe@nifi.apache.org</subscribe>
+ <unsubscribe>users-unsubscribe@nifi.apache.org</unsubscribe>
+ <post>users@nifi.apache.org</post>
+ <archive>https://lists.apache.org/list.html?users@nifi.apache.org</archive>
+ </mailingList>
+ <mailingList>
+ <name>Commits</name>
+ <subscribe>commits-subscribe@nifi.apache.org</subscribe>
+ <unsubscribe>commits-unsubscribe@nifi.apache.org</unsubscribe>
+ <post>commits@nifi.apache.org</post>
+ <archive>https://lists.apache.org/list.html?commits@nifi.apache.org</archive>
+ </mailingList>
+ </mailingLists>
+ <scm>
+ <connection>scm:git:https://github.com/apache/nifi-api</connection>
+ <developerConnection>scm:git:https://github.com/apache/nifi-api</developerConnection>
+ <url>https://github.com/apache/nifi-api</url>
+ </scm>
+ <issueManagement>
+ <system>jira</system>
+ <url>https://issues.apache.org/jira/browse/NIFI</url>
+ </issueManagement>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <maven.compiler.release>21</maven.compiler.release>
+ <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
+ <minimalJavaBuildVersion>21</minimalJavaBuildVersion>
+ <minimalMavenBuildVersion>3.9.9</minimalMavenBuildVersion>
+
+ <checkstyle.version>10.18.1</checkstyle.version>
+ <junit-bom.version>5.11.0</junit-bom.version>
+ <jacoco.version>0.8.12</jacoco.version>
+ <maven-pmd-plugin.version>3.25.0</maven-pmd-plugin.version>
+ <mockito.version>5.13.0</mockito.version>
+ <pmd.version>7.5.0</pmd.version>
+ <swagger-annotations.version>2.2.23</swagger-annotations.version>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.junit</groupId>
+ <artifactId>junit-bom</artifactId>
+ <version>${junit-bom.version}</version>
+ <scope>import</scope>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-bom</artifactId>
+ <version>${mockito.version}</version>
+ <scope>import</scope>
+ <type>pom</type>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <!-- Optional dependency for generating OpenAPI Specification -->
+ <dependency>
+ <groupId>io.swagger.core.v3</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ <version>${swagger-annotations.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-params</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>${jacoco.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${version.maven-checkstyle-plugin}</version>
+ <dependencies>
+ <dependency>
+ <groupId>com.puppycrawl.tools</groupId>
+ <artifactId>checkstyle</artifactId>
+ <version>${checkstyle.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${maven-pmd-plugin.version}</version>
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.pmd</groupId>
+ <artifactId>pmd-core</artifactId>
+ <version>${pmd.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sourceforge.pmd</groupId>
+ <artifactId>pmd-java</artifactId>
+ <version>${pmd.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <useIdeaDefaultExcludes>true</useIdeaDefaultExcludes>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ <phase>verify</phase>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <configLocation>checkstyle.xml</configLocation>
+ <includeTestSourceDirectory>true</includeTestSourceDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <id>check</id>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <skipIfEmpty>true</skipIfEmpty>
+ <archive>
+ <manifest>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Automatic-Module-Name>${project.groupId}</Automatic-Module-Name>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <quiet>true</quiet>
+ <failOnError>true</failOnError>
+ <failOnWarnings>true</failOnWarnings>
+ <doclint>all,-missing</doclint>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <configuration>
+ <printFailingErrors>true</printFailingErrors>
+ <linkXRef>false</linkXRef>
+ <targetJdk>${maven.compiler.release}</targetJdk>
+ </configuration>
+ <executions>
+ <execution>
+ <id>check</id>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>prepare-agent</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>report</id>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/src/main/java/org/apache/nifi/action/Action.java b/src/main/java/org/apache/nifi/action/Action.java
new file mode 100644
index 0000000..44c28a6
--- /dev/null
+++ b/src/main/java/org/apache/nifi/action/Action.java
@@ -0,0 +1,47 @@
+/*
+ * 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.action;
+
+import java.io.Serializable;
+import java.util.Date;
+import org.apache.nifi.action.component.details.ComponentDetails;
+import org.apache.nifi.action.details.ActionDetails;
+
+/**
+ * An action taken on the flow by a user.
+ */
+public interface Action extends Serializable {
+
+ Integer getId();
+
+ Date getTimestamp();
+
+ String getUserIdentity();
+
+ String getSourceId();
+
+ String getSourceName();
+
+ Component getSourceType();
+
+ ComponentDetails getComponentDetails();
+
+ Operation getOperation();
+
+ ActionDetails getActionDetails();
+
+}
diff --git a/src/main/java/org/apache/nifi/action/Component.java b/src/main/java/org/apache/nifi/action/Component.java
new file mode 100644
index 0000000..9a65185
--- /dev/null
+++ b/src/main/java/org/apache/nifi/action/Component.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.action;
+
+/**
+ * Defines possible components for a given action.
+ */
+public enum Component {
+
+ Controller,
+ Processor,
+ InputPort,
+ OutputPort,
+ ProcessGroup,
+ RemoteProcessGroup,
+ Funnel,
+ Connection,
+ ControllerService,
+ ReportingTask,
+ FlowAnalysisRule,
+ FlowRegistryClient,
+ ParameterContext,
+ ParameterProvider,
+ AccessPolicy,
+ User,
+ UserGroup,
+ Label;
+}
diff --git a/src/main/java/org/apache/nifi/action/Operation.java b/src/main/java/org/apache/nifi/action/Operation.java
new file mode 100644
index 0000000..1a199c5
--- /dev/null
+++ b/src/main/java/org/apache/nifi/action/Operation.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.action;
+
+/**
+ * Defines possible operations for a given action.
+ */
+public enum Operation {
+
+ Add("Add"),
+ Remove("Remove"),
+ Paste("Paste"),
+ Configure("Configure"),
+ Move("Move"),
+ Disconnect("Disconnect"),
+ Connect("Connect"),
+ Start("Start"),
+ Stop("Stop"),
+ Enable("Enable"),
+ Disable("Disable"),
+ Batch("Batch"),
+ Purge("Purge"),
+ ClearState("Clear State"),
+ StartVersionControl("Start Version Control"),
+ StopVersionControl("Stop Version Control"),
+ CommitLocalChanges("Commit Local Changes"),
+ RevertLocalChanges("Revert Local Changes"),
+ ChangeVersion("Change Version"),
+ RunOnce("RunOnce");
+
+ private final String label;
+
+ Operation(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/action/component/details/ComponentDetails.java b/src/main/java/org/apache/nifi/action/component/details/ComponentDetails.java
new file mode 100644
index 0000000..1eb35c1
--- /dev/null
+++ b/src/main/java/org/apache/nifi/action/component/details/ComponentDetails.java
@@ -0,0 +1,26 @@
+/*
+ * 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.action.component.details;
+
+import java.io.Serializable;
+
+/**
+ * Base interface for providing component details to an Action.
+ */
+public interface ComponentDetails extends Serializable {
+
+}
diff --git a/src/main/java/org/apache/nifi/action/details/ActionDetails.java b/src/main/java/org/apache/nifi/action/details/ActionDetails.java
new file mode 100644
index 0000000..75ebd9d
--- /dev/null
+++ b/src/main/java/org/apache/nifi/action/details/ActionDetails.java
@@ -0,0 +1,26 @@
+/*
+ * 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.action.details;
+
+import java.io.Serializable;
+
+/**
+ * Provides additional details about a given action.
+ */
+public interface ActionDetails extends Serializable {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/DefaultRunDuration.java b/src/main/java/org/apache/nifi/annotation/behavior/DefaultRunDuration.java
new file mode 100644
index 0000000..2ef6aa8
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/DefaultRunDuration.java
@@ -0,0 +1,40 @@
+/*
+ * 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.annotation.behavior;
+
+import java.time.Duration;
+
+public enum DefaultRunDuration {
+ NO_BATCHING(Duration.ZERO),
+ TWENTY_FIVE_MILLIS(Duration.ofMillis(25)),
+ FIFTY_MILLIS(Duration.ofMillis(50)),
+ ONE_HUNDRED_MILLIS(Duration.ofMillis(100)),
+ TWO_HUNDRED_FIFTY_MILLIS(Duration.ofMillis(250)),
+ FIVE_HUNDRED_MILLIS(Duration.ofMillis(500)),
+ ONE_SECOND(Duration.ofSeconds(1)),
+ TWO_SECONDS(Duration.ofSeconds(2));
+
+ private final Duration duration;
+
+ DefaultRunDuration(final Duration duration) {
+ this.duration = duration;
+ }
+
+ public Duration getDuration() {
+ return duration;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperties.java b/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperties.java
new file mode 100644
index 0000000..9670535
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperties.java
@@ -0,0 +1,37 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a component has more than one dynamic property
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DynamicProperties {
+
+ DynamicProperty[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperty.java b/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperty.java
new file mode 100644
index 0000000..5bccd48
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/DynamicProperty.java
@@ -0,0 +1,47 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.expression.ExpressionLanguageScope;
+
+/**
+ * An annotation that may be placed on a {@link ConfigurableComponent} to
+ * indicate that it supports a dynamic property.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DynamicProperty {
+
+ String name();
+
+ String value();
+
+ String description();
+
+ ExpressionLanguageScope expressionLanguageScope() default ExpressionLanguageScope.NONE;
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/DynamicRelationship.java b/src/main/java/org/apache/nifi/annotation/behavior/DynamicRelationship.java
new file mode 100644
index 0000000..006f6ad
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/DynamicRelationship.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.processor.Processor;
+import org.apache.nifi.processor.Relationship;
+
+/**
+ * Annotation to indicate that a {@link Processor} supports dynamic
+ * relationship. A dynamic {@link Relationship} is one where the relationship is
+ * generated based on a user defined {@link PropertyDescriptor}
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DynamicRelationship {
+
+ String name();
+
+ String description();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/InputRequirement.java b/src/main/java/org/apache/nifi/annotation/behavior/InputRequirement.java
new file mode 100644
index 0000000..372d80c
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/InputRequirement.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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker interface that a Processor can use to indicate whether it accepts, requires, or forbids
+ * input from other Processors. This information is used by the framework in order to ensure that
+ * a Processor is marked as invalid if it is missing necessary input or has input that will be ignored.
+ * This information also is used by the NiFi UI in order to prevent users from making connections
+ * to Processors that don't make sense.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface InputRequirement {
+ Requirement value();
+
+ enum Requirement {
+ /**
+ * This value is used to indicate that the Processor requires input from other Processors
+ * in order to run. As a result, the Processor will not be valid if it does not have any
+ * incoming connections.
+ */
+ INPUT_REQUIRED,
+
+ /**
+ * This value is used to indicate that the Processor will consume data from an incoming
+ * connection but does not require an incoming connection in order to perform its task.
+ * If the {@link InputRequirement} annotation is not present, this is the default value
+ * that is used.
+ */
+ INPUT_ALLOWED,
+
+ /**
+ * This value is used to indicate that the Processor is a "Source Processor" and does
+ * not accept incoming connections. Because the Processor does not pull FlowFiles from
+ * an incoming connection, it can be very confusing for users who create incoming connections
+ * to the Processor. As a result, this value can be used in order to clarify that incoming
+ * connections will not be used. This prevents the user from even creating such a connection.
+ */
+ INPUT_FORBIDDEN;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/PrimaryNodeOnly.java b/src/main/java/org/apache/nifi/annotation/behavior/PrimaryNodeOnly.java
new file mode 100644
index 0000000..6443383
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/PrimaryNodeOnly.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a {@link org.apache.nifi.processor.Processor} implementation
+ * can use to indicate that the {@link org.apache.nifi.scheduling.ExecutionNode}
+ * of the processor is to be set to PRIMARY
+ */
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface PrimaryNodeOnly {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttribute.java b/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttribute.java
new file mode 100644
index 0000000..7094b92
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttribute.java
@@ -0,0 +1,41 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.processor.Processor Processor} indicating that this
+ * processor reads a specific FlowFile attribute.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ReadsAttribute {
+
+ String attribute();
+
+ String description() default "";
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttributes.java b/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttributes.java
new file mode 100644
index 0000000..fe1f8bd
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/ReadsAttributes.java
@@ -0,0 +1,39 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.processor.Processor Processor} indicating that this
+ * processor reads specific FlowFile attributes.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ReadsAttributes {
+
+ ReadsAttribute[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/RequiresInstanceClassLoading.java b/src/main/java/org/apache/nifi/annotation/behavior/RequiresInstanceClassLoading.java
new file mode 100644
index 0000000..0796199
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/RequiresInstanceClassLoading.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a component can use to indicate that the framework should create a new ClassLoader
+ * for each instance of the component, copying all resources from the component's NARClassLoader to a
+ * new ClassLoader which will only be used by a given instance of the component.
+ *
+ * If cloneAncestorResources is set to true, the instance ClassLoader will include ancestor resources up to the
+ * first ClassLoader containing a controller service API referenced by the component, or up to the Jetty NAR.
+ *
+ * Example #1 - PutHDFS has this flag set to true and does not reference any controller services, so it will include
+ * resources from nifi-hadoop-nar, nifi-hadoop-libraries-nar, and nifi-standard-services-api-nar, stopping at nifi-jetty-nar.
+ *
+ * Example #2 - If PutHDFS referenced an SSLContext and has this flag set to true, then it would include
+ * resources from nifi-hadoop-nar, nifi-hadoop-libraries-nar, and stop before nifi-standard-services-api-nar.
+ *
+ * Example #3 - HBaseClientService_1_1_2 does not have this flag set so it defaults to false, and therefore includes
+ * only resources from the nifi-hbase-client-service-1_1_2-nar.
+ *
+ * NOTE: When this annotation is used it is important to note that each added instance of the component will increase
+ * the overall memory footprint more than that of a component without this annotation.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface RequiresInstanceClassLoading {
+
+ boolean cloneAncestorResources() default false;
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java b/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
new file mode 100644
index 0000000..8411c6c
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/Restricted.java
@@ -0,0 +1,63 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marks the usage of a component as restricted to users with elevated privileges.
+ * </p>
+ * <p>
+ * A {@code Restricted} component is one that can be used to execute arbitrary unsanitized
+ * code provided by the operator through the NiFi REST API/UI or can be used to obtain
+ * or alter data on the NiFi host system using the NiFi OS credentials. These components
+ * could be used by an otherwise authorized NiFi user to go beyond the intended use of
+ * the application, escalate privilege, or could expose data about the internals of the
+ * NiFi process or the host system. All of these capabilities should be considered
+ * privileged, and admins should be aware of these capabilities and explicitly enable
+ * them for a subset of trusted users.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Restricted {
+ /**
+ * Provides a description of why the component usage is restricted. If using granular
+ * restrictions, specific explanations should be set in the Restriction.
+ *
+ * @return Description of restriction
+ */
+ String value() default "";
+
+ /**
+ * Provides a listing of specific Restrictions. If unspecified, this component will
+ * require access to restricted components regardless of restrictions.
+ *
+ * @return Array of restrictions
+ */
+ Restriction[] restrictions() default {};
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java b/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java
new file mode 100644
index 0000000..63efc98
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/Restriction.java
@@ -0,0 +1,50 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.RequiredPermission;
+
+/**
+ * Specific restriction for a component. Indicates what the required permission is and why the restriction exists.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Restriction {
+
+ /**
+ * Provides a listing of RequiredPermissions.
+ *
+ * @return Permission required for this restriction
+ */
+ RequiredPermission requiredPermission();
+
+ /**
+ * Provides an explanation of why the component usage is restricted
+ *
+ * @return Explanation for this restriction
+ */
+ String explanation();
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SideEffectFree.java b/src/main/java/org/apache/nifi/annotation/behavior/SideEffectFree.java
new file mode 100644
index 0000000..9a64f4b
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SideEffectFree.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}
+ * implementation can use to indicate that its operations on FlowFiles can be
+ * safely repeated across process sessions. If a processor has this annotation
+ * and it allows the framework to manage session commit and rollback then the
+ * framework may elect to cascade a
+ * {@link org.apache.nifi.processor.ProcessSession ProcessSession} given to this
+ * processor's onTrigger method to the onTrigger method of another processor. It
+ * can do this knowing that if something fails along a series of processors
+ * using this same session that it can all be safely rolled back without any ill
+ * effects on external services which could not be rolled back and thus all the
+ * processes could be safely repeated (implied idempotent behavior).
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SideEffectFree {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/Stateful.java b/src/main/java/org/apache/nifi/annotation/behavior/Stateful.java
new file mode 100644
index 0000000..cfcc820
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/Stateful.java
@@ -0,0 +1,57 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.state.Scope;
+import org.apache.nifi.components.state.StateManager;
+
+/**
+ * <p>
+ * Annotation that a Processor, ReportingTask, FlowAnalysisRule, ParameterProvider, or Controller Service can use to indicate
+ * that the component makes use of the {@link StateManager}. This annotation provides the
+ * user with a description of what information is being stored so that the user is able to
+ * understand what is shown to them and know what they are clearing should they choose to
+ * clear the state. Additionally, the UI will not show any state information to users if
+ * this annotation is not present.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Stateful {
+ /**
+ * Provides a description of what information is being stored in the {@link StateManager}
+ *
+ * @return Description of information stored
+ */
+ String description();
+
+ /**
+ * Indicates the Scope(s) associated with the State that is stored and retrieved.
+ *
+ * @return Array of scopes for information stored
+ */
+ Scope[] scopes();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SupportsBatching.java b/src/main/java/org/apache/nifi/annotation/behavior/SupportsBatching.java
new file mode 100644
index 0000000..49136ad
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SupportsBatching.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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a Processor implementation can use to indicate that users
+ * should be able to supply a Batch Duration for the Processor. If a Processor
+ * uses this annotation, it is allowing the Framework to batch
+ * {@link org.apache.nifi.processor.ProcessSession ProcessSession}s' commits, as well as
+ * allowing the Framework to return the same ProcessSession multiple times from
+ * subsequent calls to
+ * {@link org.apache.nifi.processor.ProcessSessionFactory ProcessSessionFactory}.
+ * {@link org.apache.nifi.processor.ProcessSessionFactory#createSession() createSession()}.
+ *
+ * When this Annotation is used, it is important to note that calls to
+ * {@link org.apache.nifi.processor.ProcessSession#commit() ProcessSession.commit()} may
+ * not provide a guarantee that the data has been safely stored in NiFi's
+ * Content Repository or FlowFile Repository. Therefore, it is not appropriate,
+ * for instance, to use this annotation if the Processor will call
+ * ProcessSession.commit() to ensure data is persisted before deleting the data
+ * from a remote source.
+ *
+ * When the defaultDuration parameter is set, the processor is created with the supplied duration time, which can be adjusted afterwards.
+ * The supplied values can be selected from {@link org.apache.nifi.annotation.behavior.DefaultRunDuration}.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SupportsBatching {
+ DefaultRunDuration defaultDuration() default DefaultRunDuration.NO_BATCHING;
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SupportsSensitiveDynamicProperties.java b/src/main/java/org/apache/nifi/annotation/behavior/SupportsSensitiveDynamicProperties.java
new file mode 100644
index 0000000..9927641
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SupportsSensitiveDynamicProperties.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Component annotation indicating support for dynamic properties that can be designated as sensitive for the purpose of
+ * persistence and framework processing
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SupportsSensitiveDynamicProperties {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SystemResource.java b/src/main/java/org/apache/nifi/annotation/behavior/SystemResource.java
new file mode 100644
index 0000000..cea3c70
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SystemResource.java
@@ -0,0 +1,26 @@
+/*
+ * 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.annotation.behavior;
+
+/**
+ * Represents a system resource.
+ */
+public enum SystemResource {
+
+ CPU, DISK, MEMORY, NETWORK
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsideration.java b/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsideration.java
new file mode 100644
index 0000000..66c8abb
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsideration.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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.components.ConfigurableComponent Component} describes how this component may impact a
+ * system resource based on its configuration.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Repeatable(SystemResourceConsiderations.class)
+public @interface SystemResourceConsideration {
+
+ String DEFAULT_DESCRIPTION = "An instance of this component can cause high usage of this system resource. " +
+ "Multiple instances or high concurrency settings may result a degradation of performance.";
+
+ /**
+ * The {@link SystemResource SystemResource} which may be affected by this component.
+ *
+ * @return System Resource to be considered
+ */
+ SystemResource resource();
+
+ /**
+ * A description of how this component and its configuration may affect system resource usage.
+ *
+ * @return Description of resource consideration
+ */
+ String description() default DEFAULT_DESCRIPTION;
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsiderations.java b/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsiderations.java
new file mode 100644
index 0000000..cd6c8c2
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/SystemResourceConsiderations.java
@@ -0,0 +1,37 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.components.ConfigurableComponent Component} describes how this component may impact
+ * system resources based on its configuration.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SystemResourceConsiderations {
+ SystemResourceConsideration[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/TriggerSerially.java b/src/main/java/org/apache/nifi/annotation/behavior/TriggerSerially.java
new file mode 100644
index 0000000..974e358
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/TriggerSerially.java
@@ -0,0 +1,38 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}
+ * implementation can use to indicate that the Processor is not safe for
+ * concurrent execution of its onTrigger() method. By default, Processors are
+ * assumed to be safe for concurrent execution.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface TriggerSerially {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenAnyDestinationAvailable.java b/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenAnyDestinationAvailable.java
new file mode 100644
index 0000000..db115cb
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenAnyDestinationAvailable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}
+ * implementation can use to indicate that the Processor is to be triggered if
+ * any of its destinations has available space for incoming FlowFiles. By
+ * default, Processors are triggered only when all destinations report that they
+ * have available space (i.e., none of the outgoing Connections is full).
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface TriggerWhenAnyDestinationAvailable {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenEmpty.java b/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenEmpty.java
new file mode 100644
index 0000000..6fac03b
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/TriggerWhenEmpty.java
@@ -0,0 +1,57 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor}
+ * implementation can use to indicate that the Processor should still be
+ * triggered even when it has no data in its work queue.
+ * </p>
+ *
+ * <p>
+ * A Processor is scheduled to be triggered based on its configured Scheduling Period
+ * and Scheduling Strategy. However, when the scheduling period elapses, the Processor
+ * will not be scheduled if it has no work to do. Normally, a Processor is said to have
+ * work to do if one of the following circumstances is true:
+ * </p>
+ *
+ * <ul>
+ * <li>An incoming Connection has data in its queue</li>
+ * <li>The Processor has no incoming Connections.</li>
+ * <li>All incoming Connections are self-loops (both the source and destination of the Connection are the same Processor).
+ * </ul>
+ *
+ * <p>
+ * If the Processor needs to be triggered to run even when the above conditions are all
+ * <code>false</code>, the Processor's class can be annotated with this annotation, which
+ * will cause the Processor to be triggered, even if its incoming queues are empty.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface TriggerWhenEmpty {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/WritesAttribute.java b/src/main/java/org/apache/nifi/annotation/behavior/WritesAttribute.java
new file mode 100644
index 0000000..d06af8f
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/WritesAttribute.java
@@ -0,0 +1,41 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.processor.Processor Processor} indicating that this
+ * processor writes/updates a specific FlowFile attribute.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface WritesAttribute {
+
+ String attribute();
+
+ String description() default "";
+}
diff --git a/src/main/java/org/apache/nifi/annotation/behavior/WritesAttributes.java b/src/main/java/org/apache/nifi/annotation/behavior/WritesAttributes.java
new file mode 100644
index 0000000..603eb86
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/behavior/WritesAttributes.java
@@ -0,0 +1,39 @@
+/*
+ * 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.annotation.behavior;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a
+ * {@link org.apache.nifi.processor.Processor Processor} indicating that this
+ * processor writes/updates specific FlowFile attributes.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface WritesAttributes {
+
+ WritesAttribute[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/configuration/DefaultSchedule.java b/src/main/java/org/apache/nifi/annotation/configuration/DefaultSchedule.java
new file mode 100644
index 0000000..f1a145a
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/configuration/DefaultSchedule.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation.configuration;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.scheduling.SchedulingStrategy;
+
+/**
+ * <p>
+ * Marker interface that a Processor can use to configure default settings for the schedule strategy, the period and the number of concurrent tasks.
+ * Marker interface that a ReportingTask can use to configure default settings the schedule strategy and the period.
+ * Note that the number of Concurrent tasks will be ignored if the annotation @TriggerSerialy is used
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DefaultSchedule {
+
+ SchedulingStrategy strategy() default SchedulingStrategy.TIMER_DRIVEN;
+ String period() default "0 sec";
+ int concurrentTasks() default 1;
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/configuration/DefaultSettings.java b/src/main/java/org/apache/nifi/annotation/configuration/DefaultSettings.java
new file mode 100644
index 0000000..09402c7
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/configuration/DefaultSettings.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.annotation.configuration;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.logging.LogLevel;
+
+/**
+ * <p>
+ * Marker interface that a Processor can use to configure the yield duration, the penalty duration and the bulletin log level.
+ * Note that the number of Concurrent tasks will be ignored if the annotation @TriggerSerialy is used
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface DefaultSettings {
+ String yieldDuration() default "1 sec";
+ String penaltyDuration() default "30 sec";
+ LogLevel bulletinLevel() default LogLevel.WARN;
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/CapabilityDescription.java b/src/main/java/org/apache/nifi/annotation/documentation/CapabilityDescription.java
new file mode 100644
index 0000000..5ac0e97
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/CapabilityDescription.java
@@ -0,0 +1,43 @@
+/*
+ * 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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that may be placed on a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider},
+ * {@link org.apache.nifi.flowanalysis.FlowAnalysisRule FlowAnalysisRule}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} allowing for a
+ * description to be provided. This description can be provided to a user in
+ * logs, UI, etc.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface CapabilityDescription {
+
+ String value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/DeprecationNotice.java b/src/main/java/org/apache/nifi/annotation/documentation/DeprecationNotice.java
new file mode 100644
index 0000000..e385843
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/DeprecationNotice.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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.ConfigurableComponent;
+
+/**
+ * Annotation that can be applied to a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} in order to
+ * warn about the deprecation of the component. The deprecation warning is informational only
+ * and doesn't affect the processor run time behavior in any way
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+
+public @interface DeprecationNotice {
+ Class<? extends ConfigurableComponent>[] alternatives() default {};
+
+ String[] classNames() default {};
+
+ String reason() default "";
+}
+
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCase.java b/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCase.java
new file mode 100644
index 0000000..562647e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCase.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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * An annotation that can be used for Processors in order to explain a specific use case that can be
+ * accomplished using this Processor in conjunction with at least one other Processor.
+ * For Processors that are able to be used for multiple use cases, the component
+ * may be annotated with multiple MultiProcessorUseCase annotations.
+ * </p>
+ * <p>
+ * Note that this annotation differs from {@link UseCase} in that UseCase should describe a use case that is
+ * accomplished using only the extension that is annotated. In contrast, MultiProcessorUseCase documents a use case
+ * that is accomplished by using both the Processor that is annotated as well as other Processors.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Repeatable(MultiProcessorUseCases.class)
+public @interface MultiProcessorUseCase {
+
+ /**
+ * A simple 1 (at most 2) sentence description of the use case. This should not include any extraneous details, such
+ * as caveats, examples, etc. Those can be provided using the {@link #notes()} method.
+ *
+ * @return a simple description of the use case
+ */
+ String description();
+
+ /**
+ * Most of the time, 1-2 sentences is sufficient to describe a use case. Those 1-2 sentence should then be returned
+ * by the {@link #description()}. In the event that the description is not sufficient, details may be provided to
+ * further explain, by providing examples, caveats, etc.
+ *
+ * @return any important notes that pertain to the use case
+ */
+ String notes() default "";
+
+ /**
+ * An optional array of keywords that can be associated with the use case.
+ * @return keywords associated with the use case
+ */
+ String[] keywords() default {};
+
+ /**
+ * An array of {@link ProcessorConfiguration}s that are necessary in order to accomplish the task described in this use case.
+ * @return an array of processor configurations
+ */
+ ProcessorConfiguration[] configurations();
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCases.java b/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCases.java
new file mode 100644
index 0000000..a7ae09b
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/MultiProcessorUseCases.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An enclosing annotation that can be used in order to use the {@link MultiProcessorUseCase} annotation in a repeated manner.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface MultiProcessorUseCases {
+ MultiProcessorUseCase[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/ProcessorConfiguration.java b/src/main/java/org/apache/nifi/annotation/documentation/ProcessorConfiguration.java
new file mode 100644
index 0000000..ec2ab1f
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/ProcessorConfiguration.java
@@ -0,0 +1,57 @@
+/*
+ * 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.annotation.documentation;
+
+
+import org.apache.nifi.processor.Processor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that can be used in conjunction with {@link MultiProcessorUseCase} in order to specify the different
+ * components that are involved in a given use case.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ProcessorConfiguration {
+
+ /**
+ * Returns the class of the Processor that is to be used in the use case, if it is provided. Either the
+ * Processor Class or the Processor Class Name must be provided.
+ *
+ * @return the Processor's class, or <code>Processor</code> if the processor's classname is specified instead
+ */
+ Class<? extends Processor> processorClass() default Processor.class;
+
+ /**
+ * @return the fully qualified classname of the component
+ */
+ String processorClassName() default "";
+
+ /**
+ * @return an explanation of how the Processor should be configured.
+ */
+ String configuration();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/SeeAlso.java b/src/main/java/org/apache/nifi/annotation/documentation/SeeAlso.java
new file mode 100644
index 0000000..5eae159
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/SeeAlso.java
@@ -0,0 +1,45 @@
+/*
+ * 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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.components.ConfigurableComponent;
+
+/**
+ * Annotation that may be placed on a null {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider},
+ * {@link org.apache.nifi.flowanalysis.FlowAnalysisRule FlowAnalysisRule}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} that indicates
+ * this component is related to the components listed.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface SeeAlso {
+
+ Class<? extends ConfigurableComponent>[] value() default {};
+
+ String[] classNames() default {};
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/Tags.java b/src/main/java/org/apache/nifi/annotation/documentation/Tags.java
new file mode 100644
index 0000000..a22cb28
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/Tags.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation that can be applied to a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider},
+ * {@link org.apache.nifi.flowanalysis.FlowAnalysisRule FlowAnalysisRule}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} in order to
+ * associate tags (keywords) with the component. These tags do not affect the
+ * component in any way but serve as additional documentation and can be used to
+ * sort/filter Processors.
+ *
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface Tags {
+
+ String[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/UseCase.java b/src/main/java/org/apache/nifi/annotation/documentation/UseCase.java
new file mode 100644
index 0000000..478ba1e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/UseCase.java
@@ -0,0 +1,89 @@
+/*
+ * 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.annotation.documentation;
+
+import org.apache.nifi.annotation.behavior.InputRequirement;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * An annotation that can be used for extension points in order to explain a specific use case that can be
+ * accomplished using this extension. For components that are able to be used for multiple use cases, the component
+ * may be annotated with multiple UseCase annotations.
+ * </p>
+ * <p>
+ * Note that this annotation differs from {@link CapabilityDescription} in that CapabilityDescription should describe the
+ * general purpose of the extension point. UseCase, on the other hand, documents one very specific use case that
+ * can be accomplished. Some extension points may use only a single UseCase annotation while others may accomplish
+ * many use cases.
+ * </p>
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Repeatable(UseCases.class)
+public @interface UseCase {
+
+ /**
+ * A simple 1 (at most 2) sentence description of the use case. This should not include any extraneous details, such
+ * as caveats, examples, etc. Those can be provided using the {@link #notes()} method.
+ *
+ * @return a simple description of the use case
+ */
+ String description();
+
+ /**
+ * Most of the time, 1-2 sentences is sufficient to describe a use case. Those 1-2 sentence should then be returned
+ * by the {@link #description()}. In the event that the description is not sufficient, details may be provided to
+ * further explain, by providing examples, caveats, etc.
+ *
+ * @return any important notes that pertain to the use case
+ */
+ String notes() default "";
+
+ /**
+ * Most Processors specify an InputRequirement of either {@link InputRequirement.Requirement#INPUT_REQUIRED INPUT_REQUIRED}
+ * or {@link InputRequirement.Requirement#INPUT_FORBIDDEN}. However, some Processors use {@link InputRequirement.Requirement#INPUT_ALLOWED}
+ * because some use cases require input while others do not. The inputRequirement here is only relevant for Processors that use
+ * an InputRequirement of {@link InputRequirement.Requirement#INPUT_ALLOWED} and can indicate whether or not the Processor should have
+ * input (aka incoming Connections) for this particular use case.
+ *
+ * @return the {@link InputRequirement} that corresponds to this use case.
+ */
+ InputRequirement.Requirement inputRequirement() default InputRequirement.Requirement.INPUT_ALLOWED;
+
+ /**
+ * An optional array of keywords that can be associated with the use case.
+ * @return keywords associated with the use case
+ */
+ String[] keywords() default {};
+
+ /**
+ * A description of how to configure the extension for this particular use case.
+ * @return a description of how to configure the extension for this particular use case.
+ */
+ String configuration() default "";
+}
diff --git a/src/main/java/org/apache/nifi/annotation/documentation/UseCases.java b/src/main/java/org/apache/nifi/annotation/documentation/UseCases.java
new file mode 100644
index 0000000..f64c8e5
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/documentation/UseCases.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation.documentation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An enclosing annotation that can be used in order to use the {@link UseCase} annotation in a repeated manner.
+ */
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface UseCases {
+ UseCase[] value();
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.java
new file mode 100644
index 0000000..4933a38
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnAdded.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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.registry.flow.FlowRegistryClient FlowRegistryClient},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider},
+ * {@link org.apache.nifi.flowanalysis.FlowAnalysisRule FlowAnalysisRule}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation
+ * can use to indicate a method should be called whenever the component is added
+ * to the flow. This method will be called once for the entire life of a
+ * component instance.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation are called without any arguments, as all
+ * settings and properties can be assumed to be the defaults.
+ * </p>
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnAdded {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnConfigurationRestored.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnConfigurationRestored.java
new file mode 100644
index 0000000..72c5bd3
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnConfigurationRestored.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.annotation.lifecycle;
+
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker Annotation that a Processor, Reporting Task, or Controller Service can use to indicate
+ * that the method with this Annotation should be invoked whenever the component's configuration
+ * is restored after a restart of NiFi.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation are permitted to take no arguments or to take a
+ * single argument. If using a single argument, that argument must be of type
+ * {@link ConfigurationContext} if the component is a ReportingTask or a
+ * ControllerService. If the component is a Processor, then the argument must be
+ * of type {@link ProcessContext}.
+ * </p>
+ *
+ * <p>
+ * Whenever a new component is added to the flow, this method will be called immediately, since
+ * there is no configuration to restore (in this case all configuration has already been restored,
+ * since there is no configuration to restore).
+ * </p>
+ *
+ * @since 0.5.0
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnConfigurationRestored {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.java
new file mode 100644
index 0000000..c8d77e3
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnDisabled.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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.controller.ConfigurationContext;
+
+/**
+ * <p>
+ * Marker annotation a
+ * {@link org.apache.nifi.controller.ControllerService ControllerService} can
+ * use to indicate a method should be called whenever the service is disabled.
+ * </p>
+ *
+ * <p>
+ * Methods using this annotation are permitted to take zero arguments or to take
+ * a single argument of type {@link ConfigurationContext}. If a method with this
+ * annotation throws a Throwable, a log message and bulletin will be issued for
+ * the service, but the service will still be marked as Disabled. The failing
+ * method will not be called again until the service is enabled and disabled again.
+ * This is done in order to prevent a ControllerService from continually failing
+ * in such a way that the service could not be disabled and updated without
+ * restarting the instance of NiFi.
+ * </p>
+ *
+ * <p>
+ * Note that this annotation will be ignored if applied to a ReportingTask, ParameterProvider or
+ * Processor. For a Controller Service, enabling and disabling are considered
+ * lifecycle events, as the action makes them usable or unusable by other
+ * components. However, for a Processor and a Reporting Task, these are not
+ * lifecycle events but rather a mechanism to allow a component to be excluded
+ * when starting or stopping a group of components.
+ * </p>
+ *
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnDisabled {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
new file mode 100644
index 0000000..28132c8
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnEnabled.java
@@ -0,0 +1,69 @@
+/*
+ * 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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation a
+ * {@link org.apache.nifi.controller.ControllerService ControllerService} can
+ * use to indicate a method should be called whenever the service is enabled.
+ * Any method that has this annotation will be called every time a user enables
+ * the service. Additionally, each time that NiFi is restarted, if NiFi is
+ * configured to "auto-resume state" and the service is enabled, the method will
+ * be invoked.
+ * </p>
+ *
+ * <p>
+ * Methods using this annotation must take either 0 arguments or a single
+ * argument of type
+ * {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
+ * </p>
+ *
+ * <p>
+ * If a method with this annotation throws a Throwable, a log message and
+ * bulletin will be issued for the component. In this event, the service will
+ * remain in an 'ENABLING' state and will not be usable. All methods with this
+ * annotation will then be called again after a delay. The service will not be
+ * made available for use until all methods with this annotation have returned
+ * without throwing anything.
+ * </p>
+ *
+ * <p>
+ * Note that this annotation will be ignored if applied to a ReportingTask, ParameterProvider or
+ * Processor. For a Controller Service, enabling and disabling are considered
+ * lifecycle events, as the action makes them usable or unusable by other
+ * components. However, for a Processor and a Reporting Task, these are not
+ * lifecycle events but rather a mechanism to allow a component to be excluded
+ * when starting or stopping a group of components.
+ * </p>
+ *
+ *
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnEnabled {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
new file mode 100644
index 0000000..172979e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnRemoved.java
@@ -0,0 +1,57 @@
+/*
+ * 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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.registry.flow.FlowRegistryClient FlowRegistryClient},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider},
+ * {@link org.apache.nifi.flowanalysis.FlowAnalysisRule FlowAnalysisRule}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation
+ * can use to indicate a method should be called whenever the component is
+ * removed from the flow. This method will be called once for the entire life of
+ * a component instance. If the method throw any Throwable, that Throwable will
+ * be caught and logged but will not prevent subsequent methods with this
+ * annotation or removal of the component from the flow.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation are permitted to take no arguments or to take a
+ * single argument. If using a single argument, that argument must be of type
+ * {@link ConfigurationContext} if the component is a ReportingTask, a ParameterProvider, or a
+ * ControllerService. If the component is a Processor, then the argument must be
+ * of type {@link ProcessContext}.
+ * </p>
+ *
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnRemoved {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnScheduled.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnScheduled.java
new file mode 100644
index 0000000..b320858
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnScheduled.java
@@ -0,0 +1,78 @@
+/*
+ * 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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor} or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation
+ * can use to indicate a method should be called whenever the component is
+ * scheduled to run. This will be called before any call to 'onTrigger' and will
+ * be called once each time a Processor or Reporting Task is scheduled to run.
+ * This occurs in one of two ways: either a user clicks to schedule the
+ * component to run, or NiFi is restarted with the "auto-resume state"
+ * configuration set to true (the default value) and the component is already
+ * running.
+ * </p>
+ *
+ * <p>
+ * Methods using this annotation must take either 0 arguments or a single
+ * argument.
+ * </p>
+ *
+ * <p>
+ * If using 1 argument and the component using the annotation is a Processor,
+ * that argument must be of type
+ * {@link org.apache.nifi.processor.ProcessContext ProcessContext}.
+ * </p>
+ *
+ * <p>
+ * If using 1 argument and the component using the annotation is a Reporting
+ * Task, that argument must be of type
+ * {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
+ * </p>
+ *
+ * <p>
+ * If any method annotated with this annotation throws any Throwable, the
+ * framework will wait a while and then attempt to invoke the method again. This
+ * will continue until the method succeeds, and the component will then be
+ * scheduled to run after this method return successfully.
+ * </p>
+ *
+ * <p><b>Implementation Guidelines:</b>
+ * <ul>
+ * <li>Methods with this annotation are expected to perform very quick, short-lived tasks. If the function is
+ * expensive or long-lived, the logic should be performed in the {@code onTrigger} method instead.</li>
+ * <li>If a method with this annotation does not return (exceptionally or otherwise) within a short period
+ * of time (the duration is configurable in the properties file), the Thread may be interrupted.</li>
+ * <li>Methods that make use of this interface should honor Java's Thread interruption mechanisms and not swallow
+ * {@link InterruptedException}.</li>
+ * </ul>
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnScheduled {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.java
new file mode 100644
index 0000000..88a3ec3
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnShutdown.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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor},
+ * {@link org.apache.nifi.controller.ControllerService ControllerService},
+ * {@link org.apache.nifi.parameter.ParameterProvider ParameterProvider}, or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation
+ * can use to indicate a method should be called whenever the flow is being
+ * shutdown. This will be called at most once for each component in a JVM
+ * lifetime. It is not, however, guaranteed that this method will be called on
+ * shutdown, as the service may be killed suddenly.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation are permitted to take either 0 or 1 argument. If
+ * an argument is used, it must be of type {@link ConfigurationContext} if the
+ * component is a ReportingTask or Controller Service, or of type
+ * {@link ProcessContext} if the component is a Processor.
+ * </p>
+ *
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnShutdown {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
new file mode 100644
index 0000000..647c2ec
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnStopped.java
@@ -0,0 +1,75 @@
+/*
+ * 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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.nifi.controller.ConfigurationContext;
+import org.apache.nifi.processor.ProcessContext;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor} or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} implementation
+ * can use to indicate that a method should be called whenever the component is
+ * no longer scheduled to run. Methods marked with this annotation will be
+ * invoked each time the component is stopped and will be invoked only after the
+ * last thread has returned from the <code>onTrigger</code> method.
+ * </p>
+ *
+ * <p>
+ * This means that the thread executing in this method will be the only thread
+ * executing in any part of the Processor. However, since other threads may
+ * later execute other parts of the code, member variables must still be
+ * protected appropriately. However, access to multiple variables need not be
+ * atomic.
+ * </p>
+ *
+ * <p>
+ * To indicate that a method should be called immediately when a component is no
+ * longer scheduled to run (as opposed to after all threads have returned from
+ * the <code>onTrigger</code> method), see the {@link OnUnscheduled} annotation.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation are permitted to take either 0 or 1 argument. If
+ * an argument is used, it must be of type {@link ConfigurationContext} if the
+ * component is a ReportingTask or of type {@link ProcessContext} if the
+ * component is a Processor.
+ * </p>
+ *
+ * <p><b>Implementation Guidelines:</b>
+ * <ul>
+ * <li>Methods with this annotation are expected to perform very quick, short-lived tasks. If the function is
+ * expensive or long-lived, the logic should be performed in the {@code onTrigger} method instead.</li>
+ * <li>If a method with this annotation does not return (exceptionally or otherwise) within a short period
+ * of time (the duration is configurable in the properties file), the Thread may be interrupted.</li>
+ * <li>Methods that make use of this interface should honor Java's Thread interruption mechanisms and not swallow
+ * {@link InterruptedException}.</li>
+ * </ul>
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnStopped {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java b/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
new file mode 100644
index 0000000..ea041ae
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/lifecycle/OnUnscheduled.java
@@ -0,0 +1,69 @@
+/*
+ * 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.annotation.lifecycle;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation a {@link org.apache.nifi.processor.Processor Processor} or
+ * {@link org.apache.nifi.reporting.ReportingTask ReportingTask} should be
+ * called whenever the component is no longer scheduled to run. Methods marked
+ * with this annotation will be invoked each time the framework is notified to
+ * stop scheduling the component. This method is invoked as other threads are
+ * potentially running. To invoke a method after all threads have finished
+ * processing, see the {@link OnStopped} annotation.
+ * </p>
+ *
+ * <p>
+ * Methods using this annotation must take either 0 arguments or a single
+ * argument.
+ * </p>
+ *
+ * <p>
+ * If using 1 argument and the component using the annotation is a Processor,
+ * that argument must be of type
+ * {@link org.apache.nifi.processor.ProcessContext ProcessContext}.
+ * </p>
+ *
+ * <p>
+ * If using 1 argument and the component using the annotation is a Reporting
+ * Task, that argument must be of type
+ * {@link org.apache.nifi.controller.ConfigurationContext ConfigurationContext}.
+ * </p>
+ *
+ * <p><b>Implementation Guidelines:</b>
+ * <ul>
+ * <li>Methods with this annotation are expected to perform very quick, short-lived tasks. If the function is
+ * expensive or long-lived, the logic should be performed in the {@code onTrigger} method instead.</li>
+ * <li>If a method with this annotation does not return (exceptionally or otherwise) within a short period
+ * of time (the duration is configurable in the properties file), the Thread may be interrupted.</li>
+ * <li>Methods that make use of this interface should honor Java's Thread interruption mechanisms and not swallow
+ * {@link InterruptedException}.</li>
+ * </ul>
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnUnscheduled {
+}
diff --git a/src/main/java/org/apache/nifi/annotation/notification/OnPrimaryNodeStateChange.java b/src/main/java/org/apache/nifi/annotation/notification/OnPrimaryNodeStateChange.java
new file mode 100644
index 0000000..e3ff401
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/notification/OnPrimaryNodeStateChange.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.annotation.notification;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * Marker annotation that a component can use to indicate that a method should be
+ * called whenever the state of the Primary Node in a cluster has changed.
+ * </p>
+ *
+ * <p>
+ * Methods with this annotation should take either no arguments or one argument of type
+ * {@link PrimaryNodeState}. The {@link PrimaryNodeState} provides context about what changed
+ * so that the component can take appropriate action.
+ * </p>
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface OnPrimaryNodeStateChange {
+
+}
diff --git a/src/main/java/org/apache/nifi/annotation/notification/PrimaryNodeState.java b/src/main/java/org/apache/nifi/annotation/notification/PrimaryNodeState.java
new file mode 100644
index 0000000..539e5f0
--- /dev/null
+++ b/src/main/java/org/apache/nifi/annotation/notification/PrimaryNodeState.java
@@ -0,0 +1,33 @@
+/*
+ * 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.annotation.notification;
+
+/**
+ * Represents a state change that occurred for the Primary Node of a NiFi cluster.
+ */
+public enum PrimaryNodeState {
+ /**
+ * The node receiving this state has been elected the Primary Node of the NiFi cluster.
+ */
+ ELECTED_PRIMARY_NODE,
+
+ /**
+ * The node receiving this state was the Primary Node but has now had its Primary Node
+ * role revoked.
+ */
+ PRIMARY_NODE_REVOKED;
+}
diff --git a/src/main/java/org/apache/nifi/asset/Asset.java b/src/main/java/org/apache/nifi/asset/Asset.java
new file mode 100644
index 0000000..3e8c026
--- /dev/null
+++ b/src/main/java/org/apache/nifi/asset/Asset.java
@@ -0,0 +1,64 @@
+/*
+ * 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.asset;
+
+import java.io.File;
+import java.util.Optional;
+
+/**
+ * An Asset is a representation of some resource that is necessary in order to run a dataflow.
+ * An Asset is always accessed as a local file.
+ */
+public interface Asset {
+
+ /**
+ * Returns a unique identifier for the Asset
+ *
+ * @return Asset Identifier
+ */
+ String getIdentifier();
+
+ /**
+ * Returns the identifier of the parameter context the Asset belongs to
+ *
+ * @return Parameter Context Identifier
+ */
+ String getParameterContextIdentifier();
+
+ /**
+ * Returns the name of the Asset
+ *
+ * @return Asset Name
+ */
+ String getName();
+
+ /**
+ * Returns the local file that the Asset is associated with
+ *
+ * @return Asset File
+ */
+ File getFile();
+
+ /**
+ * Returns the digest of the contents of the local file that the Asset is associated with.
+ * The digest will not be present when the asset is considered missing and the local file does not exist.
+ *
+ * @return Asset Digest or empty when not found
+ */
+ Optional<String> getDigest();
+}
diff --git a/src/main/java/org/apache/nifi/components/AbstractConfigurableComponent.java b/src/main/java/org/apache/nifi/components/AbstractConfigurableComponent.java
new file mode 100644
index 0000000..2b0a061
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/AbstractConfigurableComponent.java
@@ -0,0 +1,261 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public abstract class AbstractConfigurableComponent implements ConfigurableComponent {
+
+ /**
+ * Allows subclasses to perform their own validation on the already set
+ * properties. Since each property is validated as it is set this allows
+ * validation of groups of properties together. Default return is an empty
+ * set.
+ *
+ * This method will be called only when it has been determined that all
+ * property values are valid according to their corresponding
+ * PropertyDescriptor's validators.
+ *
+ * @param validationContext provides a mechanism for obtaining externally
+ * managed values, such as property values and supplies convenience methods
+ * for operating on those values
+ *
+ * @return Collection of ValidationResult objects that will be added to any
+ * other validation findings - may be null
+ */
+ protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
+ return Collections.emptySet();
+ }
+
+ /**
+ * @param descriptorName to lookup the descriptor
+ * @return a PropertyDescriptor for the name specified that is fully
+ * populated
+ */
+ @Override
+ public final PropertyDescriptor getPropertyDescriptor(final String descriptorName) {
+ final PropertyDescriptor specDescriptor = new PropertyDescriptor.Builder().name(descriptorName).build();
+ return getPropertyDescriptor(specDescriptor);
+ }
+
+ private PropertyDescriptor getPropertyDescriptor(final PropertyDescriptor specDescriptor) {
+ //check if property supported
+ PropertyDescriptor descriptor = getSupportedPropertyDescriptor(specDescriptor);
+ if (descriptor != null) {
+ return descriptor;
+ }
+
+ descriptor = getSupportedDynamicPropertyDescriptor(specDescriptor.getName());
+ if (descriptor != null && !descriptor.isDynamic()) {
+ descriptor = new PropertyDescriptor.Builder().fromPropertyDescriptor(descriptor).dynamic(true).build();
+ }
+
+ if (descriptor == null) {
+ descriptor = new PropertyDescriptor.Builder().fromPropertyDescriptor(specDescriptor).addValidator(Validator.INVALID).dynamic(true).build();
+ }
+ return descriptor;
+ }
+
+ private PropertyDescriptor getSupportedPropertyDescriptor(final PropertyDescriptor specDescriptor) {
+ final List<PropertyDescriptor> supportedDescriptors = getSupportedPropertyDescriptors();
+ if (supportedDescriptors != null) {
+ for (final PropertyDescriptor desc : supportedDescriptors) { //find actual descriptor
+ if (specDescriptor.equals(desc)) {
+ return desc;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public final Collection<ValidationResult> validate(final ValidationContext context) {
+ // goes through context properties, should match supported properties + supported dynamic properties
+ final Collection<ValidationResult> results = new ArrayList<>();
+ final Set<PropertyDescriptor> contextDescriptors = context.getProperties().keySet();
+
+ for (final PropertyDescriptor descriptor : contextDescriptors) {
+ // If the property descriptor's dependency is not satisfied, the property does not need to be considered, as it's not relevant to the
+ // component's functionality.
+ final boolean dependencySatisfied = context.isDependencySatisfied(descriptor, this::getPropertyDescriptor);
+ if (!dependencySatisfied) {
+ continue;
+ }
+
+ validateDependencies(descriptor, context, results);
+
+ String value = context.getProperty(descriptor).getValue();
+ if (value == null) {
+ value = descriptor.getDefaultValue();
+ }
+
+ if (value == null && descriptor.isRequired()) {
+ String displayName = descriptor.getDisplayName();
+ ValidationResult.Builder builder = new ValidationResult.Builder().valid(false).input(null).subject(displayName != null ? displayName : descriptor.getName());
+ builder = (displayName != null) ? builder.explanation(displayName + " is required") : builder.explanation(descriptor.getName() + " is required");
+ results.add(builder.build());
+ continue;
+ } else if (value == null) {
+ continue;
+ }
+
+ final ValidationResult result = descriptor.validate(value, context);
+ if (!result.isValid()) {
+ results.add(result);
+ }
+ }
+
+ // only run customValidate if regular validation is successful. This allows Processor developers to not have to check
+ // if values are null or invalid so that they can focus only on the interaction between the properties, etc.
+ if (results.isEmpty()) {
+ final Collection<ValidationResult> customResults = customValidate(context);
+ if (null != customResults) {
+ for (final ValidationResult result : customResults) {
+ if (!result.isValid()) {
+ results.add(result);
+ }
+ }
+ }
+ }
+
+ return results;
+ }
+
+ private void validateDependencies(final PropertyDescriptor descriptor, final ValidationContext context, final Collection<ValidationResult> results) {
+ // Ensure that we don't have any dependencies on non-existent properties.
+ final Set<PropertyDependency> dependencies = descriptor.getDependencies();
+ for (final PropertyDependency dependency : dependencies) {
+ final String dependentPropertyName = dependency.getPropertyName();
+
+ // If there's a supported property descriptor then all is okay.
+ final PropertyDescriptor specDescriptor = new PropertyDescriptor.Builder().name(dependentPropertyName).build();
+ final PropertyDescriptor supportedDescriptor = getSupportedPropertyDescriptor(specDescriptor);
+ if (supportedDescriptor != null) {
+ continue;
+ }
+
+ final PropertyDescriptor dynamicPropertyDescriptor = getSupportedDynamicPropertyDescriptor(dependentPropertyName);
+ if (dynamicPropertyDescriptor == null) {
+ results.add(new ValidationResult.Builder()
+ .subject(descriptor.getDisplayName())
+ .valid(false)
+ .explanation("Property depends on property " + dependentPropertyName + ", which is not a known property")
+ .build());
+ }
+
+ // Dependent property is supported as a dynamic property. This is okay as long as there is a value set.
+ final PropertyValue value = context.getProperty(dynamicPropertyDescriptor);
+ if (value == null || !value.isSet()) {
+ results.add(new ValidationResult.Builder()
+ .subject(descriptor.getDisplayName())
+ .valid(false)
+ .explanation("Property depends on property " + dependentPropertyName + ", which is not a known property")
+ .build());
+ }
+ }
+
+ }
+
+ /**
+ * Hook method allowing subclasses to eagerly react to a configuration
+ * change for the given property descriptor. As an alternative to using this
+ * method a processor may simply get the latest value whenever it needs it
+ * and if necessary lazily evaluate it.
+ *
+ * @param descriptor of the modified property
+ * @param oldValue non-null property value (previous)
+ * @param newValue the new property value or if null indicates the property
+ * was removed
+ */
+ @Override
+ public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
+ }
+
+ /**
+ * <p>
+ * Used to allow subclasses to determine what PropertyDescriptor if any to
+ * use when a property is requested for which a descriptor is not already
+ * registered. By default this method simply returns a null descriptor. By
+ * overriding this method processor implementations can support dynamic
+ * properties since this allows them to register properties on demand. It is
+ * acceptable for a dynamically generated property to indicate it is
+ * required so long as it is understood it is only required once set.
+ * Dynamic properties by definition cannot be required until used.</p>
+ *
+ * <p>
+ * This method should be side effect free in the subclasses in terms of how
+ * often it is called for a given property name because there is guarantees
+ * how often it will be called for a given property name.</p>
+ *
+ * <p>
+ * Default is null.
+ *
+ * @param propertyDescriptorName used to lookup if any property descriptors exist for that name
+ * @return new property descriptor if supported
+ */
+ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
+ return null;
+ }
+
+ /**
+ * Allows subclasses to register which property descriptor objects are
+ * supported. Default return is an empty set.
+ *
+ * @return PropertyDescriptor objects this processor currently supports
+ */
+ protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public final List<PropertyDescriptor> getPropertyDescriptors() {
+ final List<PropertyDescriptor> supported = getSupportedPropertyDescriptors();
+ return supported == null ? Collections.emptyList() : Collections.unmodifiableList(supported);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ConfigurableComponent)) {
+ return false;
+ }
+
+ final ConfigurableComponent other = (ConfigurableComponent) obj;
+ return getIdentifier().equals(other.getIdentifier());
+ }
+
+ @Override
+ public int hashCode() {
+ return 235 + getIdentifier().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[id=" + getIdentifier() + "]";
+ }
+
+}
diff --git a/src/main/java/org/apache/nifi/components/AllowableValue.java b/src/main/java/org/apache/nifi/components/AllowableValue.java
new file mode 100644
index 0000000..9cae438
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/AllowableValue.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.components;
+
+import java.util.Objects;
+
+/**
+ * <p>
+ * Represents a valid value for a {@link PropertyDescriptor}
+ * </p>
+ */
+public class AllowableValue implements DescribedValue {
+
+ private final String value;
+ private final String displayName;
+ private final String description;
+
+ /**
+ * Constructs a new AllowableValue with the given value and and the same
+ * display name and no description.
+ *
+ * @param value that is allowed
+ */
+ public AllowableValue(final String value) {
+ this(value, value);
+ }
+
+ /**
+ * Constructs a new AllowableValue with the given value and display name and
+ * no description
+ *
+ * @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) {
+ this(value, displayName, null);
+ }
+
+ /**
+ * Constructs a new AllowableValue with the given value, display name, and
+ * description
+ *
+ * @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) {
+ this.value = Objects.requireNonNull(value);
+ this.displayName = Objects.requireNonNull(displayName);
+ 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
+ */
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * @return a human-readable name for this AllowableValue
+ */
+ @Override
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * @return a description for this value, or <code>null</code> if no
+ * description was provided
+ */
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return true if <code>this</code> is equal to <code>obj</code> of <code>obj</code> is the
+ * same object as <code>this</code> or if <code>obj</code> is an instance of
+ * <code>AllowableValue</code> and both have the same value, or if
+ * <code>obj</code> is a String and is equal to
+ * {@link #getValue() this.getValue()}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj instanceof AllowableValue other) {
+ return this.value.equals(other.getValue());
+ } else if (obj instanceof String) {
+ return this.value.equals(obj);
+ }
+
+ return false;
+ }
+
+ /**
+ * @return based solely off of the value
+ */
+ @Override
+ public int hashCode() {
+ return 23984731 + 17 * value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/ClassloaderIsolationKeyProvider.java b/src/main/java/org/apache/nifi/components/ClassloaderIsolationKeyProvider.java
new file mode 100644
index 0000000..9d36193
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/ClassloaderIsolationKeyProvider.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import org.apache.nifi.context.PropertyContext;
+
+/**
+ * <p>
+ * There are times when a component must be created in such a way that each instance gets its own ClassLoader hierarchy,
+ * rather than sharing the ClassLoader with other components (see {@link org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading @RequiresInstanceClassLoading}).
+ * This, however, can be extremely expensive, as all of the classes must be loaded again for each instance of the component. When thousands of these
+ * components are used in a single flow, the startup time can be great, and it can lead to massive amounts of RAM being required.
+ * </p>
+ *
+ * <p>
+ * For components that do require instance ClassLoading that clones ancestor resources, this interface can be optional implemented by the component.
+ * If the interface is implemented, the component is given the opportunity to return a distinct "key" that can be used to identify instances that may share
+ * the same ClassLoader.
+ * </p>
+ */
+public interface ClassloaderIsolationKeyProvider {
+
+ /**
+ * <p>
+ * Determines the key that identifies a shared ClassLoader that this component may use. Any two instances of the same component that return
+ * the same key may be assigned the same base ClassLoader (though it is not guaranteed that this will be the case).
+ * </p>
+ *
+ * <p>
+ * If a subsequent call to this method returns a different value, the component will be recreated with a different ClassLoader.
+ * </p>
+ *
+ * <p>
+ * Implementation Note: for components that implement this interface, this method will be called often. Therefore, performance characteristics
+ * of the implementation are critical. The method is expected to return the value of a configured property or derive a value to return based off
+ * of the values of a few properties. Accessing a remote resource, is too expensive. If the necessary computation is non-trivial, then it should be
+ * performed out of band and the pre-computed value simply returned by this method.
+ * </p>
+ *
+ * @param context the PropertyContext that can be used for determining the key
+ * @return a distinct key that can be used to indicate which shared ClassLoader is allowed to be used
+ */
+ String getClassloaderIsolationKey(PropertyContext context);
+
+}
diff --git a/src/main/java/org/apache/nifi/components/ConfigVerificationResult.java b/src/main/java/org/apache/nifi/components/ConfigVerificationResult.java
new file mode 100644
index 0000000..9f2ab77
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/ConfigVerificationResult.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.components;
+
+public class ConfigVerificationResult {
+ private final Outcome outcome;
+ private final String verificationStepName;
+ private final String explanation;
+
+ private ConfigVerificationResult(final Builder builder) {
+ outcome = builder.outcome;
+ verificationStepName = builder.verificationStepName;
+ explanation = builder.explanation;
+ }
+
+ public Outcome getOutcome() {
+ return outcome;
+ }
+
+ public String getVerificationStepName() {
+ return verificationStepName;
+ }
+
+ public String getExplanation() {
+ return explanation;
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigVerificationResult[" +
+ "outcome=" + outcome +
+ ", verificationStepName='" + verificationStepName + "'" +
+ ", explanation='" + explanation + "']";
+ }
+
+ public static class Builder {
+ private Outcome outcome = Outcome.SKIPPED;
+ private String verificationStepName = "Unknown Step Name";
+ private String explanation;
+
+ public Builder outcome(final Outcome outcome) {
+ this.outcome = outcome;
+ return this;
+ }
+
+ public Builder verificationStepName(final String verificationStepName) {
+ this.verificationStepName = verificationStepName;
+ return this;
+ }
+
+ public Builder explanation(final String explanation) {
+ this.explanation = explanation;
+ return this;
+ }
+
+ public ConfigVerificationResult build() {
+ return new ConfigVerificationResult(this);
+ }
+ }
+
+ public enum Outcome {
+ SUCCESSFUL,
+
+ FAILED,
+
+ SKIPPED;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/ConfigurableComponent.java b/src/main/java/org/apache/nifi/components/ConfigurableComponent.java
new file mode 100644
index 0000000..4c5537e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/ConfigurableComponent.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.components;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.nifi.annotation.lifecycle.OnConfigurationRestored;
+
+public interface ConfigurableComponent {
+
+ /**
+ * Validates a set of properties, returning ValidationResults for any
+ * invalid properties. All defined properties will be validated. If they are
+ * not included in the purposed configuration, the default value will
+ * be used.
+ *
+ * @param context of validation
+ * @return Collection of validation result objects for any invalid findings
+ * only. If the collection is empty then the component is valid. Guaranteed
+ * non-null
+ */
+ Collection<ValidationResult> validate(ValidationContext context);
+
+ /**
+ * @param name to lookup the descriptor
+ * @return the PropertyDescriptor with the given name, if it exists;
+ * otherwise, returns <code>null</code>
+ */
+ PropertyDescriptor getPropertyDescriptor(String name);
+
+ /**
+ * Hook method allowing subclasses to eagerly react to a configuration
+ * change for the given property descriptor. This method will be invoked
+ * regardless of property validity. As an alternative to using this method,
+ * a component may simply get the latest value whenever it needs it and if
+ * necessary lazily evaluate it. Any throwable that escapes this method will
+ * simply be ignored.
+ *
+ * When NiFi is restarted, this method will be called for each 'dynamic' property that is
+ * added, as well as for each property that is not set to the default value. I.e., if the
+ * Properties are modified from the default values. If it is undesirable for your use case
+ * to react to properties being modified in this situation, you can add the {@link OnConfigurationRestored}
+ * annotation to a method - this will allow the Processor to know when configuration has
+ * been restored, so that it can determine whether or not to perform some action in the
+ * onPropertyModified method.
+ *
+ * @param descriptor the descriptor for the property being modified
+ * @param oldValue the value that was previously set, or null if no value
+ * was previously set for this property
+ * @param newValue the new property value or if null indicates the property
+ * was removed
+ */
+ void onPropertyModified(PropertyDescriptor descriptor, String oldValue, String newValue);
+
+ /**
+ * Returns a {@link List} of all {@link PropertyDescriptor}s that this
+ * component supports.
+ *
+ * @return PropertyDescriptor objects this component currently supports
+ */
+ List<PropertyDescriptor> getPropertyDescriptors();
+
+ /**
+ * @return the unique identifier that the framework assigned to this
+ * component
+ */
+ String getIdentifier();
+
+}
diff --git a/src/main/java/org/apache/nifi/components/DescribedValue.java b/src/main/java/org/apache/nifi/components/DescribedValue.java
new file mode 100644
index 0000000..87ffc2f
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/DescribedValue.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+/**
+ * Describes a component property value with display name and description.
+ */
+public interface DescribedValue {
+
+ /**
+ * @return the property value as a string
+ */
+ String getValue();
+
+ /**
+ * @return the property display name as a string
+ */
+ String getDisplayName();
+
+ /**
+ * @return the proeprty description as a string
+ */
+ String getDescription();
+}
diff --git a/src/main/java/org/apache/nifi/components/PortFunction.java b/src/main/java/org/apache/nifi/components/PortFunction.java
new file mode 100644
index 0000000..a746eb3
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/PortFunction.java
@@ -0,0 +1,31 @@
+/*
+ * 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 PortFunction {
+ /**
+ * Port functions as a standard port, transferring FlowFiles to all outgoing connections.
+ */
+ STANDARD,
+
+ /**
+ * Port denotes that the invocation of the stateless flow has failed. If run using the Standard Engine, will operate as a Standard port. If running
+ * using the Stateless Engine, the transaction is rolled back, and any input FlowFiles will be transferred to this Port.
+ */
+ FAILURE;
+}
diff --git a/src/main/java/org/apache/nifi/components/PropertyDependency.java b/src/main/java/org/apache/nifi/components/PropertyDependency.java
new file mode 100644
index 0000000..275248e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/PropertyDependency.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+public class PropertyDependency {
+ private final String propertyName;
+ private final String displayName;
+ private final Set<String> dependentValues;
+
+ /**
+ * Creates a dependency that is satisfied if any value is set for the property with the given name
+ * @param propertyName the name of the property that is depended upon
+ * @param propertyDisplayName the display name of the property that is depended upon
+ */
+ public PropertyDependency(final String propertyName, final String propertyDisplayName) {
+ this.propertyName = Objects.requireNonNull(propertyName);
+ this.displayName = propertyDisplayName == null ? propertyName : propertyDisplayName;
+ this.dependentValues = null;
+ }
+
+ /**
+ * Creates a dependency that is satisfied only if the property with the given name has a value that is in the given set of dependent values
+ * @param propertyName the name of the property that is depended upon
+ * @param propertyDisplayName the display name of the property that is depended upon
+ * @param dependentValues the values that satisfy the dependency
+ */
+ public PropertyDependency(final String propertyName, final String propertyDisplayName, final Set<String> dependentValues) {
+ this.propertyName = Objects.requireNonNull(propertyName);
+ this.displayName = propertyDisplayName == null ? propertyName : propertyDisplayName;
+ this.dependentValues = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(dependentValues)));
+ }
+
+ /**
+ * @return the name of the property that is depended upon
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * @return the display name of the property that is depended upon
+ */
+ public String getPropertyDisplayName() {
+ return displayName;
+ }
+
+ /**
+ * @return the Set of values that satisfy the dependency
+ */
+ public Set<String> getDependentValues() {
+ return dependentValues;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyDependency[propertyName=" + propertyName + ", displayName=" + displayName + ", dependentValues=" + dependentValues + "]";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final PropertyDependency that = (PropertyDependency) o;
+ return Objects.equals(getPropertyName(), that.getPropertyName())
+ && Objects.equals(getDependentValues(), that.getDependentValues());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getPropertyName(), getDependentValues());
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/PropertyDescriptor.java b/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
new file mode 100644
index 0000000..8f61545
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/PropertyDescriptor.java
@@ -0,0 +1,911 @@
+/*
+ * 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;
+
+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.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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * An immutable object for holding information about a type of component
+ * property.
+ */
+public final class PropertyDescriptor implements Comparable<PropertyDescriptor> {
+
+ public static final PropertyDescriptor NULL_DESCRIPTOR = new PropertyDescriptor.Builder().name("").build();
+
+ /**
+ * The proper name for the property. This is the primary mechanism of
+ * comparing equality.
+ */
+ private final String name;
+
+ /**
+ * The name that should be displayed to user when referencing this property
+ */
+ private final String displayName;
+
+ /**
+ * And explanation of the meaning of the given property. This description is
+ * meant to be displayed to a user or simply provide a mechanism of
+ * documenting intent.
+ */
+ private final String description;
+ /**
+ * The default value for this property
+ */
+ private final String defaultValue;
+ /**
+ * The allowable values for this property. If empty then the allowable
+ * values are not constrained
+ */
+ private final List<AllowableValue> allowableValues;
+ /**
+ * Determines whether the property is required for this processor
+ */
+ private final boolean required;
+ /**
+ * indicates that the value for this property should be considered sensitive
+ * and protected whenever stored or represented
+ */
+ private final boolean sensitive;
+ /**
+ * indicates whether this property well-known for this processor or is
+ * user-defined
+ */
+ private final boolean dynamic;
+ /**
+ * indicates whether or nor this property will evaluate expression language
+ * against the flow file attributes
+ */
+ private final ExpressionLanguageScope expressionLanguageScope;
+ /**
+ * indicates whether or not this property represents resources that should be added
+ * to the classpath and used for loading native libraries for this instance of the component
+ */
+ private final boolean dynamicallyModifiesClasspath;
+
+ /**
+ * the interface of the {@link ControllerService} that this Property refers
+ * to; will be null if not identifying a ControllerService.
+ */
+ private final Class<? extends ControllerService> controllerServiceDefinition;
+
+ /**
+ * The validators that should be used whenever an attempt is made to set
+ * this property value. Any allowable values specified will be checked first
+ * and any validators specified will be ignored.
+ */
+ private final List<Validator> validators;
+
+ /**
+ * The list of dependencies that this property has on other properties
+ */
+ private final Set<PropertyDependency> dependencies;
+
+ /**
+ * The definition of the resource(s) that this property references
+ */
+ private final ResourceDefinition resourceDefinition;
+
+ protected PropertyDescriptor(final Builder builder) {
+ this.displayName = builder.displayName == null ? builder.name : builder.displayName;
+ this.name = builder.name;
+ this.description = builder.description;
+ this.defaultValue = builder.defaultValue;
+ 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 = List.copyOf(builder.validators);
+ this.dependencies = builder.dependencies == null ? Collections.emptySet() : Set.copyOf(builder.dependencies);
+ this.resourceDefinition = builder.resourceDefinition;
+ }
+
+ @Override
+ public int compareTo(final PropertyDescriptor o) {
+ if (o == null) {
+ return -1;
+ }
+ return getName().compareTo(o.getName());
+ }
+
+ /**
+ * Validates the given input against this property descriptor's validator.
+ * 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 context the context of validation
+ * @return the result of validating the input
+ */
+ public ValidationResult validate(final String input, final ValidationContext context) {
+ ValidationResult lastResult = Validator.INVALID.validate(this.name, input, context);
+
+ if (allowableValues != null && !allowableValues.isEmpty()) {
+ final ConstrainedSetValidator csValidator = new ConstrainedSetValidator(allowableValues);
+ final ValidationResult csResult = csValidator.validate(this.name, input, context);
+
+ if (csResult.isValid()) {
+ lastResult = csResult;
+ } else {
+ return csResult;
+ }
+ }
+
+ final ResourceDefinition resourceDefinition = getResourceDefinition();
+ if (resourceDefinition != null) {
+ final Validator validator = new ResourceDefinitionValidator(resourceDefinition, this.expressionLanguageScope);
+ final ValidationResult result = validator.validate(this.name, input, context);
+ if (!result.isValid()) {
+ return result;
+ }
+
+ lastResult = result;
+ }
+
+ for (final Validator validator : validators) {
+ lastResult = validator.validate(this.name, input, context);
+ if (!lastResult.isValid()) {
+ break;
+ }
+ }
+
+ if (getControllerServiceDefinition() != null) {
+ 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();
+ } else {
+ return new ValidationResult.Builder()
+ .valid(true)
+ .build();
+ }
+ }
+
+ return lastResult;
+ }
+
+ public static final class Builder {
+
+ private String displayName = null;
+ private String name = null;
+ private String description = "";
+ private String defaultValue = null;
+ private List<AllowableValue> allowableValues = null;
+ private Set<PropertyDependency> dependencies = null;
+ private boolean required = false;
+ private boolean sensitive = false;
+ private ExpressionLanguageScope expressionLanguageScope = ExpressionLanguageScope.NONE;
+ private boolean dynamic = false;
+ private boolean dynamicallyModifiesClasspath = false;
+ private Class<? extends ControllerService> controllerServiceDefinition;
+ private ResourceDefinition resourceDefinition;
+ private List<Validator> validators = new ArrayList<>();
+
+ public Builder fromPropertyDescriptor(final PropertyDescriptor specDescriptor) {
+ this.name = specDescriptor.name;
+ this.displayName = specDescriptor.displayName;
+ this.description = specDescriptor.description;
+ this.defaultValue = specDescriptor.defaultValue;
+ this.allowableValues = specDescriptor.allowableValues == null ? null : new ArrayList<>(specDescriptor.allowableValues);
+ this.required = specDescriptor.required;
+ this.sensitive = specDescriptor.sensitive;
+ this.dynamic = specDescriptor.dynamic;
+ this.dynamicallyModifiesClasspath = specDescriptor.dynamicallyModifiesClasspath;
+ this.expressionLanguageScope = specDescriptor.expressionLanguageScope;
+ this.controllerServiceDefinition = specDescriptor.getControllerServiceDefinition();
+ this.validators = new ArrayList<>(specDescriptor.validators);
+ this.dependencies = new HashSet<>(specDescriptor.dependencies);
+ this.resourceDefinition = specDescriptor.resourceDefinition;
+ return this;
+ }
+
+ /**
+ * Sets a unique id for the property. This field is optional and if not
+ * specified the PropertyDescriptor's name will be used as the
+ * identifying attribute. However, by supplying an id, the
+ * PropertyDescriptor's name can be changed without causing problems.
+ * This is beneficial because it allows a User Interface to represent
+ * the name differently.
+ *
+ * @param displayName of the property
+ * @return the builder
+ */
+ public Builder displayName(final String displayName) {
+ if (null != displayName) {
+ this.displayName = displayName;
+ }
+
+ return this;
+ }
+
+ /**
+ * Sets the property name.
+ *
+ * @param name of the property
+ * @return the builder
+ */
+ public Builder name(final String name) {
+ if (null != name) {
+ this.name = name;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the scope of the expression language evaluation
+ *
+ * @param expressionLanguageScope scope of the expression language evaluation
+ * @return the builder
+ */
+ public Builder expressionLanguageSupported(final ExpressionLanguageScope expressionLanguageScope) {
+ this.expressionLanguageScope = expressionLanguageScope;
+ return this;
+ }
+
+ /**
+ * @param description of the property
+ * @return the builder
+ */
+ public Builder description(final String description) {
+ if (null != description) {
+ this.description = description;
+ }
+ return this;
+ }
+
+ /**
+ * Specifies the initial value and the default value that will be used if the user does not specify a value.
+ * <p>
+ * 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
+ */
+ public Builder defaultValue(final String value) {
+ if (null != value) {
+ this.defaultValue = value;
+ }
+ return this;
+ }
+
+ /**
+ * Specifies the initial value and the default value that will be used if the user does not specify a value.
+ * <p>
+ * 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
+ */
+ 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;
+ }
+
+ /**
+ * Specifies that the value of this property represents one or more resources that the
+ * framework should add to the classpath of as well as consider when looking for native
+ * libraries for the given component.
+ * <p>
+ * 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.<br/>
+ * It also allows to load native libraries from the extra classpath.
+ * <p>
+ * 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).
+ * <p>
+ * 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.
+ *
+ * @param dynamicallyModifiesClasspath whether or not this property should be used by the framework to modify the classpath
+ * @return the builder
+ */
+ public Builder dynamicallyModifiesClasspath(final boolean dynamicallyModifiesClasspath) {
+ this.dynamicallyModifiesClasspath = dynamicallyModifiesClasspath;
+ return this;
+ }
+
+ /**
+ * Sets the Allowable Values for this Property.
+ *
+ * @param values constrained set of values
+ * @return the builder
+ */
+ public Builder allowableValues(final Set<String> values) {
+ if (null != values) {
+ this.allowableValues = values.stream().map(AllowableValue::new).toList();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the Allowable Values for this Property.
+ * <p>
+ * 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 <E> Type of Allowable Value
+ * @param values constrained set of values
+ * @return the builder
+ */
+ public <E extends Enum<E>> Builder allowableValues(final E[] values) {
+ if (null != values) {
+ this.allowableValues = Arrays.stream(values)
+ .map(enumValue -> enumValue instanceof DescribedValue describedValue
+ ? AllowableValue.fromDescribedValue(describedValue) : new AllowableValue(enumValue.name()))
+ .toList();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the Allowable Values for this Property.
+ * <p>
+ * 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 <E> generic parameter for an enum class, that may implement the DescribedValue interface
+ * @return the builder
+ */
+ public <E extends Enum<E>> Builder allowableValues(final Class<E> enumClass) {
+ return allowableValues(enumClass.getEnumConstants());
+ }
+
+ /**
+ * Sets the Allowable Values for this Property.
+ * <p>
+ * 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 <E> generic parameter for an enum class, that may implement the DescribedValue interface
+ * @return the builder
+ */
+ public <E extends Enum<E>> Builder allowableValues(final EnumSet<E> 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 = Arrays.stream(values).map(AllowableValue::new).toList();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the Allowable Values for this Property.
+ * <p>
+ * 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 allowableValues(final DescribedValue... values) {
+ if (null != values) {
+ this.allowableValues = Arrays.stream(values).map(AllowableValue::fromDescribedValue).toList();
+ }
+ return this;
+ }
+
+ /**
+ * Clears all Allowable Values from this Property
+ *
+ * @return the builder
+ */
+ public Builder clearAllowableValues() {
+ this.allowableValues = null;
+ return this;
+ }
+
+ /**
+ * @param required true if yes; false otherwise
+ * @return the builder
+ */
+ public Builder required(final boolean required) {
+ this.required = required;
+ return this;
+ }
+
+ /**
+ * @param sensitive true if sensitive; false otherwise
+ * @return the builder
+ */
+ public Builder sensitive(final boolean sensitive) {
+ this.sensitive = sensitive;
+ return this;
+ }
+
+ /**
+ * @param validator for the property
+ * @return the builder
+ */
+ public Builder addValidator(final Validator validator) {
+ if (validator != null) {
+ validators.add(validator);
+ }
+ return this;
+ }
+
+ /**
+ * Clear all Validators from this Property
+ *
+ * @return the builder
+ */
+ public Builder clearValidators() {
+ validators.clear();
+ return this;
+ }
+
+ /**
+ * Specifies that this property provides the identifier of a Controller
+ * Service that implements the given interface
+ *
+ * @param controllerServiceDefinition the interface that is implemented
+ * by the Controller Service
+ * @return the builder
+ */
+ public Builder identifiesControllerService(final Class<? extends ControllerService> controllerServiceDefinition) {
+ if (controllerServiceDefinition != null) {
+ this.controllerServiceDefinition = controllerServiceDefinition;
+ }
+ return this;
+ }
+
+ private boolean isValueAllowed(final String value) {
+ if (allowableValues == null || value == null) {
+ return true;
+ }
+
+ for (final AllowableValue allowableValue : allowableValues) {
+ if (allowableValue.getValue().equals(value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Specifies that this property references one or more resources that are external to NiFi that the component is meant to consume.
+ * Any property descriptor that identifies an external resource will be automatically validated against the following rules:
+ * <ul>
+ * <li>If the ResourceCardinality is SINGLE, the given property value must be a file, a directory, or a URL that uses a protocol of http/https/file.</li>
+ * <li>The given resourceTypes dictate which types of input are allowed. For example, if <code>identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)</code>
+ * is used, the input must be a regular file. If <code>identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)</code> is used, then the input
+ * must be exactly one file OR directory.
+ * </li>
+ * <li>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.</li>
+ * </ul>
+ * <p>
+ * 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 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
+ */
+ public Builder identifiesExternalResource(final ResourceCardinality cardinality, final ResourceType resourceType, final ResourceType... additionalResourceTypes) {
+ Objects.requireNonNull(cardinality);
+ Objects.requireNonNull(resourceType);
+
+ final Set<ResourceType> resourceTypes = new HashSet<>();
+ resourceTypes.add(resourceType);
+ resourceTypes.addAll(Arrays.asList(additionalResourceTypes));
+
+ this.resourceDefinition = new StandardResourceDefinition(cardinality, resourceTypes);
+ return this;
+ }
+
+ /**
+ * Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a non-null value.
+ * Furthermore, if one or more explicit Allowable Values are provided, this Property will not be relevant unless the given Property's value is equal to one of the given Allowable Values.
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 dependentValues the possible values for the given property for which this Property is relevant
+ * @return the builder
+ */
+ public Builder dependsOn(final PropertyDescriptor property, final AllowableValue... dependentValues) {
+ if (dependencies == null) {
+ dependencies = new HashSet<>();
+ }
+
+ if (dependentValues.length == 0) {
+ dependencies.add(new PropertyDependency(property.getName(), property.getDisplayName()));
+ } else {
+ final Set<String> dependentValueSet = new HashSet<>();
+ for (final AllowableValue value : dependentValues) {
+ dependentValueSet.add(value.getValue());
+ }
+
+ dependencies.add(new PropertyDependency(property.getName(), property.getDisplayName(), dependentValueSet));
+ }
+
+ return this;
+ }
+
+ /**
+ * Establishes a relationship between this Property and the given property by declaring that this Property is only relevant if the given Property has a value equal to one of the given
+ * <code>String</code> 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 additionalDependentValues any other values for the given property for which this Property is relevant
+ * @return the builder
+ */
+ public Builder dependsOn(final PropertyDescriptor property, final String firstDependentValue, final String... additionalDependentValues) {
+ final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1];
+ dependentValues[0] = new AllowableValue(firstDependentValue);
+ int i = 1;
+ for (final String additionalDependentValue : additionalDependentValues) {
+ dependentValues[i++] = new AllowableValue(additionalDependentValue);
+ }
+
+ return dependsOn(property, dependentValues);
+ }
+
+ /**
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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 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] = AllowableValue.fromDescribedValue(firstDependentValue);
+ int i = 1;
+ for (final DescribedValue additionalDependentValue : additionalDependentValues) {
+ dependentValues[i++] = AllowableValue.fromDescribedValue(additionalDependentValue);
+ }
+
+ return dependsOn(property, dependentValues);
+ }
+
+ /**
+ * Clear all Dependencies from this Property
+ *
+ * @return the builder
+ */
+ public Builder clearDependsOn() {
+ this.dependencies = new HashSet<>();
+ return this;
+ }
+
+ /**
+ * @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.
+ */
+ public PropertyDescriptor build() {
+ if (name == null) {
+ throw new IllegalStateException("Must specify a name");
+ }
+ if (!isValueAllowed(defaultValue)) {
+ throw new IllegalStateException("Default value [" + defaultValue + "] is not in the set of allowable values");
+ }
+
+ return new PropertyDescriptor(this);
+ }
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public boolean isSensitive() {
+ return sensitive;
+ }
+
+ public boolean isDynamic() {
+ return dynamic;
+ }
+
+ public boolean isExpressionLanguageSupported() {
+ return !expressionLanguageScope.equals(ExpressionLanguageScope.NONE);
+ }
+
+ public ExpressionLanguageScope getExpressionLanguageScope() {
+ return expressionLanguageScope;
+ }
+
+ public boolean isDynamicClasspathModifier() {
+ return dynamicallyModifiesClasspath;
+ }
+
+ public Class<? extends ControllerService> getControllerServiceDefinition() {
+ return controllerServiceDefinition;
+ }
+
+ public List<Validator> getValidators() {
+ return validators;
+ }
+
+ public List<AllowableValue> getAllowableValues() {
+ return allowableValues;
+ }
+
+ public Set<PropertyDependency> getDependencies() {
+ return dependencies;
+ }
+
+ public ResourceDefinition getResourceDefinition() {
+ return resourceDefinition;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof PropertyDescriptor otherPropertyDescriptor) {
+ return this.name.equals(otherPropertyDescriptor.name);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return 287 + this.name.hashCode() * 47;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[" + displayName + "]";
+ }
+
+ 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_DELIMITER = ", ";
+ private final String validStrings;
+ private final Collection<String> validValues;
+
+ /**
+ * Constructs a validator that will check if the given value is in the
+ * given set.
+ *
+ * @param validValues values which are acceptible
+ * @throws NullPointerException if the given validValues is null
+ */
+ private ConstrainedSetValidator(final Collection<AllowableValue> validValues) {
+ this.validValues = validValues.stream().map(AllowableValue::getValue).toList();
+ this.validStrings = String.join(VALUE_DELIMITER, this.validValues);
+ }
+
+ @Override
+ public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+ final ValidationResult.Builder builder = new ValidationResult.Builder();
+ builder.input(input);
+ builder.subject(subject);
+ if (validValues.contains(input)) {
+ builder.valid(true);
+ builder.explanation(POSITIVE_EXPLANATION);
+ } else {
+ builder.valid(false);
+ builder.explanation(String.format(NEGATIVE_EXPLANATION, validStrings));
+ }
+ return builder.build();
+ }
+ }
+
+ private static class ResourceDefinitionValidator implements Validator {
+ private final ResourceDefinition resourceDefinition;
+ private final ExpressionLanguageScope expressionLanguageScope;
+
+ public ResourceDefinitionValidator(final ResourceDefinition resourceDefinition, final ExpressionLanguageScope expressionLanguageScope) {
+ this.resourceDefinition = resourceDefinition;
+ this.expressionLanguageScope = expressionLanguageScope;
+ }
+
+ @Override
+ public ValidationResult validate(final String subject, final String configuredInput, final ValidationContext context) {
+ final ValidationResult.Builder resultBuilder = new ValidationResult.Builder()
+ .input(configuredInput)
+ .subject(subject);
+
+ if (configuredInput == null) {
+ return resultBuilder.valid(false)
+ .explanation("No value specified")
+ .build();
+ }
+
+ // If Expression Language is supported and is used in the property value, we cannot perform validation against the configured
+ // input unless the Expression Language is expressly limited to only env/syst properties variables. In that case, we can evaluate
+ // it and then validate the value after evaluating the Expression Language.
+ String input = configuredInput;
+ if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(configuredInput)) {
+ if (expressionLanguageScope != null && expressionLanguageScope == ExpressionLanguageScope.ENVIRONMENT) {
+ input = context.newPropertyValue(configuredInput).evaluateAttributeExpressions().getValue();
+ resultBuilder.input(input);
+ } else {
+ return resultBuilder.valid(true)
+ .explanation("Expression Language is present, so validation of property value cannot be performed")
+ .build();
+ }
+ }
+
+ // If the property can be text, then there's nothing to validate. Anything that is entered may be valid.
+ // This will be improved in the future, by allowing the user to specify the type of resource that is being referenced.
+ // Until then, we will simply require that the component perform any necessary validation.
+ 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();
+ }
+
+ 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();
+ }
+
+ final Set<ResourceType> resourceTypes = resourceDefinition.getResourceTypes();
+ final List<String> nonExistentResources = new ArrayList<>();
+
+ int count = 0;
+ for (final String split : splits) {
+ final ResourceReference resourceReference = new StandardResourceReferenceFactory().createResourceReference(split, resourceDefinition);
+ if (resourceReference == null) {
+ continue;
+ }
+
+ count++;
+
+ final boolean accessible = resourceReference.isAccessible();
+ if (!accessible) {
+ nonExistentResources.add(resourceReference.getLocation());
+ continue;
+ }
+
+ 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();
+ }
+ }
+
+ if (count == 0) {
+ return resultBuilder.valid(false)
+ .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();
+ }
+
+ return resultBuilder.valid(true)
+ .build();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/PropertyValue.java b/src/main/java/org/apache/nifi/components/PropertyValue.java
new file mode 100644
index 0000000..4e52c54
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/PropertyValue.java
@@ -0,0 +1,371 @@
+/*
+ * 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;
+
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.apache.nifi.components.resource.ResourceReference;
+import org.apache.nifi.components.resource.ResourceReferences;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.expression.AttributeValueDecorator;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.DataUnit;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.registry.EnvironmentVariables;
+
+/**
+ * <p>
+ * A PropertyValue provides a mechanism whereby the currently configured value
+ * of a processor property can be obtained in different forms.
+ * </p>
+ */
+public interface PropertyValue {
+
+ /**
+ * @return the raw property value as a string
+ */
+ String getValue();
+
+ /**
+ * @return an integer representation of the property value, or
+ * <code>null</code> if not set
+ * @throws NumberFormatException if not able to parse
+ */
+ Integer asInteger();
+
+ /**
+ * @return a Long representation of the property value, or <code>null</code>
+ * if not set
+ * @throws NumberFormatException if not able to parse
+ */
+ Long asLong();
+
+ /**
+ * @return a Boolean representation of the property value, or
+ * <code>null</code> if not set
+ */
+ Boolean asBoolean();
+
+ /**
+ * @return a Float representation of the property value, or
+ * <code>null</code> if not set
+ * @throws NumberFormatException if not able to parse
+ */
+ Float asFloat();
+
+ /**
+ * @return a Double representation of the property value, of
+ * <code>null</code> if not set
+ * @throws NumberFormatException if not able to parse
+ */
+ Double asDouble();
+
+ /**
+ * @param timeUnit specifies the TimeUnit to convert the time duration into
+ * @return a Long value representing the value of the configured time period
+ * in terms of the specified TimeUnit; if the property is not set, returns
+ * <code>null</code>
+ */
+ Long asTimePeriod(TimeUnit timeUnit);
+
+ /**
+ * Returns the value as a Duration
+ *
+ * @return a Duration representing the value, or <code>null</code> if the value is unset
+ */
+ Duration asDuration();
+
+ /**
+ *
+ * @param dataUnit specifies the DataUnit to convert the data size into
+ * @return a Long value representing the value of the configured data size
+ * in terms of the specified DataUnit; if hte property is not set, returns
+ * <code>null</code>
+ */
+ Double asDataSize(DataUnit dataUnit);
+
+ /**
+ * @return the ControllerService whose identifier is the raw value of
+ * <code>this</code>, or <code>null</code> if either the value is not set or
+ * the value does not identify a ControllerService
+ */
+ ControllerService asControllerService();
+
+ /**
+ * @param <T> the generic type of the controller service
+ * @param serviceType the class of the Controller Service
+ * @return the ControllerService whose identifier is the raw value of the
+ * <code>this</code>, or <code>null</code> if either the value is not set or
+ * the value does not identify a ControllerService. The object returned by
+ * this method is explicitly cast to type specified, if the type specified
+ * is valid. Otherwise, throws an IllegalArgumentException
+ *
+ * @throws IllegalArgumentException if the value of <code>this</code> points
+ * to a ControllerService but that service is not of type
+ * <code>serviceType</code> or if <code>serviceType</code> references a
+ * class that is not an interface
+ */
+ <T extends ControllerService> T asControllerService(Class<T> serviceType) throws IllegalArgumentException;
+
+ /**
+ * @return a ResourceReference for the configured property value, or <code>null</code> if no value was specified, or if the property references multiple resources.
+ * @see #asResources()
+ */
+ ResourceReference asResource();
+
+ /**
+ * @return a ResourceReferences for the configured property value. If no property value is set, a ResourceReferences will be returned that references no resources.
+ * I.e., this method will never return <code>null</code>.
+ */
+ ResourceReferences asResources();
+
+ /**
+ * Returns the property value as one of the configured allowableValues, see {@link PropertyDescriptor.Builder#allowableValues(Class)}
+ * <p>
+ * The {@link Class#getEnumConstants()} of the provided enum are searched for an entry matching the value of <code>this</code>.
+ * In case an enum value is a {@link DescribedValue}, uses the defined {@link DescribedValue#getValue()} for comparison.
+ * Otherwise, the {@link Enum#name()} is used.
+ *
+ * @param <E> 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 <code>null</code> if the value is not set.
+ * @throws IllegalArgumentException if no enum entry matching the value of <code>this</code> is found.
+ */
+ <E extends Enum<E>> E asAllowableValue(Class<E> enumType) throws IllegalArgumentException;
+
+ /**
+ * @return <code>true</code> if the user has configured a value, or if the
+ * {@link PropertyDescriptor} for the associated property has a default
+ * value, <code>false</code> otherwise
+ */
+ boolean isSet();
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language;
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @return a PropertyValue with the new value is returned, supporting call
+ * chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions() throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language;
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param attributes a Map of attributes that the Expression can reference.
+ * These will take precedence over any underlying env/syst properties values.
+ *
+ * @return a PropertyValue with the new value
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(Map<String, String> attributes) throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language.
+ * The supplied decorator is then given a chance to decorate the value, and
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param attributes a Map of attributes that the Expression can reference.
+ * These will take precedence over any variables in any underlying variable
+ * registry.
+ * @param decorator the decorator to use in order to update the values
+ * returned after variable substitution and expression language evaluation.
+ *
+ * @return a PropertyValue with the new value
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(Map<String, String> attributes, AttributeValueDecorator decorator) throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language;
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param flowFile to evaluate attributes of. It's flow file properties and
+ * then flow file attributes will take precedence over any underlying
+ * env/syst properties.
+ * @return a PropertyValue with the new value is returned, supporting call
+ * chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(FlowFile flowFile) throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language;
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param flowFile to evaluate attributes of. It's flow file properties and
+ * then flow file attributes will take precedence over any underlying
+ * env/syst properties.
+ * @param additionalAttributes a Map of additional attributes that the
+ * Expression can reference. These attributes will take precedence over any
+ * conflicting attributes in the provided flowfile or any underlying
+ * env/syst properties.
+ *
+ * @return a PropertyValue with the new value is returned, supporting call
+ * chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes) throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language;
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param flowFile to evaluate attributes of. It's flow file properties and
+ * then flow file attributes will take precedence over any underlying
+ * env/syst properties.
+ * @param additionalAttributes a Map of additional attributes that the
+ * Expression can reference. These attributes will take precedence over any
+ * conflicting attributes in the provided flowfile or any underlying
+ * env/syst properties.
+ * @param decorator the decorator to use in order to update the values
+ * returned after variable substitution and expression language evaluation.
+ *
+ * @return a PropertyValue with the new value is returned, supporting call
+ * chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator) throws ProcessException;
+
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression
+ * Language; a PropertyValue with the new value is then returned, supporting
+ * call chaining.
+ * </p>
+ *
+ * @param flowFile to evaluate attributes of
+ * @param additionalAttributes a Map of additional attributes that the Expression can reference. If entries in
+ * this Map conflict with entries in the FlowFile's attributes, the entries in this Map are given a higher priority.
+ * @param decorator the decorator to use in order to update the values returned by the Expression Language
+ * @param stateValues a Map of the state values to be referenced explicitly by specific state accessing functions
+ *
+ * @return a PropertyValue with the new value is returned, supporting call
+ * chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or evaluating
+ * the Expression against the given attributes causes an Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(FlowFile flowFile, Map<String, String> additionalAttributes, AttributeValueDecorator decorator, Map<String, String> stateValues)
+ throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language.
+ * The supplied decorator is then given a chance to decorate the value, and
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param decorator the decorator to use in order to update the values
+ * returned after variable substitution and expression language evaluation.
+ * @return a PropertyValue with the new value is then returned, supporting
+ * call chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(AttributeValueDecorator decorator) throws ProcessException;
+
+ /**
+ * <p>
+ * Replaces values in the Property Value using the NiFi Expression Language.
+ * The supplied decorator is then given a chance to decorate the value, and
+ * a PropertyValue with the new value is then returned, supporting call
+ * chaining. Before executing the expression language statement any
+ * variables names found within any underlying {@link EnvironmentVariables} will
+ * be substituted with their values.
+ * </p>
+ *
+ * @param flowFile to evaluate expressions against
+ * @param decorator the decorator to use in order to update the values
+ * returned after variable substitution and expression language evaluation.
+ *
+ *
+ * @return a PropertyValue with the new value is then returned, supporting
+ * call chaining
+ *
+ * @throws ProcessException if the Expression cannot be compiled or
+ * evaluating the Expression against the given attributes causes an
+ * Exception to be thrown
+ */
+ PropertyValue evaluateAttributeExpressions(FlowFile flowFile, AttributeValueDecorator decorator) throws ProcessException;
+
+ /**
+ * <p>
+ * Indicates whether the value of the property uses Expression Language.
+ * </p>
+ *
+ * @return <code>true</code> if the property value makes use of the Expression Language, <code>false</code> otherwise.
+ */
+ boolean isExpressionLanguagePresent();
+}
diff --git a/src/main/java/org/apache/nifi/components/RequiredPermission.java b/src/main/java/org/apache/nifi/components/RequiredPermission.java
new file mode 100644
index 0000000..81c9ded
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/RequiredPermission.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+/**
+ * Enumeration of permission required for annotated components
+ */
+public enum RequiredPermission {
+ READ_FILESYSTEM("read-filesystem", "read filesystem"),
+ WRITE_FILESYSTEM("write-filesystem", "write filesystem"),
+ READ_DISTRIBUTED_FILESYSTEM("read-distributed-filesystem", "read distributed filesystem"),
+ WRITE_DISTRIBUTED_FILESYSTEM("write-distributed-filesystem", "write distributed filesystem"),
+ EXECUTE_CODE("execute-code", "execute code"),
+ ACCESS_KEYTAB("access-keytab", "access keytab"),
+ ACCESS_TICKET_CACHE("access-ticket-cache", "access ticket cache"),
+ ACCESS_ENVIRONMENT_CREDENTIALS("access-environment-credentials", "access environment credentials"),
+ EXPORT_NIFI_DETAILS("export-nifi-details", "export nifi details"),
+ REFERENCE_REMOTE_RESOURCES("reference-remote-resources", "reference remote resources");
+
+ private String permissionIdentifier;
+ private String permissionLabel;
+
+ RequiredPermission(String permissionIdentifier, String permissionLabel) {
+ this.permissionIdentifier = permissionIdentifier;
+ this.permissionLabel = permissionLabel;
+ }
+
+ public String getPermissionIdentifier() {
+ return permissionIdentifier;
+ }
+
+ public String getPermissionLabel() {
+ return permissionLabel;
+ }
+
+ public static RequiredPermission valueOfPermissionIdentifier(final String permissionIdentifier) {
+ return Arrays.stream(values())
+ .filter(candidate -> candidate.getPermissionIdentifier().equals(permissionIdentifier))
+ .findFirst().orElse(null);
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/ValidationContext.java b/src/main/java/org/apache/nifi/components/ValidationContext.java
new file mode 100644
index 0000000..031d39a
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/ValidationContext.java
@@ -0,0 +1,203 @@
+/*
+ * 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;
+
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.context.PropertyContext;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.expression.ExpressionLanguageCompiler;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+public interface ValidationContext extends PropertyContext {
+
+ /**
+ * @return the {@link ControllerServiceLookup} which can be used to obtain
+ * Controller Services
+ */
+ ControllerServiceLookup getControllerServiceLookup();
+
+ /**
+ * @param controllerService to lookup the validation context of
+ * @return a ValidationContext that is appropriate for validating the given
+ * {@link ControllerService}
+ */
+ ValidationContext getControllerServiceValidationContext(ControllerService controllerService);
+
+ /**
+ * @return a new {@link ExpressionLanguageCompiler} that can be used to
+ * compile and evaluate Attribute Expressions
+ */
+ ExpressionLanguageCompiler newExpressionLanguageCompiler();
+
+ /**
+ * @param value to make a PropertyValue object for
+ * @return a PropertyValue that represents the given value
+ */
+ PropertyValue newPropertyValue(String value);
+
+ /**
+ * @return a Map of all configured Properties
+ */
+ Map<PropertyDescriptor, String> getProperties();
+
+ /**
+ * @return the currently configured Annotation Data
+ */
+ String getAnnotationData();
+
+ /**
+ * There are times when the framework needs to consider a component valid,
+ * even if it references an invalid ControllerService. This method will
+ * return <code>false</code> if the component is to be considered valid even
+ * if the given Controller Service is referenced and is invalid.
+ *
+ * @param service to check if validation is required
+ * @return <code>false</code> if the component is to be considered valid
+ * even if the given Controller Service is referenced and is invalid
+ */
+ boolean isValidationRequired(ControllerService service);
+
+ /**
+ * @param value to test whether expression language is present
+ * @return <code>true</code> if the given value contains a NiFi Expression
+ * Language expression, <code>false</code> if it does not
+ */
+ boolean isExpressionLanguagePresent(String value);
+
+ /**
+ * @param propertyName to test whether expression language is supported
+ * @return <code>true</code> if the property with the given name supports
+ * the NiFi Expression Language, <code>false</code> if the property does not
+ * support the Expression Language or is not a valid property name
+ */
+ boolean isExpressionLanguageSupported(String propertyName);
+
+ /**
+ * Returns the identifier of the ProcessGroup that the component being validated lives in
+ *
+ * @return the identifier of the ProcessGroup that the component being validated lives in
+ */
+ String getProcessGroupIdentifier();
+
+ /**
+ * Returns a Collection containing the names of all Parameters that are referenced by the property with the given name
+ *
+ * @param propertyName Name of property for which to get referenced parameters
+ * @return a Collection containing the names of all Parameters that are referenced by the property with the given name
+ */
+ Collection<String> getReferencedParameters(String propertyName);
+
+ /**
+ * @param parameterName the name of the Parameter
+ * @return <code>true</code> if a Parameter with the given name is defined in the currently selected Parameter Context
+ */
+ boolean isParameterDefined(String parameterName);
+
+ /**
+ * Returns <code>true</code> if a Parameter with the given name is defined and has a non-null value, <code>false</code> if either the Parameter
+ * is not defined or the Parameter is defined but has a value of <code>null</code>.
+ * @param parameterName the name of the parameter
+ * @return <code>true</code> if the Parameter is defined and has a non-null value, false otherwise
+ */
+ boolean isParameterSet(String parameterName);
+
+ /**
+ * Determines whether or not the dependencies of the given Property Descriptor are satisfied.
+ * If the given Property Descriptor has no dependency on another property, then the dependency is satisfied.
+ * If there is at least one dependency, then all dependencies must be satisfied.
+ * In order for a dependency to be considered satisfied, all of the following must be true:
+ * <ul>
+ * <li>The property that is depended upon has all of its dependencies satisfied.</li>
+ * <li>If the given Property Descriptor depends on a given AllowableValue, then the property that is depended upon has a value that falls within the given range of Allowable Values for
+ * the dependency.</li>
+ * </ul>
+ *
+ * @param propertyDescriptor the property descriptor
+ * @param propertyDescriptorLookup a lookup for converting from a property name to the property descriptor with that name
+ * @return <code>true</code> if all dependencies of the given property descriptor are satisfied, <code>false</code> otherwise
+ */
+ default boolean isDependencySatisfied(PropertyDescriptor propertyDescriptor, Function<String, PropertyDescriptor> propertyDescriptorLookup) {
+ return isDependencySatisfied(propertyDescriptor, propertyDescriptorLookup, new HashSet<>());
+ }
+
+ private boolean isDependencySatisfied(final PropertyDescriptor propertyDescriptor, final Function<String, PropertyDescriptor> propertyDescriptorLookup, final Set<String> propertiesSeen) {
+ final Set<PropertyDependency> dependencies = propertyDescriptor.getDependencies();
+ if (dependencies.isEmpty()) {
+ return true;
+ }
+
+ final boolean added = propertiesSeen.add(propertyDescriptor.getName());
+ if (!added) {
+ return false;
+ }
+
+ try {
+ for (final PropertyDependency dependency : dependencies) {
+ final String dependencyName = dependency.getPropertyName();
+
+ // Check if the property being depended upon has its dependencies satisfied.
+ final PropertyDescriptor dependencyDescriptor = propertyDescriptorLookup.apply(dependencyName);
+ if (dependencyDescriptor == null) {
+ return false;
+ }
+
+ final PropertyValue propertyValue = getProperty(dependencyDescriptor);
+ final String dependencyValue = propertyValue == null ? dependencyDescriptor.getDefaultValue() : propertyValue.getValue();
+ if (dependencyValue == null) {
+ return false;
+ }
+
+ final boolean transitiveDependencySatisfied = isDependencySatisfied(dependencyDescriptor, propertyDescriptorLookup, propertiesSeen);
+ if (!transitiveDependencySatisfied) {
+ return false;
+ }
+
+ // Check if the property being depended upon is set to one of the values that satisfies this dependency.
+ // If the dependency has no dependent values, then any non-null value satisfies the dependency.
+ // The value is already known to be non-null due to the check above.
+ final Set<String> dependentValues = dependency.getDependentValues();
+ if (dependentValues != null && !dependentValues.contains(dependencyValue)) {
+ return false;
+ }
+ }
+
+ return true;
+ } finally {
+ propertiesSeen.remove(propertyDescriptor.getName());
+ }
+ }
+
+ /**
+ * Determines whether or not incoming and outgoing connections should be validated.
+ * If <code>true</code>, then the validation should verify that all Relationships either have one or more connections that include the Relationship,
+ * or that the Relationship is auto-terminated.
+ * Additionally, if <code>true</code>, then any Processor with an {@link InputRequirement} of {@link InputRequirement.Requirement#INPUT_REQUIRED}
+ * should be invalid unless it has an incoming (non-looping) connection, and any Processor with an {@link InputRequirement} of {@link InputRequirement.Requirement#INPUT_FORBIDDEN}
+ * should be invalid if it does have any incoming connection.
+ *
+ * @return <code>true</code> if Connections should be validated, <code>false</code> if Connections should be ignored
+ */
+ default boolean isValidateConnections() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/ValidationResult.java b/src/main/java/org/apache/nifi/components/ValidationResult.java
new file mode 100644
index 0000000..ca23c1d
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/ValidationResult.java
@@ -0,0 +1,170 @@
+/*
+ * 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;
+
+import java.util.Objects;
+
+/**
+ * Immutable - thread safe
+ */
+public class ValidationResult {
+
+ private final String subject;
+ private final String input;
+ private final String explanation;
+ private final boolean valid;
+
+ protected ValidationResult(final Builder builder) {
+ this.subject = builder.subject;
+ this.input = builder.input;
+ this.explanation = builder.explanation;
+ this.valid = builder.valid;
+ }
+
+ /**
+ * @return true if current result is valid; false otherwise
+ */
+ public boolean isValid() {
+ return this.valid;
+ }
+
+ /**
+ * @return this input value that was tested for validity
+ */
+ public String getInput() {
+ return this.input;
+ }
+
+ /**
+ * @return an explanation of the validation result
+ */
+ public String getExplanation() {
+ return this.explanation;
+ }
+
+ /**
+ * @return the item being validated
+ */
+ public String getSubject() {
+ return this.subject;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 79 * hash + Objects.hashCode(this.subject);
+ hash = 79 * hash + Objects.hashCode(this.input);
+ hash = 79 * hash + Objects.hashCode(this.explanation);
+ hash = 79 * hash + (this.valid ? 1 : 0);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final ValidationResult other = (ValidationResult) obj;
+ if (!Objects.equals(this.subject, other.subject)) {
+ return false;
+ }
+ if (!Objects.equals(this.input, other.input)) {
+ return false;
+ }
+ if (!Objects.equals(this.explanation, other.explanation)) {
+ return false;
+ }
+ if (this.valid != other.valid) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ if (input == null) {
+ return String.format("'%s' is %s because %s", subject, valid ? "valid" : "invalid", explanation);
+ } else {
+ return String.format("'%s' validated against '%s' is %s because %s", subject, input, valid ? "valid" : "invalid", explanation);
+ }
+ }
+
+ public static final class Builder {
+
+ private boolean valid = false;
+ private String input = null;
+ private String explanation = "";
+ private String subject = "";
+
+ /**
+ * Defaults to false
+ *
+ * @param valid true if is valid; false otherwise
+ * @return the builder
+ */
+ public Builder valid(final boolean valid) {
+ this.valid = valid;
+ return this;
+ }
+
+ /**
+ * Defaults to empty string
+ *
+ * @param input what was validated
+ * @return the builder
+ */
+ public Builder input(final String input) {
+ if (null != input) {
+ this.input = input;
+ }
+ return this;
+ }
+
+ /**
+ * Defaults to empty string
+ *
+ * @param explanation of validation result
+ * @return the builder
+ */
+ public Builder explanation(final String explanation) {
+ if (null != explanation) {
+ this.explanation = explanation;
+ }
+ return this;
+ }
+
+ /**
+ * Defaults to empty string
+ *
+ * @param subject the thing that was validated
+ * @return the builder
+ */
+ public Builder subject(final String subject) {
+ if (null != subject) {
+ this.subject = subject;
+ }
+ return this;
+ }
+
+ public ValidationResult build() {
+ return new ValidationResult(this);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/Validator.java b/src/main/java/org/apache/nifi/components/Validator.java
new file mode 100644
index 0000000..babdc41
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/Validator.java
@@ -0,0 +1,51 @@
+/*
+ * 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 interface Validator {
+
+ /**
+ * Validator object providing validation behavior in which validation always
+ * fails
+ */
+ Validator INVALID = new Validator() {
+ @Override
+ public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+ return new ValidationResult.Builder().subject(subject).explanation(String.format("'%s' is not a supported property or has no Validator associated with it", subject)).input(input).build();
+ }
+ };
+
+ /**
+ * Validator object providing validation behavior in which validation always
+ * passes
+ */
+ Validator VALID = new Validator() {
+ @Override
+ public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
+ return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
+ }
+ };
+
+ /**
+ * @param subject what is being validated
+ * @param input the string to be validated
+ * @param context the ValidationContext to use when validating properties
+ * @return ValidationResult
+ * @throws NullPointerException of given input is null
+ */
+ ValidationResult validate(String subject, String input, ValidationContext context);
+}
diff --git a/src/main/java/org/apache/nifi/components/VersionedComponent.java b/src/main/java/org/apache/nifi/components/VersionedComponent.java
new file mode 100644
index 0000000..53fc62c
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/VersionedComponent.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import java.util.Optional;
+
+public interface VersionedComponent {
+
+ /**
+ * @return the unique identifier that maps this component to a component that is versioned
+ * in a Flow Registry or has been imported, or <code>Optional.empty</code> if this component has not
+ * been saved to a Flow Registry or imported.
+ */
+ Optional<String> getVersionedComponentId();
+
+ /**
+ * Updates the versioned component identifier
+ *
+ * @param versionedComponentId the identifier of the versioned component
+ *
+ * @throws IllegalStateException if this component is already under version control with a different ID and
+ * the given ID is not null
+ */
+ void setVersionedComponentId(String versionedComponentId);
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/FileResourceReference.java b/src/main/java/org/apache/nifi/components/resource/FileResourceReference.java
new file mode 100644
index 0000000..65fc167
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/FileResourceReference.java
@@ -0,0 +1,100 @@
+/*
+ * 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.resource;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Objects;
+
+public class FileResourceReference implements ResourceReference {
+ private final File file;
+ private final ResourceType resourceType;
+
+ public FileResourceReference(final File file) {
+ this.file = Objects.requireNonNull(file);
+ this.resourceType = file.isDirectory() ? ResourceType.DIRECTORY : ResourceType.FILE;
+ }
+
+ @Override
+ public File asFile() {
+ return file;
+ }
+
+ @Override
+ public URL asURL() {
+ try {
+ return file.toURI().toURL();
+ } catch (final MalformedURLException e) {
+ throw new AssertionError("File " + file.getAbsolutePath() + " cannot be represented as a URL"); // we won't encounter this.
+ }
+ }
+
+ @Override
+ public InputStream read() throws IOException {
+ if (resourceType != ResourceType.FILE) {
+ throw new FileNotFoundException("Could not read from file with name " + file.getAbsolutePath() + " because that references a directory");
+ }
+
+ return new FileInputStream(file);
+ }
+
+ @Override
+ public boolean isAccessible() {
+ return file.exists() && file.canRead();
+ }
+
+ @Override
+ public String getLocation() {
+ return file.getAbsolutePath();
+ }
+
+ @Override
+ public ResourceType getResourceType() {
+ return resourceType;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final FileResourceReference that = (FileResourceReference) o;
+ return Objects.equals(file, that.file)
+ && resourceType == that.resourceType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(file, resourceType);
+ }
+
+ @Override
+ public String toString() {
+ return "FileResourceReference[file=" + file + ", resourceType=" + resourceType + "]";
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceCardinality.java b/src/main/java/org/apache/nifi/components/resource/ResourceCardinality.java
new file mode 100644
index 0000000..1563df7
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceCardinality.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.components.resource;
+
+/**
+ * Indicates the cardinality of how many resources can be referenced by a given property.
+ */
+public enum ResourceCardinality {
+
+ /**
+ * Exactly one resource must be specified
+ */
+ SINGLE,
+
+ /**
+ * One or more resources may be supplied, as a comma-separated list
+ */
+ MULTIPLE;
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceContext.java b/src/main/java/org/apache/nifi/components/resource/ResourceContext.java
new file mode 100644
index 0000000..bf02c59
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceContext.java
@@ -0,0 +1,35 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.components.PropertyDescriptor;
+
+/**
+ * Provides the context in which a Reference Reference is to be evaluated
+ */
+public interface ResourceContext {
+ /**
+ * @return a ResourceReferenceFactory that can be used to parse a property value into a {@link ResourceReference} or {@link ResourceReferences}
+ */
+ ResourceReferenceFactory getResourceReferenceFactory();
+
+ /**
+ * @return the PropertyDescriptor that describes the property whose value may be a resource
+ */
+ PropertyDescriptor getPropertyDescriptor();
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceDefinition.java b/src/main/java/org/apache/nifi/components/resource/ResourceDefinition.java
new file mode 100644
index 0000000..b91065e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceDefinition.java
@@ -0,0 +1,37 @@
+/*
+ * 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.resource;
+
+import java.util.Set;
+
+/**
+ * Defines the number and types of resources that allowed to be referenced by a component property
+ */
+public interface ResourceDefinition {
+
+ /**
+ * Specifies the number of resources that should be reference-able by a component property
+ * @return the cardinality for the number of resources that should be referenced
+ */
+ ResourceCardinality getCardinality();
+
+ /**
+ * @return the types of resources that are allowed to be referenced
+ */
+ Set<ResourceType> getResourceTypes();
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceReference.java b/src/main/java/org/apache/nifi/components/resource/ResourceReference.java
new file mode 100644
index 0000000..49b95d3
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceReference.java
@@ -0,0 +1,66 @@
+/*
+ * 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.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * A reference to a Resource that is identified by a property value
+ */
+public interface ResourceReference {
+
+ /**
+ * @return a file representation of the resource, or <code>null</code> if the Resource cannot be represented as a File
+ */
+ File asFile();
+
+ /**
+ * @return a URL representation of the resource, or <code>null</code> if the Resource cannot be represented as a URL
+ */
+ URL asURL();
+
+ /**
+ * @return an InputStream to read the contents of the resource
+ *
+ * @throws IOException if unable to obtain an InputStream from the resource
+ */
+ InputStream read() throws IOException;
+
+ /**
+ * Indicates whether or not the resource is accessible. What it means for the resource to be accessible depends on the type of
+ * resource. A File resource, for example, might be accessible only if the file exists and is readable, while a URL resource might
+ * always be considered accessible, or might be accesssible only if the existence of the resource can be confirmed.
+ *
+ * @return <code>true</code> if the file can be accessed, <code>false</code> otherwise
+ */
+ boolean isAccessible();
+
+ /**
+ * @return a String representation of the location, or <code>null</code> for a Resource that does not have an external location.
+ * For a File or a Directory, this will be the full path name; for a URL it will be the String form of the URL
+ */
+ String getLocation();
+
+ /**
+ * @return the type of resource that is being referenced
+ */
+ ResourceType getResourceType();
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceReferenceFactory.java b/src/main/java/org/apache/nifi/components/resource/ResourceReferenceFactory.java
new file mode 100644
index 0000000..67b056e
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceReferenceFactory.java
@@ -0,0 +1,24 @@
+/*
+ * 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.resource;
+
+public interface ResourceReferenceFactory {
+ ResourceReference createResourceReference(String value, ResourceDefinition resourceDefinition);
+
+ ResourceReferences createResourceReferences(String value, ResourceDefinition resourceDefinition);
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceReferences.java b/src/main/java/org/apache/nifi/components/resource/ResourceReferences.java
new file mode 100644
index 0000000..5addba0
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceReferences.java
@@ -0,0 +1,73 @@
+/*
+ * 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.resource;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * A representation of zero or more {@link ResourceReference}s
+ */
+public interface ResourceReferences {
+
+ /**
+ * @return a List representation of all Resource References
+ */
+ List<ResourceReference> asList();
+
+ /**
+ * @return a list of all Resource References' locations
+ */
+ List<String> asLocations();
+
+ /**
+ * @return a list of all Resource References' URLs
+ */
+ List<URL> asURLs();
+
+ /**
+ * @return the number of Resource References held
+ */
+ int getCount();
+
+ /**
+ * Iterates through the Resource References and for any reference that may represent more than one
+ * resource, flattens the resource into a List of single-entity references. For example, consider that this ResourceReferences
+ * holds a single ResourceReference, of type DIRECTORY and the referenced directory contains 10 files. Calling {@link #asList()} would
+ * return a single ResourceReference. But calling <code>flatten()</code> would return a new ResourceReferences type whose {@link #asList()}
+ * method would return 10 ResourceReference objects, each with a ResourceType of FILE. The flatten operation is not recursive, meaning that if
+ * a DIRECTORY is flattened, any sub-directories will be dropped. If the contents of the subdirectories are to be retained, use {@link #flattenRecursively()}
+ * instead.
+ *
+ * @return a flattened ResourceReferences
+ */
+ ResourceReferences flatten();
+
+ /**
+ * Recursively iterates through the Resource References and for any reference that may represent more than one
+ * resource, flattens the resource into a List of single-entity references. For example, consider that this ResourceReferences
+ * holds a single ResourceReference, of type DIRECTORY and the referenced directory contains 10 files. Calling {@link #asList()} would
+ * return a single ResourceReference. But calling <code>flatten()</code> would return a new ResourceReferences type whose {@link #asList()}
+ * method would return 10 ResourceReference objects, each with a ResourceType of FILE. The flatten operation is recursive, meaning that if
+ * a DIRECTORY is encountered, its reference will be replaced with a new reference for each file, even if that file exists 100 levels deep
+ * in the directory structure.
+ *
+ * @return a flattened ResourceReferences
+ */
+ ResourceReferences flattenRecursively();
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/ResourceType.java b/src/main/java/org/apache/nifi/components/resource/ResourceType.java
new file mode 100644
index 0000000..60eff90
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/ResourceType.java
@@ -0,0 +1,51 @@
+/*
+ * 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.resource;
+
+public enum ResourceType {
+ /**
+ * Referenced Resource is a File on a local (or mounted) file system
+ */
+ FILE("file"),
+
+ /**
+ * Referenced Resource is a directory on a local (or mounted) file system
+ */
+ DIRECTORY("directory"),
+
+ /**
+ * Referenced Resource is UTF-8 text, rather than an external entity
+ */
+ TEXT("text"),
+
+ /**
+ * Referenced Resource is a URL that uses the HTTP, HTTPS, or file protocol
+ * (i.e., <code>http://...</code>, <code>https://...</code>, or <code>file:...</code>)
+ */
+ URL("URL");
+
+ private final String prettyPrintName;
+ ResourceType(final String prettyPrintName) {
+ this.prettyPrintName = prettyPrintName;
+ }
+
+ @Override
+ public String toString() {
+ return prettyPrintName;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/StandardResourceContext.java b/src/main/java/org/apache/nifi/components/resource/StandardResourceContext.java
new file mode 100644
index 0000000..4e1fa6b
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/StandardResourceContext.java
@@ -0,0 +1,40 @@
+/*
+ * 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.resource;
+
+import org.apache.nifi.components.PropertyDescriptor;
+
+public class StandardResourceContext implements ResourceContext {
+ private final ResourceReferenceFactory resourceReferenceFactory;
+ private final PropertyDescriptor propertyDescriptor;
+
+ public StandardResourceContext(final ResourceReferenceFactory resourceReferenceFactory, final PropertyDescriptor propertyDescriptor) {
+ this.resourceReferenceFactory = resourceReferenceFactory;
+ this.propertyDescriptor = propertyDescriptor;
+ }
+
+ @Override
+ public ResourceReferenceFactory getResourceReferenceFactory() {
+ return resourceReferenceFactory;
+ }
+
+ @Override
+ public PropertyDescriptor getPropertyDescriptor() {
+ return propertyDescriptor;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/StandardResourceDefinition.java b/src/main/java/org/apache/nifi/components/resource/StandardResourceDefinition.java
new file mode 100644
index 0000000..125dcbf
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/StandardResourceDefinition.java
@@ -0,0 +1,40 @@
+/*
+ * 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.resource;
+
+import java.util.Set;
+
+public class StandardResourceDefinition implements ResourceDefinition {
+ private final ResourceCardinality cardinality;
+ private final Set<ResourceType> resourceTypes;
+
+ public StandardResourceDefinition(final ResourceCardinality cardinality, final Set<ResourceType> resourceTypes) {
+ this.cardinality = cardinality;
+ this.resourceTypes = resourceTypes;
+ }
+
+ @Override
+ public ResourceCardinality getCardinality() {
+ return cardinality;
+ }
+
+ @Override
+ public Set<ResourceType> getResourceTypes() {
+ return resourceTypes;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/StandardResourceReferenceFactory.java b/src/main/java/org/apache/nifi/components/resource/StandardResourceReferenceFactory.java
new file mode 100644
index 0000000..3980a9b
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/StandardResourceReferenceFactory.java
@@ -0,0 +1,131 @@
+/*
+ * 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.resource;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class StandardResourceReferenceFactory implements ResourceReferenceFactory {
+
+ private static final ResourceReferences EMPTY_RESOURCE_REFERENCES = new StandardResourceReferences(Collections.emptyList());
+
+ public ResourceReferences createResourceReferences(final String value, final ResourceDefinition resourceDefinition) {
+ if (value == null) {
+ return EMPTY_RESOURCE_REFERENCES;
+ }
+
+ final String trimmed = value.trim();
+ if (trimmed.isEmpty()) {
+ return EMPTY_RESOURCE_REFERENCES;
+ }
+
+ if (resourceDefinition == null) {
+ return EMPTY_RESOURCE_REFERENCES;
+ }
+
+ final List<ResourceReference> references;
+ final List<String> locations = parseResourceLocations(value);
+ references = new ArrayList<>(locations.size());
+ locations.forEach(location -> references.add(createResourceReference(location, resourceDefinition)));
+
+ return new StandardResourceReferences(references);
+ }
+
+ public ResourceReference createResourceReference(final String value, final ResourceDefinition resourceDefinition) {
+ if (value == null) {
+ return null;
+ }
+
+ final String trimmed = value.trim();
+ if (trimmed.isEmpty()) {
+ return null;
+ }
+
+ if (resourceDefinition == null) {
+ return null;
+ }
+
+ final Set<ResourceType> allowedResourceTypes = resourceDefinition.getResourceTypes();
+ if (allowedResourceTypes.contains(ResourceType.URL)) {
+ try {
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
+ return new URLResourceReference(URI.create(trimmed).toURL());
+ }
+
+ if (trimmed.startsWith("file:")) {
+ final URL url = URI.create(trimmed).toURL();
+ final String filename = url.getFile();
+ final File file = new File(filename);
+ return new FileResourceReference(file);
+ }
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Invalid URL: " + trimmed);
+ }
+ }
+
+ final boolean fileAllowed = allowedResourceTypes.contains(ResourceType.FILE) || allowedResourceTypes.contains(ResourceType.DIRECTORY);
+ final boolean textAllowed = allowedResourceTypes.contains(ResourceType.TEXT);
+
+ if (fileAllowed && textAllowed) {
+ // We have to make a determination whether this is a file or text. Eventually, it will be best if the user tells us explicitly.
+ // For now, we will make a determination based on a couple of simple rules.
+ final File file = new File(trimmed);
+ if (file.isAbsolute() || file.exists()) {
+ return new FileResourceReference(file);
+ }
+
+ if (trimmed.startsWith("./") || trimmed.startsWith(".\\")) {
+ return new FileResourceReference(file);
+ }
+
+ return new Utf8TextResource(value); // Use explicit value, not trimmed value, as the white space may be important for textual content.
+ }
+
+ if (fileAllowed) {
+ final File file = new File(trimmed);
+ return new FileResourceReference(file);
+ }
+
+ if (textAllowed) {
+ return new Utf8TextResource(value);
+ }
+
+ return null;
+ }
+
+ private List<String> parseResourceLocations(final String rawValue) {
+ final List<String> resourceLocations = new ArrayList<>();
+ final String[] splits = rawValue.split(",");
+ for (final String split : splits) {
+ final String trimmed = split.trim();
+ if (trimmed.isEmpty()) {
+ continue;
+ }
+
+ resourceLocations.add(trimmed);
+ }
+
+ return resourceLocations;
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/StandardResourceReferences.java b/src/main/java/org/apache/nifi/components/resource/StandardResourceReferences.java
new file mode 100644
index 0000000..11ce057
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/StandardResourceReferences.java
@@ -0,0 +1,154 @@
+/*
+ * 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.resource;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class StandardResourceReferences implements ResourceReferences {
+ public final List<ResourceReference> resourceReferences;
+
+ public StandardResourceReferences(final List<ResourceReference> resourceReferences) {
+ this.resourceReferences = new ArrayList<>(Objects.requireNonNull(resourceReferences));
+ }
+
+ @Override
+ public List<ResourceReference> asList() {
+ return Collections.unmodifiableList(resourceReferences);
+ }
+
+ @Override
+ public List<String> asLocations() {
+ final List<String> locations = new ArrayList<>(resourceReferences.size());
+ resourceReferences.forEach(ref -> locations.add(ref.getLocation()));
+ return locations;
+ }
+
+ @Override
+ public List<URL> asURLs() {
+ final List<URL> locations = new ArrayList<>(resourceReferences.size());
+ resourceReferences.forEach(ref -> locations.add(ref.asURL()));
+ return locations;
+ }
+
+ @Override
+ public int getCount() {
+ return resourceReferences.size();
+ }
+
+ @Override
+ public ResourceReferences flatten() {
+ if (resourceReferences.isEmpty()) {
+ return this;
+ }
+
+ final List<ResourceReference> flattened = new ArrayList<>();
+ resourceReferences.forEach(reference -> {
+ if (reference.getResourceType() == ResourceType.DIRECTORY) {
+ addChildren(reference.asFile(), flattened);
+ } else {
+ flattened.add(reference);
+ }
+ });
+
+ return new StandardResourceReferences(flattened);
+ }
+
+ private void addChildren(final File file, final List<ResourceReference> flattened) {
+ if (file == null) {
+ return;
+ }
+
+ if (file.isDirectory()) {
+ final File[] children = file.listFiles();
+ if (children != null) {
+ for (final File child : children) {
+ if (child.isFile()) {
+ flattened.add(new FileResourceReference(child));
+ }
+ }
+ }
+ } else {
+ flattened.add(new FileResourceReference(file));
+ }
+ }
+
+
+ @Override
+ public ResourceReferences flattenRecursively() {
+ if (resourceReferences.isEmpty()) {
+ return this;
+ }
+
+ final List<ResourceReference> flattened = new ArrayList<>();
+ resourceReferences.forEach(reference -> {
+ if (reference.getResourceType() == ResourceType.DIRECTORY) {
+ recurse(reference.asFile(), flattened);
+ } else {
+ flattened.add(reference);
+ }
+ });
+
+ return new StandardResourceReferences(flattened);
+ }
+
+ private void recurse(final File file, final List<ResourceReference> flattened) {
+ if (file == null) {
+ return;
+ }
+
+ if (file.isDirectory()) {
+ final File[] children = file.listFiles();
+ if (children != null) {
+ for (final File child : children) {
+ recurse(child, flattened);
+ }
+ }
+ } else {
+ flattened.add(new FileResourceReference(file));
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final StandardResourceReferences that = (StandardResourceReferences) o;
+ return Objects.equals(resourceReferences, that.resourceReferences);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(resourceReferences);
+ }
+
+ @Override
+ public String toString() {
+ return "StandardResourceReferences[resources=" + resourceReferences + "]";
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/URLResourceReference.java b/src/main/java/org/apache/nifi/components/resource/URLResourceReference.java
new file mode 100644
index 0000000..8b5f7f7
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/URLResourceReference.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.components.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+public class URLResourceReference implements ResourceReference {
+ private final URL url;
+
+ public URLResourceReference(final URL url) {
+ this.url = url;
+ }
+
+ @Override
+ public File asFile() {
+ return null;
+ }
+
+ @Override
+ public URL asURL() {
+ return url;
+ }
+
+ @Override
+ public InputStream read() throws IOException {
+ return url.openStream();
+ }
+
+ @Override
+ public boolean isAccessible() {
+ return true;
+ }
+
+ @Override
+ public String getLocation() {
+ return url.toExternalForm();
+ }
+
+ @Override
+ public ResourceType getResourceType() {
+ return ResourceType.URL;
+ }
+
+ @Override
+ public String toString() {
+ return "URLResourceReference[url=" + url + "]";
+ }
+
+}
diff --git a/src/main/java/org/apache/nifi/components/resource/Utf8TextResource.java b/src/main/java/org/apache/nifi/components/resource/Utf8TextResource.java
new file mode 100644
index 0000000..78f94e9
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/resource/Utf8TextResource.java
@@ -0,0 +1,68 @@
+/*
+ * 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.resource;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+
+public class Utf8TextResource implements ResourceReference {
+ private final String text;
+
+ public Utf8TextResource(final String text) {
+ this.text = text;
+ }
+
+ @Override
+ public File asFile() {
+ return null;
+ }
+
+ @Override
+ public URL asURL() {
+ return null;
+ }
+
+ @Override
+ public InputStream read() throws IOException {
+ return new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+ }
+
+ @Override
+ public boolean isAccessible() {
+ return true;
+ }
+
+ @Override
+ public String getLocation() {
+ return null;
+ }
+
+ @Override
+ public ResourceType getResourceType() {
+ return ResourceType.TEXT;
+ }
+
+ @Override
+ public String toString() {
+ return "Utf8TextResource[text=" + text.length() + " characters]";
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/state/Scope.java b/src/main/java/org/apache/nifi/components/state/Scope.java
new file mode 100644
index 0000000..dd0d0aa
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/state/Scope.java
@@ -0,0 +1,41 @@
+/*
+ * 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.state;
+
+/**
+ * A Scope represents how a NiFi component's state is to be stored and retrieved when running in a cluster.
+ */
+public enum Scope {
+
+ /**
+ * State is to be treated as "global" across the cluster. I.e., the same component on all nodes will
+ * have access to the same state.
+ */
+ CLUSTER,
+
+ /**
+ * State is to be treated local to the node. I.e., the same component will have different state on each
+ * node in the cluster.
+ */
+ LOCAL;
+
+ @Override
+ public String toString() {
+ return name();
+ }
+}
diff --git a/src/main/java/org/apache/nifi/components/state/StateManager.java b/src/main/java/org/apache/nifi/components/state/StateManager.java
new file mode 100644
index 0000000..1669ea1
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/state/StateManager.java
@@ -0,0 +1,101 @@
+/*
+ * 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.state;
+
+import java.io.IOException;
+import java.util.Map;
+import org.apache.nifi.annotation.behavior.Stateful;
+import org.apache.nifi.components.state.exception.StateTooLargeException;
+
+/**
+ * <p>
+ * The StateManager is responsible for providing NiFi components a mechanism for storing
+ * and retrieving state.
+ * </p>
+ *
+ * <p>
+ * When calling methods in this class, the {@link Scope} is used in order to specify whether
+ * state should be stored/retrieved from the local state or the clustered state. However, if
+ * any instance of NiFi is not clustered (or is disconnected from its cluster), the Scope is
+ * not really relevant and the local state will be used. This allows component
+ * developers to not concern themselves with whether or not a particular instance of NiFi is
+ * clustered. Instead, developers should assume that the instance is indeed clustered and write
+ * the component accordingly. If not clustered, the component will still behave in the same
+ * manner, as a standalone node could be thought of as a "cluster of 1."
+ * </p>
+ *
+ * <p>
+ * This mechanism is designed to allow developers to easily store and retrieve small amounts of state.
+ * The storage mechanism is implementation-specific, but is typically either stored on a remote system
+ * or on disk. For that reason, one should consider the cost of storing and retrieving this data, and the
+ * amount of data should be kept to the minimum required.
+ * </p>
+ *
+ * <p>
+ * Any component that wishes to use the StateManager should also use the {@link Stateful} annotation to provide
+ * a description of what state is being stored and what Scope is used. If this annotation is not present, the UI
+ * will not expose such information or allow DFMs to clear the state.
+ * </p>
+ */
+public interface StateManager {
+
+ /**
+ * Updates the value of the component's state, setting it to given value
+ *
+ * @param state the value to change the state to
+ * @param scope the scope to use when storing the state
+ *
+ * @throws StateTooLargeException if attempting to store more state than is allowed by the backing storage mechanism
+ * @throws IOException if unable to communicate with the underlying storage mechanism
+ */
+ void setState(Map<String, String> state, Scope scope) throws IOException;
+
+ /**
+ * Returns the current state for the component. This return value will never be <code>null</code>.
+ * If the state has not yet been set, the StateMap's version will be -1, and the map of values will be empty.
+ *
+ * @param scope the scope to use when fetching the state
+ * @return the current state for the component
+ * @throws IOException if unable to communicate with the underlying storage mechanism
+ */
+ StateMap getState(Scope scope) throws IOException;
+
+ /**
+ * Updates the value of the component's state to the new value if and only if the value currently
+ * is the same as the given oldValue.
+ *
+ * @param oldValue the old value to compare against
+ * @param newValue the new value to use if and only if the state's value is the same as the given oldValue
+ * @param scope the scope to use for storing the new state
+ * @return <code>true</code> if the state was updated to the new value, <code>false</code> if the state's value was not
+ * equal to oldValue
+ *
+ * @throws StateTooLargeException if attempting to store more state than is allowed by the backing storage mechanism
+ * @throws IOException if unable to communicate with the underlying storage mechanism
+ */
+ boolean replace(StateMap oldValue, Map<String, String> newValue, Scope scope) throws IOException;
+
+ /**
+ * Clears all keys and values from the component's state
+ *
+ * @param scope the scope whose values should be cleared
+ *
+ * @throws IOException if unable to communicate with the underlying storage mechanism
+ */
+ void clear(Scope scope) throws IOException;
+}
diff --git a/src/main/java/org/apache/nifi/components/state/StateMap.java b/src/main/java/org/apache/nifi/components/state/StateMap.java
new file mode 100644
index 0000000..95829a7
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/state/StateMap.java
@@ -0,0 +1,51 @@
+/*
+ * 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.state;
+
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Provides a representation of a component's state at some point in time.
+ */
+public interface StateMap {
+
+ /**
+ * Get state version is not guaranteed to be numeric, but can be used to compare against an expected version.
+ * The default implementation uses the available version number and considers -1 as indicating an empty version
+ *
+ * @return State version or empty when not known
+ */
+ Optional<String> getStateVersion();
+
+ /**
+ * Returns the value associated with the given key
+ *n
+ * @param key the key whose value should be retrieved
+ * @return the value associated with the given key, or <code>null</code> if no value is associated
+ * with this key.
+ */
+ String get(String key);
+
+ /**
+ * Returns an immutable Map representation of all keys and values for the state of a component.
+ *
+ * @return an immutable Map representation of all keys and values for the state of a component.
+ */
+ Map<String, String> toMap();
+}
diff --git a/src/main/java/org/apache/nifi/components/state/exception/StateTooLargeException.java b/src/main/java/org/apache/nifi/components/state/exception/StateTooLargeException.java
new file mode 100644
index 0000000..41c8b24
--- /dev/null
+++ b/src/main/java/org/apache/nifi/components/state/exception/StateTooLargeException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.state.exception;
+
+import java.io.IOException;
+import org.apache.nifi.components.state.StateManager;
+
+/**
+ * Thrown when attempting to store state via the {@link StateManager} but the state being
+ * stored is larger than is allowed by the backing storage mechanism.
+ */
+public class StateTooLargeException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ public StateTooLargeException(final String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/apache/nifi/context/ClusterContext.java b/src/main/java/org/apache/nifi/context/ClusterContext.java
new file mode 100644
index 0000000..aba5244
--- /dev/null
+++ b/src/main/java/org/apache/nifi/context/ClusterContext.java
@@ -0,0 +1,30 @@
+/*
+ * 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.context;
+
+/**
+ * A context for retrieving information about the state of the cluster.
+ */
+public interface ClusterContext {
+
+ /**
+ * Retrieves the current state of the cluster connection of this node.
+ *
+ * @return True if this node is connected to the cluster. False otherwise.
+ */
+ boolean isConnectedToCluster();
+}
diff --git a/src/main/java/org/apache/nifi/context/PropertyContext.java b/src/main/java/org/apache/nifi/context/PropertyContext.java
new file mode 100644
index 0000000..89ffb45
--- /dev/null
+++ b/src/main/java/org/apache/nifi/context/PropertyContext.java
@@ -0,0 +1,40 @@
+/*
+ * 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.context;
+
+import java.util.Map;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+
+/**
+ * A context for retrieving a PropertyValue from a PropertyDescriptor.
+ */
+public interface PropertyContext {
+
+ /**
+ * Retrieves the current value set for the given descriptor, if a value is
+ * set - else uses the descriptor to determine the appropriate default value
+ *
+ * @param descriptor to lookup the value of
+ * @return the property value of the given descriptor
+ */
+ PropertyValue getProperty(PropertyDescriptor descriptor);
+
+
+ Map<String, String> getAllProperties();
+
+}
diff --git a/src/main/java/org/apache/nifi/controller/AbstractControllerService.java b/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
new file mode 100644
index 0000000..30773ee
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/AbstractControllerService.java
@@ -0,0 +1,130 @@
+/*
+ * 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.controller;
+
+import org.apache.nifi.annotation.lifecycle.OnDisabled;
+import org.apache.nifi.annotation.lifecycle.OnEnabled;
+import org.apache.nifi.components.AbstractConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.components.state.StateManager;
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.reporting.InitializationException;
+
+public abstract class AbstractControllerService extends AbstractConfigurableComponent implements ControllerService {
+
+ private String identifier;
+ private ControllerServiceLookup serviceLookup;
+ private ComponentLog logger;
+ private StateManager stateManager;
+ private volatile ConfigurationContext configurationContext;
+ private volatile boolean enabled = false;
+ private NodeTypeProvider nodeTypeProvider;
+
+ @Override
+ public final void initialize(final ControllerServiceInitializationContext context) throws InitializationException {
+ this.identifier = context.getIdentifier();
+ serviceLookup = context.getControllerServiceLookup();
+ logger = context.getLogger();
+ stateManager = context.getStateManager();
+ nodeTypeProvider = context.getNodeTypeProvider();
+ init(context);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * @return the {@link ControllerServiceLookup} that was passed to the
+ * {@link #init(ControllerServiceInitializationContext)} method
+ */
+ protected final ControllerServiceLookup getControllerServiceLookup() {
+ return serviceLookup;
+ }
+
+ /**
+ * @return the {@link NodeTypeProvider} that was passed to the
+ * {@link #init(ControllerServiceInitializationContext)} method
+ */
+ protected final NodeTypeProvider getNodeTypeProvider() {
+ return nodeTypeProvider;
+ }
+
+ /**
+ * Provides a mechanism by which subclasses can perform initialization of
+ * the Controller Service before it is scheduled to be run
+ *
+ * @param config of initialization context
+ * @throws InitializationException if unable to init
+ */
+ protected void init(final ControllerServiceInitializationContext config) throws InitializationException {
+ }
+
+ @OnEnabled
+ public final void enabled() {
+ this.enabled = true;
+ }
+
+ @OnDisabled
+ public final void disabled() {
+ this.enabled = false;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ /**
+ * @return the logger that has been provided to the component by the
+ * framework in its initialize method
+ */
+ protected ComponentLog getLogger() {
+ return logger;
+ }
+
+ /**
+ * @return the StateManager that can be used to store and retrieve state for this Controller Service
+ */
+ protected StateManager getStateManager() {
+ return stateManager;
+ }
+
+ @OnEnabled
+ public final void abstractStoreConfigContext(final ConfigurationContext configContext) {
+ this.configurationContext = configContext;
+ }
+
+ @OnDisabled
+ public final void abstractClearConfigContext() {
+ this.configurationContext = null;
+ }
+
+ protected ConfigurationContext getConfigurationContext() {
+ final ConfigurationContext context = this.configurationContext;
+ if (context == null) {
+ throw new IllegalStateException("No Configuration Context exists");
+ }
+
+ return configurationContext;
+ }
+
+ protected PropertyValue getProperty(final PropertyDescriptor descriptor) {
+ return getConfigurationContext().getProperty(descriptor);
+ }
+}
diff --git a/src/main/java/org/apache/nifi/controller/ConfigurationContext.java b/src/main/java/org/apache/nifi/controller/ConfigurationContext.java
new file mode 100644
index 0000000..2886a2a
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/ConfigurationContext.java
@@ -0,0 +1,66 @@
+/*
+ * 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.controller;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.context.PropertyContext;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This context is passed to ControllerServices and Reporting Tasks in order
+ * to expose their configuration to them.
+ */
+public interface ConfigurationContext extends PropertyContext {
+
+ /**
+ * @return an unmodifiable map of all configured properties for this
+ * {@link ControllerService}
+ */
+ Map<PropertyDescriptor, String> getProperties();
+
+ /**
+ * @return the annotation data configured for the component
+ */
+ String getAnnotationData();
+
+ /**
+ * @return a String representation of the scheduling period, or <code>null</code> if
+ * the component does not have a scheduling period (e.g., for ControllerServices)
+ */
+ String getSchedulingPeriod();
+
+ /**
+ * Returns the amount of time, in the given {@link TimeUnit} that will
+ * elapsed between the return of one execution of the
+ * component's <code>onTrigger</code> method and
+ * the time at which the method is invoked again. This method will return
+ * null if the component does not have a scheduling period (e.g., for ControllerServices)
+ *
+ * @param timeUnit unit of time for scheduling
+ * @return period of time or <code>null</code> if component does not have a scheduling
+ * period
+ */
+ Long getSchedulingPeriod(TimeUnit timeUnit);
+
+ /**
+ * Returns the component's (ControllerService, ReportingTask, ParameterProvider, e.g.) name
+ * @return the String name of this component
+ */
+ String getName();
+}
diff --git a/src/main/java/org/apache/nifi/controller/ControllerService.java b/src/main/java/org/apache/nifi/controller/ControllerService.java
new file mode 100644
index 0000000..e8f9850
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/ControllerService.java
@@ -0,0 +1,206 @@
+/*
+ * 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.controller;
+
+import org.apache.nifi.annotation.behavior.Stateful;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.flowanalysis.FlowAnalysisRule;
+import org.apache.nifi.migration.PropertyConfiguration;
+import org.apache.nifi.parameter.ParameterProvider;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSessionFactory;
+import org.apache.nifi.processor.Processor;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingTask;
+
+/**
+ * <p>
+ * This interface provides a mechanism for creating services that are shared
+ * among all {@link Processor}s, {@link ReportingTask}s, {@link FlowAnalysisRule}s, {@link ParameterProvider}s and other
+ * {@code ControllerService}s.
+ * </p>
+ *
+ * <p>
+ * <code>ControllerService</code>s are discovered using Java's
+ * <code>ServiceLoader</code> mechanism. As a result, all implementations must
+ * follow these rules:
+ * </p>
+ *
+ * <ul>
+ * <li>The implementation must implement this interface.</li>
+ * <li>The implementation must have a file named
+ * org.apache.nifi.controller.ControllerService located within the jar's
+ * <code>META-INF/services</code> directory. This file contains a list of
+ * fully-qualified class names of all <code>ControllerService</code>s in the
+ * jar, one-per-line.
+ * <li>The implementation must support a default constructor.</li>
+ * </ul>
+ *
+ * <p>
+ * <b>All implementations of this interface must be thread-safe.</b>
+ * </p>
+ *
+ * <h2>Accessing Controller Services</h2>
+ * <p>
+ * A ControllerService is accessible only through its interface. The framework
+ * provides access to a ControllerService through two different mechanisms:
+ * <ul>
+ * <li>
+ * A {@link PropertyDescriptor} can be created via the
+ * {@link PropertyDescriptor.Builder} after calling the
+ * {@link PropertyDescriptor.Builder#identifiesControllerService(Class) identifiesControllerService(Class)}
+ * method and then the ControllerService is accessed via
+ * {@link PropertyValue#asControllerService(Class)} method.
+ * <p>
+ * For example:
+ * </p>
+ * <pre><code>
+ * public static final PropertyDescriptor MY_PROPERTY = new PropertyDescriptor.Builder()
+ * .name("My Property")
+ * .description("Example Property")
+ * .identifiesControllerService( MyControllerServiceInterface.class )
+ * .build();
+ *
+ * ...
+ * public void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException {
+ * // Obtain the user-selected controller service
+ * final MyControllerServiceInterface service = context.getProperty(MY_PROPERTY).asControllerService( MyControllerServiceInterface.class );
+ * ...
+ * }
+ *
+ * </code></pre>
+ * <li>A Controller Service can be obtained via a
+ * {@link ControllerServiceLookup}. This lookup may be obtained, for example,
+ * from the {@link ProcessContext} that is provided to a {@link Processor}'s
+ * {@link Processor#onTrigger(ProcessContext, ProcessSessionFactory) onTrigger}
+ * method.
+ * <p>
+ * For example:
+ * </p>
+ *
+ * <pre><code>
+ * public void onTrigger(ProcessContext context, ProcessSessionFactory sessionFactory) throws ProcessException {
+ * final MyControllerServiceInterface service = (MyControllerServiceInterface) context.getControllerServiceLookup().getControllerService("service_identifier");
+ * }
+ * </code></pre>
+ * </li>
+ * </ul>
+ *
+ * <h2>Defining a Controller Service</h2>
+ * <p>
+ * Note in both of the examples above, that the Controller Service was accessed
+ * only by its interface, and this interface extends ControllerService. If we
+ * have an implementation named MyServiceImpl, for example, that implements
+ * MyControllerServiceInterface, we cannot, in either case, attempt to cast the
+ * ControllerService to the desired implementation. Doing so will result in a
+ * {@link ClassCastException}. This is by design and is done for the following
+ * reasons:
+ *
+ * <ul>
+ * <li>
+ * It is a good coding practice to implement such a service as an interface in
+ * general.
+ * </li>
+ * <li>
+ * A Controller Service can be referenced from different NiFi Archives (NARs).
+ * This means that the Controller Service may be defined in one ClassLoader and
+ * referenced from another unrelated ClassLoader. In order to account for this,
+ * NiFi will change the current thread's ClassLoader as appropriate when
+ * entering the Controller Service's code and revert back to the previous
+ * ClassLoader after exiting the Controller Service's code.
+ * </li>
+ * </ul>
+ *
+ * <h2>Controller Services and NARs</h2>
+ * <p>
+ * Due to the fact that a Controller Service may be referenced from a different
+ * NAR than the one in which the implementation lives, it is crucial that both
+ * the Controller Service's interface and the code referencing the interface
+ * inherit from the same ClassLoader. This is accomplished by ensuring that the
+ * NAR that contains the Controller Service interface is the parent (or
+ * ancestor) of the NAR that references the Controller Service interface.
+ * </p>
+ *
+ * <p>
+ * Typically, this is done by creating a NAR structure as follows:
+ * <pre>
+ * + my-services-api-nar
+ * +--- service-X-implementation-nar
+ * +--- service-Y-implementation-nar
+ * +--- service-Z-implementation-nar
+ * +--- processor-A-nar
+ * +--- processor-B-nar
+ * </pre>
+ *
+ * <p>
+ * In this case, the {@code MyControllerServiceInterface} interface, and any
+ * other Controller Service interfaces, will be defined in the
+ * {@code my-services-api-nar} NAR. Implementations are then encapsulated within
+ * the {@code service-X-implementation-nar},
+ * {@code service-Y-implementation-nar}, and
+ * {@code service-Z-implementation-nar} NARs. All Controller Services and all
+ * Processors defined in these NARs are able to reference any other Controller
+ * Services whose interfaces are provided in the {@code my-services-api-nar}
+ * NAR.
+ * </p>
+ *
+ * <p>
+ * For more information on NARs, see the NiFi Developer Guide.
+ * </p>
+ */
+public interface ControllerService extends ConfigurableComponent {
+
+ /**
+ * Provides the Controller Service with access to objects that may be of use
+ * throughout the life of the service. This method will be called before any
+ * properties are set
+ *
+ * @param context of initialization
+ * @throws org.apache.nifi.reporting.InitializationException if unable to init
+ */
+ void initialize(ControllerServiceInitializationContext context) throws InitializationException;
+
+ /**
+ * Indicates whether this controller service, configured with the given {@link ConfigurationContext}, stores state.
+ * @param context provides access to convenience methods for obtaining property values
+ * @return True if this controller service stores state
+ */
+ default boolean isStateful(ConfigurationContext context) {
+ return this.getClass().isAnnotationPresent(Stateful.class);
+ }
+
+ /**
+ * <p>
+ * Allows for the migration of an old property configuration to a new configuration. This allows the Controller Service to evolve over time,
+ * as it allows properties to be renamed, removed, or reconfigured.
+ * </p>
+ *
+ * <p>
+ * This method is called only when a Controller Service is restored from a previous configuration. For example, when NiFi is restarted and the
+ * flow is restored from disk, when a previously configured flow is imported (e.g., from a JSON file that was exported or a NiFi Registry),
+ * or when a node joins a cluster and inherits a flow that has a new Controller Service. Once called, the method will not be invoked again for this
+ * Controller Service until NiFi is restarted.
+ * </p>
+ *
+ * @param config the current property configuration
+ */
+ default void migrateProperties(PropertyConfiguration config) {
+ }
+
+}
diff --git a/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java b/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.java
new file mode 100644
index 0000000..c778869
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/ControllerServiceInitializationContext.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.controller;
+
+import org.apache.nifi.components.state.StateManager;
+import org.apache.nifi.kerberos.KerberosContext;
+import org.apache.nifi.logging.ComponentLog;
+
+public interface ControllerServiceInitializationContext extends KerberosContext {
+
+ /**
+ * @return the identifier associated with the {@link ControllerService} with
+ * which this context is associated
+ */
+ String getIdentifier();
+
+ /**
+ * @return the {@link ControllerServiceLookup} which can be used to obtain
+ * Controller Services
+ */
+ ControllerServiceLookup getControllerServiceLookup();
+
+ /**
+ * @return a logger that can be used to log important events in a standard
+ * way and generate bulletins when appropriate
+ */
+ ComponentLog getLogger();
+
+ /**
+ * @return the StateManager that can be used to store and retrieve state for this component
+ */
+ StateManager getStateManager();
+
+ /**
+ * @return the {@link NodeTypeProvider} which can be used to detect the node
+ * type of this NiFi instance.
+ */
+ NodeTypeProvider getNodeTypeProvider();
+}
diff --git a/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java b/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
new file mode 100644
index 0000000..0144438
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/ControllerServiceLookup.java
@@ -0,0 +1,72 @@
+/*
+ * 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.controller;
+
+import java.util.Set;
+
+public interface ControllerServiceLookup {
+
+ /**
+ * @param serviceIdentifier of controller service
+ * @return the ControllerService that is registered with the given
+ * identifier
+ */
+ ControllerService getControllerService(String serviceIdentifier);
+
+ /**
+ * @param serviceIdentifier identifier of service to check
+ * @return <code>true</code> if the Controller Service with the given
+ * identifier is enabled, <code>false</code> otherwise. If the given
+ * identifier is not known by this ControllerServiceLookup, returns
+ * <code>false</code>
+ */
+ boolean isControllerServiceEnabled(String serviceIdentifier);
+
+ /**
+ * @param serviceIdentifier identifier of service to check
+ * @return <code>true</code> if the Controller Service with the given
+ * identifier has been enabled but is still in the transitioning state,
+ * otherwise returns <code>false</code>. If the given identifier is not
+ * known by this ControllerServiceLookup, returns <code>false</code>
+ */
+ boolean isControllerServiceEnabling(String serviceIdentifier);
+
+ /**
+ * @param service service to check
+ * @return <code>true</code> if the given Controller Service is enabled,
+ * <code>false</code> otherwise. If the given Controller Service is not
+ * known by this ControllerServiceLookup, returns <code>false</code>
+ */
+ boolean isControllerServiceEnabled(ControllerService service);
+
+ /**
+ *
+ * @param serviceType type of service to get identifiers for
+ *
+ * @return the set of all Controller Service Identifiers whose Controller
+ * Service is of the given type.
+ * @throws IllegalArgumentException if the given class is not an interface
+ */
+ Set<String> getControllerServiceIdentifiers(Class<? extends ControllerService> serviceType) throws IllegalArgumentException;
+
+ /**
+ * @param serviceIdentifier identifier to look up
+ * @return the name of the Controller service with the given identifier. If
+ * no service can be found with this identifier, returns {@code null}
+ */
+ String getControllerServiceName(String serviceIdentifier);
+}
diff --git a/src/main/java/org/apache/nifi/controller/NodeTypeProvider.java b/src/main/java/org/apache/nifi/controller/NodeTypeProvider.java
new file mode 100644
index 0000000..1552282
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/NodeTypeProvider.java
@@ -0,0 +1,78 @@
+/*
+ * 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.controller;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * This interface provides a set of methods for checking NiFi node type.
+ */
+public interface NodeTypeProvider {
+
+ /**
+ * @return true if this instance is clustered, false otherwise.MockProcessContext
+ * Clustered means that a node is either connected or trying to connect to the cluster.
+ */
+ boolean isClustered();
+
+ /**
+ * @return true if the expected state of clustering is true, false otherwise. Contrary to {{@link #isClustered()}}
+ * this does not dynamically change with the state of this node.
+ */
+ default boolean isConfiguredForClustering() {
+ return false;
+ }
+
+ /**
+ * @return true if this instances is clustered and connected to the cluster.
+ */
+ default boolean isConnected() {
+ return false;
+ }
+
+ /**
+ * @return true if this instance is the primary node in the cluster; false otherwise
+ */
+ boolean isPrimary();
+
+ /**
+ * @return Returns with the hostname of the current node, if clustered. For For a standalone
+ * NiFi this returns an empty instead.
+ */
+ default Optional<String> getCurrentNode() {
+ if (isClustered()) {
+ throw new IllegalStateException("Clustered environment is not handled!");
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * @return Names/IP addresses of all expected hosts in the cluster (including the current one). For a standalone
+ * NiFi this returns an empty set instead.
+ */
+ default Set<String> getClusterMembers() {
+ if (isClustered()) {
+ throw new IllegalStateException("Clustered environment is not handled!");
+ } else {
+ return Collections.emptySet();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/nifi/controller/ScheduledState.java b/src/main/java/org/apache/nifi/controller/ScheduledState.java
new file mode 100644
index 0000000..f457a9c
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/ScheduledState.java
@@ -0,0 +1,43 @@
+/*
+ * 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.controller;
+
+/**
+ * Indicates the valid values for the state of a <code>Triggerable</code> entity
+ * with respect to scheduling the entity to run.
+ */
+public enum ScheduledState {
+
+ /**
+ * Entity cannot be scheduled to run
+ */
+ DISABLED,
+ /**
+ * Entity can be scheduled to run but currently is not
+ */
+ STOPPED,
+ /**
+ * Entity is currently scheduled to run
+ */
+ RUNNING,
+
+ STARTING,
+
+ STOPPING,
+
+ RUN_ONCE;
+}
diff --git a/src/main/java/org/apache/nifi/controller/VerifiableControllerService.java b/src/main/java/org/apache/nifi/controller/VerifiableControllerService.java
new file mode 100644
index 0000000..c9978f8
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/VerifiableControllerService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.controller;
+
+import org.apache.nifi.logging.ComponentLog;
+import org.apache.nifi.components.ConfigVerificationResult;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * Any Controller Service that implements this interface will be provided the opportunity to verify
+ * a given configuration of the Controller Service. This allows the Controller Service to provide meaningful feedback
+ * to users when configuring the dataflow.
+ * </p>
+ *
+ * <p>
+ * Generally speaking, verification differs from validation in that validation is expected to be very
+ * quick and run often. If a Controller Service is not valid, it cannot be started. However, verification may be
+ * more expensive or time-consuming to complete. For example, validation may ensure that a username is
+ * provided for connecting to an external service but should not perform any sort of network connection
+ * in order to verify that the username is accurate. Verification, on the other hand, may create resources
+ * such as network connections, may be more expensive to complete, and may be run only when a user invokes
+ * the action (though verification may later occur at other stages, such as when starting a component).
+ * </p>
+ *
+ * <p>
+ * Verification is allowed to be run only when a Controller Service is fully disabled.
+ * Therefore, any initialization logic that may need to be performed
+ * before the Controller Service is triggered may also be required for verification. However, the framework is not responsible
+ * for triggering the Lifecycle management stages, such as @OnScheduled before triggering the verification. Such
+ * methods should be handled by the {@link #verify(ConfigurationContext, ComponentLog, Map)} itself.
+ * The {@link #verify(ConfigurationContext, ComponentLog, Map)} method will only be called if the configuration is valid according to the
+ * validation rules (i.e., all Property Descriptors' validators and customValidate methods have indicated that the configuration is valid).
+ * </p>
+ */
+public interface VerifiableControllerService {
+
+ /**
+ * Verifies that the configuration defined by the given ConfigurationContext is valid.
+ * @param context the ProcessContext that contains the necessary configuration
+ * @param verificationLogger a logger that can be used during verification. While the typical logger can be used, doing so may result
+ * in producing bulletins, which can be confusing.
+ * @param variables a Map of key/value pairs that can be used to resolve variables referenced in property values via Expression Language
+ *
+ * @return a List of ConfigVerificationResults, each illustrating one step of the verification process that was completed
+ */
+ List<ConfigVerificationResult> verify(ConfigurationContext context, ComponentLog verificationLogger, Map<String, String> variables);
+
+}
diff --git a/src/main/java/org/apache/nifi/controller/VersionedControllerServiceLookup.java b/src/main/java/org/apache/nifi/controller/VersionedControllerServiceLookup.java
new file mode 100644
index 0000000..899bcbc
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/VersionedControllerServiceLookup.java
@@ -0,0 +1,29 @@
+/*
+ * 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.controller;
+
+import org.apache.nifi.flow.VersionedControllerService;
+
+public interface VersionedControllerServiceLookup {
+
+ /**
+ * @param serviceIdentifier of controller service
+ * @return the VersionedControllerService with the given
+ * identifier
+ */
+ VersionedControllerService getVersionedControllerService(String serviceIdentifier);
+}
diff --git a/src/main/java/org/apache/nifi/controller/queue/QueueSize.java b/src/main/java/org/apache/nifi/controller/queue/QueueSize.java
new file mode 100644
index 0000000..ef6a145
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/queue/QueueSize.java
@@ -0,0 +1,94 @@
+/*
+ * 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.controller.queue;
+
+import java.text.NumberFormat;
+
+public class QueueSize {
+ private final int objectCount;
+ private final long totalSizeBytes;
+ private final int hashCode;
+
+ public QueueSize(final int numberObjects, final long totalSizeBytes) {
+ if (numberObjects < 0 || totalSizeBytes < 0) {
+ throw new IllegalArgumentException();
+ }
+ objectCount = numberObjects;
+ this.totalSizeBytes = totalSizeBytes;
+ hashCode = (int) (41 + 47 * objectCount + 51 * totalSizeBytes);
+ }
+
+ /**
+ * @return number of objects present on the queue
+ */
+ public int getObjectCount() {
+ return objectCount;
+ }
+
+ /**
+ * @return total size in bytes of the content for the data on the queue
+ */
+ public long getByteCount() {
+ return totalSizeBytes;
+ }
+
+ /**
+ * Returns a new QueueSize that is the sum of this QueueSize and the provided QueueSize
+ *
+ * @param other the other QueueSize to add to this QueueSize
+ * @return a new QueueSize that is the sum of this QueueSize and the provided QueueSize
+ */
+ public QueueSize add(final QueueSize other) {
+ if (other == null) {
+ return new QueueSize(objectCount, totalSizeBytes);
+ }
+
+ return new QueueSize(objectCount + other.getObjectCount(), totalSizeBytes + other.getByteCount());
+ }
+
+ public QueueSize add(final int count, final long bytes) {
+ return new QueueSize(objectCount + count, totalSizeBytes + bytes);
+ }
+
+ @Override
+ public String toString() {
+ return "QueueSize[FlowFiles=" + objectCount + ", ContentSize=" + NumberFormat.getNumberInstance().format(totalSizeBytes) + " Bytes]";
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (!(obj instanceof QueueSize)) {
+ return false;
+ }
+
+ final QueueSize other = (QueueSize) obj;
+ return getObjectCount() == other.getObjectCount() && getByteCount() == other.getByteCount();
+ }
+}
diff --git a/src/main/java/org/apache/nifi/controller/status/ConnectionStatus.java b/src/main/java/org/apache/nifi/controller/status/ConnectionStatus.java
new file mode 100644
index 0000000..bd2cd29
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/status/ConnectionStatus.java
@@ -0,0 +1,313 @@
+/*
+ * 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.controller.status;
+
+import org.apache.nifi.controller.status.analytics.ConnectionStatusPredictions;
+import org.apache.nifi.processor.DataUnit;
+
+public class ConnectionStatus implements Cloneable {
+
+ private String id;
+ private String groupId;
+ private String name;
+ private String sourceId;
+ private String sourceName;
+ private String destinationId;
+ private String destinationName;
+ private String backPressureDataSizeThreshold;
+ private ConnectionStatusPredictions predictions;
+ private long backPressureBytesThreshold;
+ private long backPressureObjectThreshold;
+ private int inputCount;
+ private long inputBytes;
+ private int queuedCount;
+ private long queuedBytes;
+ private int outputCount;
+ private long outputBytes;
+ private int maxQueuedCount;
+ private long maxQueuedBytes;
+ private long totalQueuedDuration;
+ private long maxQueuedDuration;
+ private FlowFileAvailability flowFileAvailability;
+ private LoadBalanceStatus loadBalanceStatus;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(final String id) {
+ this.id = id;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(final String groupId) {
+ this.groupId = groupId;
+ }
+
+ public LoadBalanceStatus getLoadBalanceStatus() {
+ return loadBalanceStatus;
+ }
+
+ public void setLoadBalanceStatus(final LoadBalanceStatus loadBalanceStatus) {
+ this.loadBalanceStatus = loadBalanceStatus;
+ }
+
+ public int getQueuedCount() {
+ return queuedCount;
+ }
+
+ public void setQueuedCount(final int queuedCount) {
+ this.queuedCount = queuedCount;
+ }
+
+ public long getQueuedBytes() {
+ return queuedBytes;
+ }
+
+ public void setQueuedBytes(final long queuedBytes) {
+ this.queuedBytes = queuedBytes;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getSourceId() {
+ return sourceId;
+ }
+
+ public void setSourceId(String sourceId) {
+ this.sourceId = sourceId;
+ }
+
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ public void setSourceName(String sourceName) {
+ this.sourceName = sourceName;
+ }
+
+ public String getDestinationId() {
+ return destinationId;
+ }
+
+ public void setDestinationId(String destinationId) {
+ this.destinationId = destinationId;
+ }
+
+ public String getDestinationName() {
+ return destinationName;
+ }
+
+ public void setDestinationName(String destinationName) {
+ this.destinationName = destinationName;
+ }
+
+ public String getBackPressureDataSizeThreshold() {
+ return backPressureDataSizeThreshold;
+ }
+
+ public void setBackPressureDataSizeThreshold(String backPressureDataSizeThreshold) {
+ this.backPressureDataSizeThreshold = backPressureDataSizeThreshold;
+ setBackPressureBytesThreshold(DataUnit.parseDataSize(backPressureDataSizeThreshold, DataUnit.B).longValue());
+ }
+
+ public ConnectionStatusPredictions getPredictions() {
+ return predictions;
+ }
+
+ public void setPredictions(ConnectionStatusPredictions predictions) {
+ this.predictions = predictions;
+ }
+
+ public long getBackPressureObjectThreshold() {
+ return backPressureObjectThreshold;
+ }
+
+ public void setBackPressureObjectThreshold(long backPressureObjectThreshold) {
+ this.backPressureObjectThreshold = backPressureObjectThreshold;
+ }
+
+ public long getInputBytes() {
+ return inputBytes;
+ }
+
+ public void setInputBytes(long inputBytes) {
+ this.inputBytes = inputBytes;
+ }
+
+ public int getInputCount() {
+ return inputCount;
+ }
+
+ public void setInputCount(int inputCount) {
+ this.inputCount = inputCount;
+ }
+
+ public long getOutputBytes() {
+ return outputBytes;
+ }
+
+ public void setOutputBytes(long outputBytes) {
+ this.outputBytes = outputBytes;
+ }
+
+ public int getOutputCount() {
+ return outputCount;
+ }
+
+ public void setOutputCount(int outputCount) {
+ this.outputCount = outputCount;
+ }
+
+ public int getMaxQueuedCount() {
+ return maxQueuedCount;
+ }
+
+ public void setMaxQueuedCount(int maxQueuedCount) {
+ this.maxQueuedCount = maxQueuedCount;
+ }
+
+ public long getMaxQueuedBytes() {
+ return maxQueuedBytes;
+ }
+
+ public void setMaxQueuedBytes(long maxQueueBytes) {
+ this.maxQueuedBytes = maxQueueBytes;
+ }
+
+ public long getBackPressureBytesThreshold() {
+ return backPressureBytesThreshold;
+ }
+
+ public void setBackPressureBytesThreshold(long backPressureBytesThreshold) {
+ this.backPressureBytesThreshold = backPressureBytesThreshold;
+ }
+
+ public long getTotalQueuedDuration() {
+ return totalQueuedDuration;
+ }
+
+ public void setTotalQueuedDuration(long totalQueuedDuration) {
+ this.totalQueuedDuration = totalQueuedDuration;
+ }
+
+ public long getMaxQueuedDuration() {
+ return maxQueuedDuration;
+ }
+
+ public void setMaxQueuedDuration(long maxQueuedDuration) {
+ this.maxQueuedDuration = maxQueuedDuration;
+ }
+
+ public FlowFileAvailability getFlowFileAvailability() {
+ return flowFileAvailability;
+ }
+
+ public void setFlowFileAvailability(final FlowFileAvailability availability) {
+ this.flowFileAvailability = availability;
+ }
+
+ @Override
+ public ConnectionStatus clone() {
+ final ConnectionStatus clonedObj = new ConnectionStatus();
+ clonedObj.groupId = groupId;
+ clonedObj.id = id;
+ clonedObj.loadBalanceStatus = loadBalanceStatus;
+ clonedObj.inputBytes = inputBytes;
+ clonedObj.inputCount = inputCount;
+ clonedObj.name = name;
+ clonedObj.outputBytes = outputBytes;
+ clonedObj.outputCount = outputCount;
+ clonedObj.queuedBytes = queuedBytes;
+ clonedObj.queuedCount = queuedCount;
+ clonedObj.sourceId = sourceId;
+ clonedObj.sourceName = sourceName;
+ clonedObj.destinationId = destinationId;
+ clonedObj.destinationName = destinationName;
+ clonedObj.flowFileAvailability = flowFileAvailability;
+
+ if (predictions != null) {
+ clonedObj.setPredictions(predictions.clone());
+ }
+
+ clonedObj.backPressureDataSizeThreshold = backPressureDataSizeThreshold;
+ clonedObj.backPressureObjectThreshold = backPressureObjectThreshold;
+ clonedObj.maxQueuedBytes = maxQueuedBytes;
+ clonedObj.maxQueuedCount = maxQueuedCount;
+ clonedObj.totalQueuedDuration = totalQueuedDuration;
+ clonedObj.maxQueuedDuration = maxQueuedDuration;
+ return clonedObj;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("ConnectionStatus [id=");
+ builder.append(id);
+ builder.append(", groupId=");
+ builder.append(groupId);
+ builder.append(", name=");
+ builder.append(name);
+ builder.append(", sourceId=");
+ builder.append(sourceId);
+ builder.append(", sourceName=");
+ builder.append(sourceName);
+ builder.append(", destinationId=");
+ builder.append(destinationId);
+ builder.append(", destinationName=");
+ builder.append(destinationName);
+ builder.append(", backPressureDataSizeThreshold=");
+ builder.append(backPressureDataSizeThreshold);
+ builder.append(", backPressureObjectThreshold=");
+ builder.append(backPressureObjectThreshold);
+ builder.append(", flowFileAvailability=");
+ builder.append(flowFileAvailability);
+ builder.append(", inputCount=");
+ builder.append(inputCount);
+ builder.append(", inputBytes=");
+ builder.append(inputBytes);
+ builder.append(", queuedCount=");
+ builder.append(queuedCount);
+ builder.append(", queuedBytes=");
+ builder.append(queuedBytes);
+ builder.append(", outputCount=");
+ builder.append(outputCount);
+ builder.append(", outputBytes=");
+ builder.append(outputBytes);
+ builder.append(", maxQueuedCount=");
+ builder.append(maxQueuedCount);
+ builder.append(", maxQueueBytes=");
+ builder.append(maxQueuedBytes);
+ builder.append(", totalActiveQueuedDuration=");
+ builder.append(totalQueuedDuration);
+ builder.append(", maxActiveQueuedDuration=");
+ builder.append(maxQueuedDuration);
+ builder.append(", loadBalanceStatus=");
+ builder.append(loadBalanceStatus);
+ builder.append("]");
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/org/apache/nifi/controller/status/FlowFileAvailability.java b/src/main/java/org/apache/nifi/controller/status/FlowFileAvailability.java
new file mode 100644
index 0000000..d805312
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/status/FlowFileAvailability.java
@@ -0,0 +1,26 @@
+/*
+ * 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.controller.status;
+
+public enum FlowFileAvailability {
+ ACTIVE_QUEUE_EMPTY,
+
+ HEAD_OF_QUEUE_PENALIZED,
+
+ FLOWFILE_AVAILABLE;
+}
diff --git a/src/main/java/org/apache/nifi/controller/status/LoadBalanceStatus.java b/src/main/java/org/apache/nifi/controller/status/LoadBalanceStatus.java
new file mode 100644
index 0000000..bd3bed4
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/status/LoadBalanceStatus.java
@@ -0,0 +1,26 @@
+/*
+ * 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.controller.status;
+
+public enum LoadBalanceStatus {
+ LOAD_BALANCE_NOT_CONFIGURED,
+
+ LOAD_BALANCE_ACTIVE,
+
+ LOAD_BALANCE_INACTIVE;
+}
diff --git a/src/main/java/org/apache/nifi/controller/status/NodeStatus.java b/src/main/java/org/apache/nifi/controller/status/NodeStatus.java
new file mode 100644
index 0000000..0414ee2
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/status/NodeStatus.java
@@ -0,0 +1,207 @@
+/*
+ * 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.controller.status;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The status of a NiFi node.
+ */
+public class NodeStatus implements Cloneable {
+ private long createdAtInMs;
+
+ private long freeHeap;
+ private long usedHeap;
+ private long heapUtilization;
+ private long freeNonHeap;
+ private long usedNonHeap;
+
+ private long openFileHandlers;
+ private double processorLoadAverage;
+
+ private long totalThreads;
+ private long timerDrivenThreads;
+
+ private long flowFileRepositoryFreeSpace;
+ private long flowFileRepositoryUsedSpace;
+
+ private List<StorageStatus> contentRepositories = new ArrayList<>();
+ private List<StorageStatus> provenanceRepositories = new ArrayList<>();
+
+ public long getCreatedAtInMs() {
+ return createdAtInMs;
+ }
+
+ public void setCreatedAtInMs(final long createdAtInMs) {
+ this.createdAtInMs = createdAtInMs;
+ }
+
+ public long getFreeHeap() {
+ return freeHeap;
+ }
+
+ public void setFreeHeap(final long freeHeap) {
+ this.freeHeap = freeHeap;
+ }
+
+ public long getUsedHeap() {
+ return usedHeap;
+ }
+
+ public void setUsedHeap(final long usedHeap) {
+ this.usedHeap = usedHeap;
+ }
+
+ public long getHeapUtilization() {
+ return heapUtilization;
+ }
+
+ public void setHeapUtilization(final long heapUtilization) {
+ this.heapUtilization = heapUtilization;
+ }
+
+ public long getFreeNonHeap() {
+ return freeNonHeap;
+ }
+
+ public void setFreeNonHeap(final long freeNonHeap) {
+ this.freeNonHeap = freeNonHeap;
+ }
+
+ public long getUsedNonHeap() {
+ return usedNonHeap;
+ }
+
+ public void setUsedNonHeap(final long usedNonHeap) {
+ this.usedNonHeap = usedNonHeap;
+ }
+
+ public long getOpenFileHandlers() {
+ return openFileHandlers;
+ }
+
+ public void setOpenFileHandlers(final long openFileHandlers) {
+ this.openFileHandlers = openFileHandlers;
+ }
+
+ public double getProcessorLoadAverage() {
+ return processorLoadAverage;
+ }
+
+ public void setProcessorLoadAverage(final double processorLoadAverage) {
+ this.processorLoadAverage = processorLoadAverage;
+ }
+
+ public long getTotalThreads() {
+ return totalThreads;
+ }
+
+ public void setTotalThreads(final long totalThreads) {
+ this.totalThreads = totalThreads;
+ }
+
+ public long getTimerDrivenThreads() {
+ return timerDrivenThreads;
+ }
+
+ public void setTimerDrivenThreads(final long timerDrivenThreads) {
+ this.timerDrivenThreads = timerDrivenThreads;
+ }
+
+ public long getFlowFileRepositoryFreeSpace() {
+ return flowFileRepositoryFreeSpace;
+ }
+
+ public void setFlowFileRepositoryFreeSpace(final long flowFileRepositoryFreeSpace) {
+ this.flowFileRepositoryFreeSpace = flowFileRepositoryFreeSpace;
+ }
+
+ public long getFlowFileRepositoryUsedSpace() {
+ return flowFileRepositoryUsedSpace;
+ }
+
+ public void setFlowFileRepositoryUsedSpace(final long flowFileRepositoryUsedSpace) {
+ this.flowFileRepositoryUsedSpace = flowFileRepositoryUsedSpace;
+ }
+
+ public List<StorageStatus> getContentRepositories() {
+ return contentRepositories;
+ }
+
+ public void setContentRepositories(final List<StorageStatus> contentRepositories) {
+ this.contentRepositories = new ArrayList<>();
+ this.contentRepositories.addAll(contentRepositories);
+ }
+
+ public List<StorageStatus> getProvenanceRepositories() {
+ return provenanceRepositories;
+ }
+
+ public void setProvenanceRepositories(final List<StorageStatus> provenanceRepositories) {
+ this.provenanceRepositories = new ArrayList<>();
+ this.provenanceRepositories.addAll(provenanceRepositories);
+ }
+
+ @Override
+ protected NodeStatus clone() {
+ final NodeStatus clonedObj = new NodeStatus();
+ clonedObj.createdAtInMs = createdAtInMs;
+ clonedObj.freeHeap = freeHeap;
+ clonedObj.usedHeap = usedHeap;
+ clonedObj.heapUtilization = heapUtilization;
+ clonedObj.freeNonHeap = freeNonHeap;
+ clonedObj.usedNonHeap = usedNonHeap;
+ clonedObj.openFileHandlers = openFileHandlers;
+ clonedObj.processorLoadAverage = processorLoadAverage;
+ clonedObj.totalThreads = totalThreads;
+ clonedObj.timerDrivenThreads = timerDrivenThreads;
+ clonedObj.flowFileRepositoryFreeSpace = flowFileRepositoryFreeSpace;
+ clonedObj.flowFileRepositoryUsedSpace = flowFileRepositoryUsedSpace;
+
+ final List<StorageStatus> clonedContentRepositories = new ArrayList<>();
+ contentRepositories.stream().map(r -> r.clone()).forEach(r -> clonedContentRepositories.add(r));
+ clonedObj.contentRepositories = clonedContentRepositories;
+
+ final List<StorageStatus> clonedProvenanceRepositories = new ArrayList<>();
+ provenanceRepositories.stream().map(r -> r.clone()).forEach(r -> clonedProvenanceRepositories.add(r));
+ clonedObj.provenanceRepositories = clonedProvenanceRepositories;
+
+ return clonedObj;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("NodeStatus{");
+ sb.append("createdAtInMs=").append(createdAtInMs);
+ sb.append(", freeHeap=").append(freeHeap);
+ sb.append(", usedHeap=").append(usedHeap);
+ sb.append(", heapUtilization=").append(heapUtilization);
+ sb.append(", freeNonHeap=").append(freeNonHeap);
+ sb.append(", usedNonHeap=").append(usedNonHeap);
+ sb.append(", openFileHandlers=").append(openFileHandlers);
+ sb.append(", processorLoadAverage=").append(processorLoadAverage);
+ sb.append(", totalThreads=").append(totalThreads);
+ sb.append(", timerDrivenThreads=").append(timerDrivenThreads);
+ sb.append(", flowFileRepositoryFreeSpace=").append(flowFileRepositoryFreeSpace);
+ sb.append(", flowFileRepositoryUsedSpace=").append(flowFileRepositoryUsedSpace);
+ sb.append(", contentRepositories=").append(contentRepositories);
+ sb.append(", provenanceRepositories=").append(provenanceRepositories);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/apache/nifi/controller/status/PortStatus.java b/src/main/java/org/apache/nifi/controller/status/PortStatus.java
new file mode 100644
index 0000000..0d248cd
--- /dev/null
+++ b/src/main/java/org/apache/nifi/controller/status/PortStatus.java
@@ -0,0 +1,201 @@
+/*
+ * 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.controller.status;
+
+/**
+ * The status of a port.
+ */
+public class PortStatus implements Cloneable {
+
+ private String id;
+ private String groupId;
+ private String name;
+ private Integer activeThreadCount;
+ private int inputCount;
+ private long inputBytes;
+ private int outputCount;
+ private long outputBytes;
+ private int flowFilesReceived;
+ private long bytesReceived;
+ private int flowFilesSent;
+ private long bytesSent;
+ private Boolean transmitting;
+ private RunStatus runStatus;
+
+ public Boolean isTransmitting() {
+ return transmitting;
+ }
+
+ public void setTransmitting(Boolean transmitting) {
+ this.transmitting = transmitting;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getActiveThreadCount() {
+ return activeThreadCount;
+ }
+
+ public void setActiveThreadCount(Integer activeThreadCount) {
+ this.activeThreadCount = activeThreadCount;
+ }
+
+ public int getInputCount() {
+ return inputCount;
+ }
+
+ public void setInputCount(int inputCount) {
+ this.inputCount = inputCount;
+ }
+
+ public long getInputBytes() {
+ return inputBytes;
+ }
+
+ public void setInputBytes(long inputBytes) {
+ this.inputBytes = inputBytes;
+ }
+
+ public int getOutputCount() {
+ return outputCount;
+ }
+
+ public void setOutputCount(final int outputCount) {
+ this.outputCount = outputCount;
+ }
+
+ public long getOutputBytes() {
+ return outputBytes;
+ }
+
+ public void setOutputBytes(final long outputBytes) {
+ this.outputBytes = outputBytes;
+ }
+
+ public RunStatus getRunStatus() {
+ return runStatus;
+ }
+
+ public void setRunStatus(RunStatus runStatus) {
+ this.runStatus = runStatus;
+ }
+
+ public int getFlowFilesReceived() {
+ return flowFilesReceived;
+ }
+
+ public void setFlowFilesReceived(int flowFilesReceived) {
+ this.flowFilesReceived = flowFilesReceived;
+ }
+
+ public long getBytesReceived() {
+ return bytesReceived;
+ }
+
+ public void setBytesReceived(long bytesReceived) {
+ this.bytesReceived = bytesReceived;
+ }
+
+ public int getFlowFilesSent() {
+ return flowFilesSent;
... 20258 lines suppressed ...