This is an automated email from the ASF dual-hosted git repository.

markap14 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-api.git


The following commit(s) were added to refs/heads/main by this push:
     new d7d0a74  NIFI-16470: Add a flow evolution hook and persist the managed 
flow. (#111)
d7d0a74 is described below

commit d7d0a743a9d763182eada2764a4427007adca553
Author: Bob Paulin <[email protected]>
AuthorDate: Fri Oct 16 15:53:01 2026 -0500

    NIFI-16470: Add a flow evolution hook and persist the managed flow. (#111)
    
    prepareForFlowEvolution runs when the Connector version changes, while
    draining. The managed group, its bundle, and its parameter context are
    persisted.
---
 .../nifi/components/connector/Connector.java       | 20 +++++++++--
 .../components/connector/FlowEvolutionContext.java | 37 +++++++++++++++++++
 .../org/apache/nifi/flow/VersionedConnector.java   | 42 ++++++++++++++++++----
 3 files changed, 89 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/apache/nifi/components/connector/Connector.java 
b/src/main/java/org/apache/nifi/components/connector/Connector.java
index c1f696a..853db0d 100644
--- a/src/main/java/org/apache/nifi/components/connector/Connector.java
+++ b/src/main/java/org/apache/nifi/components/connector/Connector.java
@@ -93,11 +93,11 @@ public interface Connector {
      * </p>
      *
      * <p>
-     * This method must be idempotent and free of side effects.
+     * Build the flow from the given context's configuration, not from the 
live group. The method must be idempotent and
+     * must not change the flow it returns.
      * </p>
      *
-     * @param activeFlowContext the Connector's current Active Flow Context, 
used as the source of configuration
-     *                          from which the returned flow is built
+     * @param activeFlowContext the context whose configuration the flow is 
built from
      * @return the Connector's authoritative view of the flow for the given 
context
      */
     VersionedExternalFlow getActiveFlow(FlowContext activeFlowContext);
@@ -239,6 +239,20 @@ public interface Connector {
      */
     List<DescribedValue> fetchAllowableValues(String stepName, String 
propertyName, FlowContext flowContext, String filter);
 
+    /**
+     * Called when a change is detected in the Connector version, while 
draining and before the new flow replaces the
+     * Active flow. Return a Future that completes when preparation is 
finished; the framework may cancel it. The
+     * default implementation does nothing. Not called when the Connector 
leaves Troubleshooting; the managed flow must
+     * be empty then.
+     *
+     * @param context the pre-upgrade flow and the upgraded bundle
+     * @return a Future that completes when preparation is finished
+     * @throws FlowUpdateException if the pre-upgrade flow cannot be prepared
+     */
+    default CompletableFuture<Void> prepareForFlowEvolution(final 
FlowEvolutionContext context) throws FlowUpdateException {
+        return CompletableFuture.completedFuture(null);
+    }
+
     /**
      * Drains any in-flight FlowFiles from the flow associated with the given 
Flow Context by processing the existing data
      * but not accepting any new data.
diff --git 
a/src/main/java/org/apache/nifi/components/connector/FlowEvolutionContext.java 
b/src/main/java/org/apache/nifi/components/connector/FlowEvolutionContext.java
new file mode 100644
index 0000000..05006f3
--- /dev/null
+++ 
b/src/main/java/org/apache/nifi/components/connector/FlowEvolutionContext.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.connector;
+
+import org.apache.nifi.components.connector.components.FlowContext;
+import org.apache.nifi.flow.Bundle;
+
+/**
+ * Context for {@link Connector#prepareForFlowEvolution(FlowEvolutionContext)} 
during an upgrade drain.
+ */
+public interface FlowEvolutionContext {
+
+    /**
+     * @return the pre-upgrade Active flow, with configuration already migrated
+     */
+    FlowContext getPreUpgradeActiveFlowContext();
+
+    /**
+     * @return the bundle that installs the upgraded flow
+     */
+    Bundle getPostUpgradeBundle();
+}
diff --git a/src/main/java/org/apache/nifi/flow/VersionedConnector.java 
b/src/main/java/org/apache/nifi/flow/VersionedConnector.java
index 064bd58..2629fee 100644
--- a/src/main/java/org/apache/nifi/flow/VersionedConnector.java
+++ b/src/main/java/org/apache/nifi/flow/VersionedConnector.java
@@ -27,7 +27,9 @@ public class VersionedConnector {
     private List<VersionedConfigurationStep> workingFlowConfiguration;
     private String type;
     private Bundle bundle;
+    private Bundle managedFlowBundle;
     private VersionedProcessGroup managedProcessGroup;
+    private VersionedParameterContext managedParameterContext;
 
     public String getInstanceIdentifier() {
         return instanceIdentifier;
@@ -77,6 +79,11 @@ public class VersionedConnector {
         this.type = type;
     }
 
+    /**
+     * Returns the Connector bundle loaded on restart.
+     *
+     * @return the Connector code to load, or {@code null} if none was 
persisted
+     */
     public Bundle getBundle() {
         return bundle;
     }
@@ -86,14 +93,24 @@ public class VersionedConnector {
     }
 
     /**
-     * Returns the contents of the Connector's Managed Process Group. This 
field is populated only when the Connector is
-     * in Troubleshooting mode at the time the flow is persisted, so that any 
user modifications made while in Troubleshooting
-     * survive a restart. For any other Connector state, this field is {@code 
null} and the Managed Process Group's contents
-     * are reconstructed by re-applying the configuration steps upon restore. 
If the Connector's scheduled state is not
-     * {@link VersionedConnectorState#TROUBLESHOOTING TROUBLESHOOTING}, this 
field is ignored.
+     * Returns the bundle that built {@link #getManagedProcessGroup()}. 
Differs from {@link #getBundle()} only while an
+     * upgrade is pending.
+     *
+     * @return the bundle that built the flow, or {@code null} if it was not 
persisted
+     */
+    public Bundle getManagedFlowBundle() {
+        return managedFlowBundle;
+    }
+
+    public void setManagedFlowBundle(final Bundle managedFlowBundle) {
+        this.managedFlowBundle = managedFlowBundle;
+    }
+
+    /**
+     * Returns the last applied flow. While the Connector is in 
Troubleshooting, this holds the user's edits so they
+     * survive a restart.
      *
-     * @return the persisted contents of the Managed Process Group, or {@code 
null} if the Connector was not in
-     *         Troubleshooting mode when the flow was persisted
+     * @return the last applied flow, or {@code null} if none was persisted
      */
     public VersionedProcessGroup getManagedProcessGroup() {
         return managedProcessGroup;
@@ -102,4 +119,15 @@ public class VersionedConnector {
     public void setManagedProcessGroup(final VersionedProcessGroup 
managedProcessGroup) {
         this.managedProcessGroup = managedProcessGroup;
     }
+
+    /**
+     * @return the parameter context for the managed group, or {@code null} if 
none was persisted
+     */
+    public VersionedParameterContext getManagedParameterContext() {
+        return managedParameterContext;
+    }
+
+    public void setManagedParameterContext(final VersionedParameterContext 
managedParameterContext) {
+        this.managedParameterContext = managedParameterContext;
+    }
 }

Reply via email to