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:51 UTC
(nifi-api) 01/01: NIFI-13732 Initial commit of nifi-api 2.0.0-SNAPSHOT
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 ...