From 7d6d7d8ecb2213256b5f9aa85a51947d7c2b4f0d Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Thu, 26 Feb 2026 13:57:52 -0500 Subject: [PATCH 1/3] chore: commonizes several FDv2 related types --- .../com/launchdarkly/sdk/fdv2/ChangeSet.java | 104 ++++++++++++++++++ .../launchdarkly/sdk/fdv2/ChangeSetType.java | 21 ++++ .../com/launchdarkly/sdk/fdv2/Selector.java | 78 +++++++++++++ .../sdk/fdv2/SourceResultType.java | 18 +++ .../launchdarkly/sdk/fdv2/SourceSignal.java | 29 +++++ .../launchdarkly/sdk/fdv2/package-info.java | 7 ++ 6 files changed, 257 insertions(+) create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSet.java create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSetType.java create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/Selector.java create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceResultType.java create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceSignal.java create mode 100644 lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSet.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSet.java new file mode 100644 index 00000000..20c77bf0 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSet.java @@ -0,0 +1,104 @@ +package com.launchdarkly.sdk.fdv2; + +import java.util.Objects; + +/** + * Represents a set of changes to apply to a data store. + * + * @param the type of the data payload + */ +public final class ChangeSet { + private final ChangeSetType type; + private final Selector selector; + private final String environmentId; + private final T data; + private final boolean shouldPersist; + + /** + * Constructs a new ChangeSet. + *

+ * When implementing a custom data source, pass {@link Selector#EMPTY} for the {@code selector} + * parameter. Non-empty selectors are only meaningful for LaunchDarkly's own data sources. + * + * @param type the type of the changeset + * @param selector the selector for this change; null is normalized to {@link Selector#EMPTY} + * @param data the data payload + * @param environmentId the environment ID, or null if not available + * @param shouldPersist true if the data should be persisted to persistent stores + */ + public ChangeSet(ChangeSetType type, Selector selector, T data, String environmentId, boolean shouldPersist) { + this.type = type; + this.selector = selector != null ? selector : Selector.EMPTY; + this.data = data; + this.environmentId = environmentId; + this.shouldPersist = shouldPersist; + } + + /** + * Returns the type of the changeset. + * + * @return the changeset type + */ + public ChangeSetType getType() { + return type; + } + + /** + * Returns the selector for this change. Will not be null; may be {@link Selector#EMPTY}. + * + * @return the selector + */ + public Selector getSelector() { + return selector; + } + + /** + * Returns the environment ID associated with the change, or null if not available. + * + * @return the environment ID, or null + */ + public String getEnvironmentId() { + return environmentId; + } + + /** + * Returns the data payload for this changeset. + * + * @return the data + */ + public T getData() { + return data; + } + + /** + * Returns whether this data should be persisted to persistent stores. + * + * @return true if the data should be persisted, false otherwise + */ + public boolean shouldPersist() { + return shouldPersist; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ChangeSet) { + ChangeSet other = (ChangeSet) o; + return type == other.type + && shouldPersist == other.shouldPersist + && Objects.equals(selector, other.selector) + && Objects.equals(environmentId, other.environmentId) + && Objects.equals(data, other.data); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(type, selector, environmentId, data, shouldPersist); + } + + @Override + public String toString() { + return "ChangeSet(" + type + "," + selector + "," + environmentId + "," + data + "," + shouldPersist + ")"; + } +} diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSetType.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSetType.java new file mode 100644 index 00000000..68ecc7f4 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/ChangeSetType.java @@ -0,0 +1,21 @@ +package com.launchdarkly.sdk.fdv2; + +/** + * Indicates the type of a change set applied to a data store. + */ +public enum ChangeSetType { + /** + * Represents a full store update which replaces all data currently in the store. + */ + Full, + + /** + * Represents an incremental set of changes to be applied to the existing data in the store. + */ + Partial, + + /** + * Indicates that there are no changes; the changeset may still carry a selector to store. + */ + None +} diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/Selector.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/Selector.java new file mode 100644 index 00000000..b48e80c7 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/Selector.java @@ -0,0 +1,78 @@ +package com.launchdarkly.sdk.fdv2; + +/** + * Identifies a specific version of data in the LaunchDarkly backend, used to request incremental + * updates from a known point. + *

+ * A selector is either empty ({@link #EMPTY}) or contains a version number and state string that + * were provided by a LaunchDarkly data source. Empty selectors signal that the client has no + * existing data and requires a full payload. + *

+ * For SDK consumers implementing custom data sources: you should always use + * {@link #EMPTY} when constructing a {@link ChangeSet}. Non-empty selectors are set by + * LaunchDarkly's own data sources based on state received from the LaunchDarkly backend, and + * are not meaningful when constructed externally. + */ +public final class Selector { + private final boolean isEmpty; + private final int version; + private final String state; + + private Selector(int version, String state, boolean isEmpty) { + this.version = version; + this.state = state; + this.isEmpty = isEmpty; + } + + /** + * If true, then this selector is empty. An empty selector cannot be used as a basis for + * requesting incremental updates from a data source. + * + * @return whether the selector is empty + */ + public boolean isEmpty() { + return isEmpty; + } + + /** + * The version of the data associated with this selector. + * + * @return the version + */ + public int getVersion() { + return version; + } + + /** + * The state associated with the payload. + * + * @return the state identifier, or null if empty + */ + public String getState() { + return state; + } + + static Selector empty() { + return new Selector(0, null, true); + } + + /** + * Creates a new Selector with the given version and state. + *

+ * This method is intended for use by LaunchDarkly data sources only. + * Custom data source implementations should use {@link #EMPTY} instead. + * + * @param version the version number + * @param state the state identifier + * @return a new Selector instance + */ + public static Selector make(int version, String state) { + return new Selector(version, state, false); + } + + /** + * An empty selector instance. Custom data source implementations should always use this + * value when constructing a {@link ChangeSet}. + */ + public static final Selector EMPTY = empty(); +} diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceResultType.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceResultType.java new file mode 100644 index 00000000..7ea7ce58 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceResultType.java @@ -0,0 +1,18 @@ +package com.launchdarkly.sdk.fdv2; + +/** + * Indicates whether an FDv2 source result carries a change set or a status update. + */ +public enum SourceResultType { + /** + * The source has emitted a change set. This implies that the source is in a valid state. + */ + CHANGE_SET, + + /** + * The source is emitting a status update, indicating a transition from being valid to being + * in some kind of error or non-operational state. The source will emit a {@link #CHANGE_SET} + * if it becomes valid again. + */ + STATUS, +} diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceSignal.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceSignal.java new file mode 100644 index 00000000..24fe0bb1 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/SourceSignal.java @@ -0,0 +1,29 @@ +package com.launchdarkly.sdk.fdv2; + +/** + * Represents the state of an FDv2 data source when emitting a status result. + */ +public enum SourceSignal { + /** + * The data source has encountered an interruption and will attempt to reconnect. This is not + * intended to be used with an initializer; use {@link #TERMINAL_ERROR} instead. When used with + * an initializer it will still be treated as a terminal state. + */ + INTERRUPTED, + + /** + * The data source has been shut down and will not produce any further results. + */ + SHUTDOWN, + + /** + * The data source has encountered a terminal error and will not produce any further results. + */ + TERMINAL_ERROR, + + /** + * The data source has been instructed to disconnect (e.g. the server sent a goodbye message) + * and will not produce any further results. + */ + GOODBYE, +} diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java new file mode 100644 index 00000000..09719056 --- /dev/null +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java @@ -0,0 +1,7 @@ +/** + * Types for the FDv2 (Flag Delivery v2) data source protocol. + *

+ * This package contains the public types used by SDK data source implementations that support + * the FDv2 data sources. + */ +package com.launchdarkly.sdk.fdv2; From 143146097bc77c71ca399f9dd84363a7c7ff6e4d Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Thu, 26 Feb 2026 14:00:07 -0500 Subject: [PATCH 2/3] chore: commonizes several FDv2 related types --- .../buildSrc/src/main/kotlin/Dependencies.kt | 2 +- .../internal/fdv2/sources/FDv2ChangeSet.java | 1 + .../fdv2/sources/FDv2ProtocolHandler.java | 1 + .../sdk/internal/fdv2/sources/Selector.java | 65 ------------------- 4 files changed, 3 insertions(+), 66 deletions(-) delete mode 100644 lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/Selector.java diff --git a/lib/shared/internal/buildSrc/src/main/kotlin/Dependencies.kt b/lib/shared/internal/buildSrc/src/main/kotlin/Dependencies.kt index 747c2df3..89a5f147 100644 --- a/lib/shared/internal/buildSrc/src/main/kotlin/Dependencies.kt +++ b/lib/shared/internal/buildSrc/src/main/kotlin/Dependencies.kt @@ -4,7 +4,7 @@ object Versions { const val gson = "2.13.1" - const val launchdarklyJavaSdkCommon = "2.1.2" + const val launchdarklyJavaSdkCommon = "2.4.0" const val launchdarklyLogging = "1.1.1" const val okhttp = "4.12.0" const val testHelpers = "1.2.0" diff --git a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ChangeSet.java b/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ChangeSet.java index 8ffc69e1..41d8eb4b 100644 --- a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ChangeSet.java +++ b/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ChangeSet.java @@ -1,6 +1,7 @@ package com.launchdarkly.sdk.internal.fdv2.sources; import com.google.gson.JsonElement; +import com.launchdarkly.sdk.fdv2.Selector; import java.util.Collections; import java.util.List; diff --git a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ProtocolHandler.java b/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ProtocolHandler.java index 5bdd7fc8..a00b48a8 100644 --- a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ProtocolHandler.java +++ b/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/FDv2ProtocolHandler.java @@ -1,5 +1,6 @@ package com.launchdarkly.sdk.internal.fdv2.sources; +import com.launchdarkly.sdk.fdv2.Selector; import com.launchdarkly.sdk.internal.fdv2.payloads.DeleteObject; import com.launchdarkly.sdk.internal.fdv2.payloads.Error; import com.launchdarkly.sdk.internal.fdv2.payloads.FDv2Event; diff --git a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/Selector.java b/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/Selector.java deleted file mode 100644 index 5d13844c..00000000 --- a/lib/shared/internal/src/main/java/com/launchdarkly/sdk/internal/fdv2/sources/Selector.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.launchdarkly.sdk.internal.fdv2.sources; - -/** - * A selector can either be empty or it can contain state and a version. - */ -public final class Selector { - private final boolean isEmpty; - private final int version; - private final String state; - - private Selector(int version, String state, boolean isEmpty) { - this.version = version; - this.state = state; - this.isEmpty = isEmpty; - } - - /** - * If true, then this selector is empty. An empty selector cannot be used as a basis for a data source. - * - * @return whether the selector is empty - */ - public boolean isEmpty() { - return isEmpty; - } - - /** - * The version of the data associated with this selector. - * - * @return the version - */ - public int getVersion() { - return version; - } - - /** - * The state associated with the payload. - * - * @return the state identifier, or null if empty - */ - public String getState() { - return state; - } - - static Selector empty() { - return new Selector(0, null, true); - } - - /** - * Creates a new Selector with the given version and state. - * - * @param version the version number - * @param state the state identifier - * @return a new Selector instance - */ - public static Selector make(int version, String state) { - return new Selector(version, state, false); - } - - /** - * An empty selector instance. - */ - public static final Selector EMPTY = empty(); -} - - From 430c038f6f20b4c2d9a01c834058d937559a946d Mon Sep 17 00:00:00 2001 From: Todd Anderson Date: Thu, 26 Feb 2026 15:36:43 -0500 Subject: [PATCH 3/3] tweaking package-info.java --- .../src/main/java/com/launchdarkly/sdk/fdv2/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java index 09719056..a14856aa 100644 --- a/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java +++ b/lib/shared/common/src/main/java/com/launchdarkly/sdk/fdv2/package-info.java @@ -1,5 +1,5 @@ /** - * Types for the FDv2 (Flag Delivery v2) data source protocol. + * Types for the FDv2 (Flag Delivery v2). *

* This package contains the public types used by SDK data source implementations that support * the FDv2 data sources.