Skip to content

Commit 5be699e

Browse files
authored
chore: adds data system configuration APIs (#101)
**Requirements** - [x] I have added test coverage for new or changed functionality - [x] I have followed the repository's [pull request submission guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests) - [ ] I have validated my changes against all supported platform versions Cannot be done yet. **Related issues** SDK-1623 **Describe the solution you've provided** Porting of Dotnet Server SDK implementation.
1 parent 2c26877 commit 5be699e

File tree

8 files changed

+995
-8
lines changed

8 files changed

+995
-8
lines changed

lib/sdk/server/src/main/java/com/launchdarkly/sdk/server/StandardEndpoints.java

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44

55
import java.net.URI;
66

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

10-
static final URI DEFAULT_STREAMING_BASE_URI = URI.create("https://stream.launchdarkly.com");
11-
static final URI DEFAULT_POLLING_BASE_URI = URI.create("https://app.launchdarkly.com");
12-
static final URI DEFAULT_EVENTS_BASE_URI = URI.create("https://events.launchdarkly.com");
18+
public static final URI DEFAULT_STREAMING_BASE_URI = URI.create("https://stream.launchdarkly.com");
19+
public static final URI DEFAULT_POLLING_BASE_URI = URI.create("https://app.launchdarkly.com");
20+
public static final URI DEFAULT_EVENTS_BASE_URI = URI.create("https://events.launchdarkly.com");
1321

14-
static final String STREAMING_REQUEST_PATH = "/all";
15-
static final String POLLING_REQUEST_PATH = "/sdk/latest-all";
22+
public static final String STREAMING_REQUEST_PATH = "/all";
23+
public static final String POLLING_REQUEST_PATH = "/sdk/latest-all";
1624

1725
/**
1826
* Internal method to decide which URI a given component should connect to.
@@ -26,7 +34,7 @@ private StandardEndpoints() {}
2634
* @param logger the logger to which we should print the warning, if needed
2735
* @return the base URI we should connect to
2836
*/
29-
static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String description, LDLogger logger) {
37+
public static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String description, LDLogger logger) {
3038
if (serviceEndpointsValue != null) {
3139
return serviceEndpointsValue;
3240
}
@@ -46,7 +54,7 @@ static URI selectBaseUri(URI serviceEndpointsValue, URI defaultValue, String des
4654
* @param defaultValue the constant default URI value defined in StandardEndpoints
4755
* @return true iff the base URI was customized
4856
*/
49-
static boolean isCustomBaseUri(URI serviceEndpointsValue, URI defaultValue) {
57+
public static boolean isCustomBaseUri(URI serviceEndpointsValue, URI defaultValue) {
5058
return serviceEndpointsValue != null && !serviceEndpointsValue.equals(defaultValue);
5159
}
5260
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.launchdarkly.sdk.server.integrations;
2+
3+
import com.google.common.collect.ImmutableList;
4+
import com.launchdarkly.sdk.server.subsystems.ComponentConfigurer;
5+
import com.launchdarkly.sdk.server.subsystems.DataSource;
6+
import com.launchdarkly.sdk.server.subsystems.DataStore;
7+
import com.launchdarkly.sdk.server.subsystems.DataSystemConfiguration;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
/**
13+
* Configuration builder for the SDK's data acquisition and storage strategy.
14+
* <p>
15+
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
16+
* 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
17+
* </p>
18+
*/
19+
public final class DataSystemBuilder {
20+
21+
private final List<ComponentConfigurer<DataSource>> initializers = new ArrayList<>();
22+
private final List<ComponentConfigurer<DataSource>> synchronizers = new ArrayList<>();
23+
private ComponentConfigurer<DataSource> fDv1FallbackSynchronizer;
24+
private ComponentConfigurer<DataStore> persistentStore;
25+
private DataSystemConfiguration.DataStoreMode persistentDataStoreMode;
26+
27+
/**
28+
* Add one or more initializers to the builder.
29+
* To replace initializers, please refer to {@link #replaceInitializers(ComponentConfigurer[])}.
30+
*
31+
* @param initializers the initializers to add
32+
* @return a reference to the builder
33+
*/
34+
public DataSystemBuilder initializers(ComponentConfigurer<DataSource>... initializers) {
35+
for (ComponentConfigurer<DataSource> initializer : initializers) {
36+
this.initializers.add(initializer);
37+
}
38+
return this;
39+
}
40+
41+
/**
42+
* Replaces any existing initializers with the given initializers.
43+
* To add initializers, please refer to {@link #initializers(ComponentConfigurer[])}.
44+
*
45+
* @param initializers the initializers to replace the current initializers with
46+
* @return a reference to this builder
47+
*/
48+
public DataSystemBuilder replaceInitializers(ComponentConfigurer<DataSource>... initializers) {
49+
this.initializers.clear();
50+
for (ComponentConfigurer<DataSource> initializer : initializers) {
51+
this.initializers.add(initializer);
52+
}
53+
return this;
54+
}
55+
56+
/**
57+
* Add one or more synchronizers to the builder.
58+
* To replace synchronizers, please refer to {@link #replaceSynchronizers(ComponentConfigurer[])}.
59+
*
60+
* @param synchronizers the synchronizers to add
61+
* @return a reference to the builder
62+
*/
63+
public DataSystemBuilder synchronizers(ComponentConfigurer<DataSource>... synchronizers) {
64+
for (ComponentConfigurer<DataSource> synchronizer : synchronizers) {
65+
this.synchronizers.add(synchronizer);
66+
}
67+
return this;
68+
}
69+
70+
/**
71+
* Replaces any existing synchronizers with the given synchronizers.
72+
* To add synchronizers, please refer to {@link #synchronizers(ComponentConfigurer[])}.
73+
*
74+
* @param synchronizers the synchronizers to replace the current synchronizers with
75+
* @return a reference to this builder
76+
*/
77+
public DataSystemBuilder replaceSynchronizers(ComponentConfigurer<DataSource>... synchronizers) {
78+
this.synchronizers.clear();
79+
for (ComponentConfigurer<DataSource> synchronizer : synchronizers) {
80+
this.synchronizers.add(synchronizer);
81+
}
82+
return this;
83+
}
84+
85+
/**
86+
* Configure the FDv1 fallback synchronizer.
87+
* <p>
88+
* LaunchDarkly can instruct the SDK to fall back to this synchronizer.
89+
* </p>
90+
*
91+
* @param fDv1FallbackSynchronizer the FDv1 fallback synchronizer
92+
* @return a reference to the builder
93+
*/
94+
public DataSystemBuilder fDv1FallbackSynchronizer(ComponentConfigurer<DataSource> fDv1FallbackSynchronizer) {
95+
this.fDv1FallbackSynchronizer = fDv1FallbackSynchronizer;
96+
return this;
97+
}
98+
99+
/**
100+
* Configures the persistent data store.
101+
* <p>
102+
* The SDK will use the persistent data store to store feature flag data.
103+
* </p>
104+
*
105+
* @param persistentStore the persistent data store
106+
* @param mode the mode for the persistent data store
107+
* @return a reference to the builder
108+
* @see DataSystemConfiguration.DataStoreMode
109+
*/
110+
public DataSystemBuilder persistentStore(ComponentConfigurer<DataStore> persistentStore, DataSystemConfiguration.DataStoreMode mode) {
111+
this.persistentStore = persistentStore;
112+
this.persistentDataStoreMode = mode;
113+
return this;
114+
}
115+
116+
/**
117+
* Build the data system configuration.
118+
* <p>
119+
* This method is internal and should not be called by application code.
120+
* This function should remain internal.
121+
* </p>
122+
*
123+
* @return the data system configuration
124+
*/
125+
public DataSystemConfiguration build() {
126+
return new DataSystemConfiguration(
127+
ImmutableList.copyOf(initializers),
128+
ImmutableList.copyOf(synchronizers),
129+
fDv1FallbackSynchronizer,
130+
persistentStore,
131+
persistentDataStoreMode);
132+
}
133+
}
134+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.launchdarkly.sdk.server.integrations;
2+
3+
import com.launchdarkly.sdk.server.Components;
4+
5+
/**
6+
* Components for use with the data system.
7+
* <p>
8+
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
9+
* 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
10+
* </p>
11+
*/
12+
public final class DataSystemComponents {
13+
14+
private DataSystemComponents() {}
15+
16+
/**
17+
* Get a builder for a polling data source.
18+
*
19+
* @return the polling data source builder
20+
*/
21+
public static FDv2PollingDataSourceBuilder polling() {
22+
return new FDv2PollingDataSourceBuilder();
23+
}
24+
25+
/**
26+
* Get a builder for a streaming data source.
27+
*
28+
* @return the streaming data source builder
29+
*/
30+
public static FDv2StreamingDataSourceBuilder streaming() {
31+
return new FDv2StreamingDataSourceBuilder();
32+
}
33+
34+
/**
35+
* Get a builder for a FDv1 compatible polling data source.
36+
* <p>
37+
* This is intended for use as a fallback.
38+
* </p>
39+
*
40+
* @return the FDv1 compatible polling data source builder
41+
*/
42+
public static PollingDataSourceBuilder fDv1Polling() {
43+
return Components.pollingDataSource();
44+
}
45+
}
46+
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package com.launchdarkly.sdk.server.integrations;
2+
3+
import com.launchdarkly.sdk.server.subsystems.ComponentConfigurer;
4+
import com.launchdarkly.sdk.server.subsystems.DataStore;
5+
import com.launchdarkly.sdk.server.subsystems.DataSystemConfiguration;
6+
7+
/**
8+
* A set of different data system modes which provide pre-configured {@link DataSystemBuilder}s.
9+
* <p>
10+
* This class is not stable, and not subject to any backwards compatibility guarantees or semantic versioning.
11+
* 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
12+
* </p>
13+
* <p>
14+
* This implementation is non-static to allow for easy usage with "Components".
15+
* Where we can return an instance of this object, and the user can chain into their desired configuration.
16+
* </p>
17+
*/
18+
public final class DataSystemModes {
19+
// This implementation is non-static to allow for easy usage with "Components".
20+
// Where we can return an instance of this object, and the user can chain into their desired configuration.
21+
22+
/**
23+
* Configure's LaunchDarkly's recommended flag data acquisition strategy.
24+
* <p>
25+
* Currently, it operates a two-phase method for getting data: first, it requests data from LaunchDarkly's
26+
* global CDN. Then, it initiates a streaming connection to LaunchDarkly's Flag Delivery services to receive
27+
* real-time updates. If the streaming connection is interrupted for an extended period of time, the SDK will
28+
* automatically fall back to polling the global CDN for updates.
29+
* </p>
30+
* <p>
31+
* <b>Example:</b>
32+
* </p>
33+
* <pre><code>
34+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
35+
* .dataSystem(Components.dataSystem().defaultMode());
36+
* </code></pre>
37+
*
38+
* @return a builder containing our default configuration
39+
*/
40+
public DataSystemBuilder defaultMode() {
41+
return custom()
42+
.initializers(DataSystemComponents.polling())
43+
.synchronizers(DataSystemComponents.streaming(), DataSystemComponents.polling())
44+
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
45+
}
46+
47+
/**
48+
* Configures the SDK to stream data without polling for the initial payload.
49+
* <p>
50+
* This is not our recommended strategy, which is {@link #defaultMode()}, but it may be
51+
* suitable for some situations.
52+
* </p>
53+
* <p>
54+
* This configuration will not automatically fall back to polling, but it can be instructed by LaunchDarkly
55+
* to fall back to polling in certain situations.
56+
* </p>
57+
* <p>
58+
* <b>Example:</b>
59+
* </p>
60+
* <pre><code>
61+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
62+
* .dataSystem(Components.dataSystem().streaming());
63+
* </code></pre>
64+
*
65+
* @return a builder containing a primarily streaming configuration
66+
*/
67+
public DataSystemBuilder streaming() {
68+
return custom()
69+
.synchronizers(DataSystemComponents.streaming())
70+
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
71+
}
72+
73+
/**
74+
* Configure the SDK to poll data instead of receiving real-time updates via a stream.
75+
* <p>
76+
* This is not our recommended strategy, which is {@link #defaultMode()}, but it may be
77+
* required for certain network configurations.
78+
* </p>
79+
* <p>
80+
* <b>Example:</b>
81+
* </p>
82+
* <pre><code>
83+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
84+
* .dataSystem(Components.dataSystem().polling());
85+
* </code></pre>
86+
*
87+
* @return a builder containing a polling-only configuration
88+
*/
89+
public DataSystemBuilder polling() {
90+
return custom()
91+
.synchronizers(DataSystemComponents.polling())
92+
.fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling());
93+
}
94+
95+
/**
96+
* Configures the SDK to read from a persistent store integration that is populated by Relay Proxy
97+
* or other SDKs. The SDK will not connect to LaunchDarkly. In this mode, the SDK never writes to the data
98+
* store.
99+
* <p>
100+
* <b>Example:</b>
101+
* </p>
102+
* <pre><code>
103+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
104+
* .dataSystem(Components.dataSystem().daemon(persistentStore));
105+
* </code></pre>
106+
*
107+
* @param persistentStore the persistent store configurer
108+
* @return a builder which is configured for daemon mode
109+
*/
110+
public DataSystemBuilder daemon(ComponentConfigurer<DataStore> persistentStore) {
111+
return custom()
112+
.persistentStore(persistentStore, DataSystemConfiguration.DataStoreMode.READ_ONLY);
113+
}
114+
115+
/**
116+
* PersistentStore is similar to Default, with the addition of a persistent store integration. Before data has
117+
* arrived from LaunchDarkly, the SDK is able to evaluate flags using data from the persistent store.
118+
* Once fresh data is available, the SDK will no longer read from the persistent store, although it will keep
119+
* it up to date.
120+
* <p>
121+
* <b>Example:</b>
122+
* </p>
123+
* <pre><code>
124+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
125+
* .dataSystem(Components.dataSystem()
126+
* .persistentStore(Components.persistentDataStore(SomeDatabaseName.dataStore())));
127+
* </code></pre>
128+
*
129+
* @param persistentStore the persistent store configurer
130+
* @return a builder which is configured for persistent store mode
131+
*/
132+
public DataSystemBuilder persistentStore(ComponentConfigurer<DataStore> persistentStore) {
133+
return defaultMode()
134+
.persistentStore(persistentStore, DataSystemConfiguration.DataStoreMode.READ_WRITE);
135+
}
136+
137+
/**
138+
* Custom returns a builder suitable for creating a custom data acquisition strategy. You may configure
139+
* how the SDK uses a Persistent Store, how the SDK obtains an initial set of data, and how the SDK keeps data
140+
* up to date.
141+
* <p>
142+
* <b>Example:</b>
143+
* </p>
144+
* <pre><code>
145+
* LDConfig config = new LDConfig.Builder("my-sdk-key")
146+
* .dataSystem(Components.dataSystem().custom()
147+
* .initializers(DataSystemComponents.polling())
148+
* .synchronizers(DataSystemComponents.streaming(), DataSystemComponents.polling())
149+
* .fDv1FallbackSynchronizer(DataSystemComponents.fDv1Polling()));
150+
* </code></pre>
151+
*
152+
* @return a builder without any base configuration
153+
*/
154+
public DataSystemBuilder custom() {
155+
return new DataSystemBuilder();
156+
}
157+
}
158+

0 commit comments

Comments
 (0)