Skip to content

Commit f1e677a

Browse files
authored
NIFI-15988: Split MigratableConnector's migrate method into two methods: migrateConfiguration, migrateState (#92)
Signed-off-by: Kevin Doran <kdoran@apache.org>
1 parent 6913182 commit f1e677a

2 files changed

Lines changed: 94 additions & 55 deletions

File tree

src/main/java/org/apache/nifi/components/connector/migration/ConnectorMigrationContext.java

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,29 @@
1818
package org.apache.nifi.components.connector.migration;
1919

2020
import org.apache.nifi.components.connector.AssetReference;
21+
import org.apache.nifi.components.connector.ConfigurationStep;
22+
import org.apache.nifi.components.connector.ConnectorInitializationContext;
2123
import org.apache.nifi.components.connector.components.FlowContext;
24+
import org.apache.nifi.flow.VersionedComponentState;
2225
import org.apache.nifi.flow.VersionedExternalFlow;
2326

27+
import java.util.Map;
28+
2429
/**
25-
* Context provided to a Connector when evaluating or performing migration from a Versioned Process Group export. The
26-
* source flow exposed through this context is a read-only reference that the Connector uses to update its own managed
27-
* flow; the source flow itself is never installed onto the Connector.
30+
* Migration context for a {@link MigratableConnector}.
31+
*
32+
* <p>
33+
* The source flow is read-only and is never installed directly. Write methods are phase-scoped:
34+
* {@link #setProperties(String, Map)} and {@link #replaceProperties(String, Map)} are valid only during
35+
* {@link MigratableConnector#migrateConfiguration(ConnectorMigrationContext)}, and
36+
* {@link #setComponentState(String, VersionedComponentState)} is valid only during
37+
* {@link MigratableConnector#migrateState(ConnectorMigrationContext)}.
38+
* </p>
39+
*
40+
* <p>
41+
* Calling {@link ConnectorInitializationContext#updateFlow(FlowContext, VersionedExternalFlow) updateFlow(...)}
42+
* through this context throws.
43+
* </p>
2844
*/
2945
public interface ConnectorMigrationContext {
3046

@@ -45,8 +61,9 @@ public interface ConnectorMigrationContext {
4561

4662
/**
4763
* Returns the active flow context for the Connector being migrated.
64+
* Calling {@code updateFlow(...)} through this context throws.
4865
*
49-
* @return the active flow context
66+
* @return active flow context
5067
*/
5168
FlowContext getActiveFlowContext();
5269

@@ -72,4 +89,35 @@ public interface ConnectorMigrationContext {
7289
* available when the migration source is a local Versioned Process Group
7390
*/
7491
AssetReference copyAssetFromSource(String sourceAssetId);
92+
93+
/**
94+
* Records configuration properties to merge into the named {@link ConfigurationStep}.
95+
* A {@code null} value removes the property.
96+
*
97+
* @param stepName configuration step name
98+
* @param propertyValues properties to record
99+
* @throws IllegalStateException when called outside {@code migrateConfiguration(...)}
100+
*/
101+
void setProperties(String stepName, Map<String, String> propertyValues);
102+
103+
/**
104+
* Records configuration properties that replace the named {@link ConfigurationStep}.
105+
* Properties not included in {@code propertyValues} are removed.
106+
*
107+
* @param stepName configuration step name
108+
* @param propertyValues properties to record
109+
* @throws IllegalStateException when called outside {@code migrateConfiguration(...)}
110+
*/
111+
void replaceProperties(String stepName, Map<String, String> propertyValues);
112+
113+
/**
114+
* Records the {@link VersionedComponentState} for a managed component.
115+
* Repeated calls for the same component replace prior recorded state; an empty state clears previously recorded state.
116+
*
117+
* @param managedComponentId managed Processor or Controller Service versioned identifier
118+
* @param state state to write
119+
* @throws IllegalArgumentException when {@code managedComponentId} is blank or {@code state} is {@code null}
120+
* @throws IllegalStateException when called outside {@code migrateState(...)}
121+
*/
122+
void setComponentState(String managedComponentId, VersionedComponentState state);
75123
}

src/main/java/org/apache/nifi/components/connector/migration/MigratableConnector.java

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,72 +21,63 @@
2121
import org.apache.nifi.components.connector.ConnectorInitializationContext;
2222
import org.apache.nifi.components.connector.FlowUpdateException;
2323
import org.apache.nifi.components.connector.components.FlowContext;
24-
import org.apache.nifi.flow.VersionedExternalFlow;
24+
import org.apache.nifi.flow.VersionedComponentState;
2525

2626
/**
27+
* Optional {@link Connector} capability for migration from a source flow.
28+
*
29+
* <p>
30+
* Migration runs in two phases:
31+
* </p>
32+
* <ol>
33+
* <li>{@link #migrateConfiguration(ConnectorMigrationContext)} records configuration changes and copied assets.</li>
34+
* <li>{@link #migrateState(ConnectorMigrationContext)} records component {@link VersionedComponentState}.</li>
35+
* </ol>
36+
*
2737
* <p>
28-
* An optional capability interface that may be implemented by a {@link Connector} to indicate that it supports
29-
* being populated from an existing source flow (for example, a Versioned Process Group already running on this
30-
* NiFi instance, or an uploaded flow definition). The framework discovers this capability by checking whether a
31-
* Connector is an instance of {@code MigratableConnector}; Connectors that do not implement this interface are
32-
* never offered as migration targets.
38+
* Connectors must not call
39+
* {@link ConnectorInitializationContext#updateFlow(FlowContext, org.apache.nifi.flow.VersionedExternalFlow)
40+
* updateFlow(...)} during migration. The framework applies recorded configuration by calling
41+
* {@link Connector#applyUpdate(FlowContext, FlowContext) applyUpdate(...)} between phases, then writes recorded
42+
* state. If any phase fails, migration is rolled back.
3343
* </p>
3444
*
35-
* <b>Implementation Note:</b> This API is currently experimental, as it is under very active development. As such,
36-
* it is subject to change without notice between releases.
45+
* <p>
46+
* Sensitive values are not included in the source flow and must be configured by the user after migration.
47+
* </p>
48+
*
49+
* <p>
50+
* <b>Implementation Note:</b> This API is experimental and may change between releases.
51+
* </p>
3752
*/
3853
public interface MigratableConnector {
3954

4055
/**
41-
* Indicates whether this Connector can be migrated from the source flow described by the given context.
42-
*
43-
* <p>
44-
* Implementations should inspect the source flow structure and metadata using
45-
* {@link ConnectorMigrationContext#getSourceFlow()} and return quickly without mutating the Connector or the
46-
* source flow. This method must not call {@link ConnectorMigrationContext#copyAssetFromSource(String)}.
47-
* </p>
56+
* Returns whether this Connector supports migration from the source flow in the given context.
57+
* This method is read-only and must not call context write methods.
4858
*
49-
* @param context the migration context describing the source flow and target Connector
50-
* @return {@code true} when this Connector can be migrated from the provided source flow
59+
* @param context migration context
60+
* @return {@code true} when migration is supported
5161
*/
5262
boolean isMigrationSupported(ConnectorMigrationContext context);
5363

5464
/**
55-
* Migrates this Connector by updating its own managed flow to mirror the configuration, parameters, and component
56-
* state captured in the provided source flow. The source flow is a reference: it is read, not modified, and is not
57-
* installed onto the Connector. The Connector remains the owner of its flow and is responsible for translating the
58-
* source into its own representation.
59-
*
60-
* <p>
61-
* The framework guarantees the following preconditions when this method is invoked:
62-
* </p>
63-
* <ul>
64-
* <li>The Connector is stopped.</li>
65-
* <li>The Connector has not had any configuration changes applied by the user and has not been started.</li>
66-
* </ul>
67-
*
68-
* <p>
69-
* Because of these preconditions, the implementation updates the active {@link FlowContext} directly rather than
70-
* making use of {@code prepareForUpdate} and {@code applyUpdate}. Those two lifecycle methods exist to safely
71-
* transition a running Connector from one active configuration to another; for migration, the Connector is
72-
* already required to be in the target-safe state, so the working-to-active swap is unnecessary.
73-
* </p>
65+
* First migration phase. Record configuration changes using
66+
* {@link ConnectorMigrationContext#setProperties(String, java.util.Map)} or
67+
* {@link ConnectorMigrationContext#replaceProperties(String, java.util.Map)}, and copy assets as needed.
68+
* The framework calls {@link Connector#applyUpdate(FlowContext, FlowContext)} after this method returns.
7469
*
75-
* <p>
76-
* Implementations are responsible for transforming the source flow, updating the active {@link FlowContext}, and
77-
* applying any parameter or step configuration changes needed by the Connector. Sensitive parameter values are not
78-
* present in the source flow and must be left for the user to configure after the migration completes.
79-
* </p>
80-
*
81-
* <p>
82-
* Connectors that extend {@code AbstractConnector} can typically retain their {@link ConnectorInitializationContext}
83-
* from {@code initialize(ConnectorInitializationContext)} and call
84-
* {@link ConnectorInitializationContext#updateFlow(FlowContext, VersionedExternalFlow)} using
85-
* {@link ConnectorMigrationContext#getActiveFlowContext()}.
86-
* </p>
70+
* @param context migration context
71+
* @throws FlowUpdateException when migration fails
72+
*/
73+
void migrateConfiguration(ConnectorMigrationContext context) throws FlowUpdateException;
74+
75+
/**
76+
* Second migration phase. This method is invoked after the framework rebuilds the managed flow from configuration.
77+
* Record component state using {@link ConnectorMigrationContext#setComponentState(String, VersionedComponentState)}.
8778
*
88-
* @param context the migration context describing the source flow and target Connector
89-
* @throws FlowUpdateException when the migration cannot be completed successfully
79+
* @param context migration context
80+
* @throws FlowUpdateException when state migration fails
9081
*/
91-
void migrate(ConnectorMigrationContext context) throws FlowUpdateException;
82+
void migrateState(ConnectorMigrationContext context) throws FlowUpdateException;
9283
}

0 commit comments

Comments
 (0)