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
Expand Up @@ -4,15 +4,23 @@

import java.net.URI;

abstract class StandardEndpoints {
/**
* Internal utility class for resolving service endpoint URIs.
* <p>
* This class is for internal LaunchDarkly SDK use only. It is public only because it needs to be
* accessible to SDK code in different packages. It is not part of the public supported API and
* should not be referenced by application code. This class is subject to change without notice.
* </p>
*/
public abstract class StandardEndpoints {
private StandardEndpoints() {}

static final URI DEFAULT_STREAMING_BASE_URI = URI.create("https://stream.launchdarkly.com");
static final URI DEFAULT_POLLING_BASE_URI = URI.create("https://app.launchdarkly.com");
static final URI DEFAULT_EVENTS_BASE_URI = URI.create("https://events.launchdarkly.com");
public static final URI DEFAULT_STREAMING_BASE_URI = URI.create("https://stream.launchdarkly.com");
public static final URI DEFAULT_POLLING_BASE_URI = URI.create("https://app.launchdarkly.com");
public static final URI DEFAULT_EVENTS_BASE_URI = URI.create("https://events.launchdarkly.com");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For reviewers: Made public for describeConfiguration in the data source builders.


static final String STREAMING_REQUEST_PATH = "/all";
static final String POLLING_REQUEST_PATH = "/sdk/latest-all";
public static final String STREAMING_REQUEST_PATH = "/all";
public static final String POLLING_REQUEST_PATH = "/sdk/latest-all";

/**
* Internal method to decide which URI a given component should connect to.
Expand All @@ -26,7 +34,7 @@ private StandardEndpoints() {}
* @param logger the logger to which we should print the warning, if needed
* @return the base URI we should connect to
*/
static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String description, LDLogger logger) {
public static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String description, LDLogger logger) {
if (serviceEndpointsValue != null) {
return serviceEndpointsValue;
}
Expand All @@ -46,7 +54,7 @@ static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String des
* @param defaultValue the constant default URI value defined in StandardEndpoints
* @return true iff the base URI was customized
*/
static boolean isCustomBaseUri(URI serviceEndpointsValue, URI defaultValue) {
public static boolean isCustomBaseUri(URI serviceEndpointsValue, URI defaultValue) {
return serviceEndpointsValue != null && !serviceEndpointsValue.equals(defaultValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.launchdarkly.sdk.server.integrations;

import com.google.common.collect.ImmutableList;
import com.launchdarkly.sdk.server.subsystems.ComponentConfigurer;
import com.launchdarkly.sdk.server.subsystems.DataSource;
import com.launchdarkly.sdk.server.subsystems.DataStore;
import com.launchdarkly.sdk.server.subsystems.DataSystemConfiguration;

import java.util.ArrayList;
import java.util.List;

/**
* Configuration builder for the SDK's data acquisition and storage strategy.
* <p>
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
* It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
* </p>
*/
public final class DataSystemBuilder {

private final List<ComponentConfigurer<DataSource>> initializers = new ArrayList<>();
private final List<ComponentConfigurer<DataSource>> synchronizers = new ArrayList<>();
private ComponentConfigurer<DataSource> fDv1FallbackSynchronizer;
private ComponentConfigurer<DataStore> persistentStore;
private DataSystemConfiguration.DataStoreMode persistentDataStoreMode;

/**
* Add one or more initializers to the builder.
* To replace initializers, please refer to {@link #replaceInitializers(ComponentConfigurer[])}.
*
* @param initializers the initializers to add
* @return a reference to the builder
*/
public DataSystemBuilder initializers(ComponentConfigurer<DataSource>... initializers) {
for (ComponentConfigurer<DataSource> initializer : initializers) {
this.initializers.add(initializer);
}
return this;
}

/**
* Replaces any existing initializers with the given initializers.
* To add initializers, please refer to {@link #initializers(ComponentConfigurer[])}.
*
* @param initializers the initializers to replace the current initializers with
* @return a reference to this builder
*/
public DataSystemBuilder replaceInitializers(ComponentConfigurer<DataSource>... initializers) {
this.initializers.clear();
for (ComponentConfigurer<DataSource> initializer : initializers) {
this.initializers.add(initializer);
}
return this;
}

/**
* Add one or more synchronizers to the builder.
* To replace synchronizers, please refer to {@link #replaceSynchronizers(ComponentConfigurer[])}.
*
* @param synchronizers the synchronizers to add
* @return a reference to the builder
*/
public DataSystemBuilder synchronizers(ComponentConfigurer<DataSource>... synchronizers) {
for (ComponentConfigurer<DataSource> synchronizer : synchronizers) {
this.synchronizers.add(synchronizer);
}
return this;
}

/**
* Replaces any existing synchronizers with the given synchronizers.
* To add synchronizers, please refer to {@link #synchronizers(ComponentConfigurer[])}.
*
* @param synchronizers the synchronizers to replace the current synchronizers with
* @return a reference to this builder
*/
public DataSystemBuilder replaceSynchronizers(ComponentConfigurer<DataSource>... synchronizers) {
this.synchronizers.clear();
for (ComponentConfigurer<DataSource> synchronizer : synchronizers) {
this.synchronizers.add(synchronizer);
}
return this;
}

/**
* Configure the FDv1 fallback synchronizer.
* <p>
* LaunchDarkly can instruct the SDK to fall back to this synchronizer.
* </p>
*
* @param fDv1FallbackSynchronizer the FDv1 fallback synchronizer
* @return a reference to the builder
*/
public DataSystemBuilder fDv1FallbackSynchronizer(ComponentConfigurer<DataSource> fDv1FallbackSynchronizer) {
this.fDv1FallbackSynchronizer = fDv1FallbackSynchronizer;
return this;
}

/**
* Configures the persistent data store.
* <p>
* The SDK will use the persistent data store to store feature flag data.
* </p>
*
* @param persistentStore the persistent data store
* @param mode the mode for the persistent data store
* @return a reference to the builder
* @see DataSystemConfiguration.DataStoreMode
*/
public DataSystemBuilder persistentStore(ComponentConfigurer<DataStore> persistentStore, DataSystemConfiguration.DataStoreMode mode) {
this.persistentStore = persistentStore;
this.persistentDataStoreMode = mode;
return this;
}

/**
* Build the data system configuration.
* <p>
* This method is internal and should not be called by application code.
* This function should remain internal.
* </p>
*
* @return the data system configuration
*/
public DataSystemConfiguration build() {
return new DataSystemConfiguration(
ImmutableList.copyOf(initializers),
ImmutableList.copyOf(synchronizers),
fDv1FallbackSynchronizer,
persistentStore,
persistentDataStoreMode);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.launchdarkly.sdk.server.integrations;

import com.launchdarkly.sdk.server.Components;

/**
* Components for use with the data system.
* <p>
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
* It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
* </p>
*/
public final class DataSystemComponents {

private DataSystemComponents() {}

/**
* Get a builder for a polling data source.
*
* @return the polling data source builder
*/
public static FDv2PollingDataSourceBuilder polling() {
return new FDv2PollingDataSourceBuilder();
}

/**
* Get a builder for a streaming data source.
*
* @return the streaming data source builder
*/
public static FDv2StreamingDataSourceBuilder streaming() {
return new FDv2StreamingDataSourceBuilder();
}

/**
* Get a builder for a FDv1 compatible polling data source.
* <p>
* This is intended for use as a fallback.
* </p>
*
* @return the FDv1 compatible polling data source builder
*/
public static PollingDataSourceBuilder fDv1Polling() {
return Components.pollingDataSource();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package com.launchdarkly.sdk.server.integrations;

import com.launchdarkly.sdk.server.subsystems.ComponentConfigurer;
import com.launchdarkly.sdk.server.subsystems.DataStore;
import com.launchdarkly.sdk.server.subsystems.DataSystemConfiguration;

/**
* A set of different data system modes which provide pre-configured {@link DataSystemBuilder}s.
* <p>
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
* It is in early access. If you want access to this feature please join the EAP. https://launchdarkly.com/docs/sdk/features/data-saving-mode
* </p>
* <p>
* This implementation is non-static to allow for easy usage with "Components".
* Where we can return an instance of this object, and the user can chain into their desired configuration.
* </p>
*/
public final class DataSystemModes {
// This implementation is non-static to allow for easy usage with "Components".
// Where we can return an instance of this object, and the user can chain into their desired configuration.

/**
* Configure's LaunchDarkly's recommended flag data acquisition strategy.
* <p>
* Currently, it operates a two-phase method for getting data: first, it requests data from LaunchDarkly's
* global CDN. Then, it initiates a streaming connection to LaunchDarkly's Flag Delivery services to receive
* real-time updates. If the streaming connection is interrupted for an extended period of time, the SDK will
* automatically fall back to polling the global CDN for updates.
* </p>
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem().defaultMode());
* </code></pre>
*
* @return a builder containing our default configuration
*/
public DataSystemBuilder defaultMode() {
return custom()
.initializers(DataSystemComponents.polling())
.synchronizers(DataSystemComponents.streaming(), DataSystemComponents.polling())
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
}

/**
* Configures the SDK to stream data without polling for the initial payload.
* <p>
* This is not our recommended strategy, which is {@link #defaultMode()}, but it may be
* suitable for some situations.
* </p>
* <p>
* This configuration will not automatically fall back to polling, but it can be instructed by LaunchDarkly
* to fall back to polling in certain situations.
* </p>
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem().streaming());
* </code></pre>
*
* @return a builder containing a primarily streaming configuration
*/
public DataSystemBuilder streaming() {
return custom()
.synchronizers(DataSystemComponents.streaming())
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
}

/**
* Configure the SDK to poll data instead of receiving real-time updates via a stream.
* <p>
* This is not our recommended strategy, which is {@link #defaultMode()}, but it may be
* required for certain network configurations.
* </p>
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem().polling());
* </code></pre>
*
* @return a builder containing a polling-only configuration
*/
public DataSystemBuilder polling() {
return custom()
.synchronizers(DataSystemComponents.polling())
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
}

/**
* Configures the SDK to read from a persistent store integration that is populated by Relay Proxy
* or other SDKs. The SDK will not connect to LaunchDarkly. In this mode, the SDK never writes to the data
* store.
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem().daemon(persistentStore));
* </code></pre>
*
* @param persistentStore the persistent store configurer
* @return a builder which is configured for daemon mode
*/
public DataSystemBuilder daemon(ComponentConfigurer<DataStore> persistentStore) {
return custom()
.persistentStore(persistentStore, DataSystemConfiguration.DataStoreMode.READ_ONLY);
}

/**
* PersistentStore is similar to Default, with the addition of a persistent store integration. Before data has
* arrived from LaunchDarkly, the SDK is able to evaluate flags using data from the persistent store.
* Once fresh data is available, the SDK will no longer read from the persistent store, although it will keep
* it up to date.
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem()
* .persistentStore(Components.persistentDataStore(SomeDatabaseName.dataStore())));
* </code></pre>
*
* @param persistentStore the persistent store configurer
* @return a builder which is configured for persistent store mode
*/
public DataSystemBuilder persistentStore(ComponentConfigurer<DataStore> persistentStore) {
return defaultMode()
.persistentStore(persistentStore, DataSystemConfiguration.DataStoreMode.READ_WRITE);
}

/**
* Custom returns a builder suitable for creating a custom data acquisition strategy. You may configure
* how the SDK uses a Persistent Store, how the SDK obtains an initial set of data, and how the SDK keeps data
* up to date.
* <p>
* <b>Example:</b>
* </p>
* <pre><code>
* LDConfig config = new LDConfig.Builder("my-sdk-key")
* .dataSystem(Components.dataSystem().custom()
* .initializers(DataSystemComponents.polling())
* .synchronizers(DataSystemComponents.streaming(), DataSystemComponents.polling())
* .fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling()));
* </code></pre>
*
* @return a builder without any base configuration
*/
public DataSystemBuilder custom() {
return new DataSystemBuilder();
}
}

Loading