Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 <T> the type of the data payload
*/
public final class ChangeSet<T> {
private final ChangeSetType type;
private final Selector selector;
private final String environmentId;
private final T data;
private final boolean shouldPersist;

/**
* Constructs a new ChangeSet.
* <p>
* 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 + ")";
}
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.launchdarkly.sdk.fdv2;

import java.util.Objects;

/**
* Identifies a specific version of data in the LaunchDarkly backend, used to request incremental
* updates from a known point.
* <p>
* 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.
* <p>
* <strong>For SDK consumers implementing custom data sources:</strong> 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.
* <p>
* <strong>This method is intended for use by LaunchDarkly data sources only.</strong>
* 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();

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Selector)) return false;
Selector other = (Selector) o;
return isEmpty == other.isEmpty
&& version == other.version
&& Objects.equals(state, other.state);
}

@Override
public int hashCode() {
return Objects.hash(isEmpty, version, state);
}

@Override
public String toString() {
return isEmpty ? "Selector(empty)" : "Selector(" + version + "," + state + ")";
}
}
Original file line number Diff line number Diff line change
@@ -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,
}
Copy link
Contributor Author

@tanderson-ld tanderson-ld Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers: Renamed this from FDv2SourceResult.State to SourceSignal to reduce similarity to a very similar DataSourceStatusProvider enum that already exists that talks about data source state.

  /**
   * An enumeration of possible values for {@link DataSourceStatusProvider.Status#getState()}.
   */
  public enum State {

Original file line number Diff line number Diff line change
@@ -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,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Types for the FDv2 (Flag Delivery v2).
* <p>
* This package contains the public types used by SDK data source implementations that support
* the FDv2 data sources.
*/
package com.launchdarkly.sdk.fdv2;