Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit 6585f90

Browse files
prepare 5.6.0 release (#242)
1 parent 3f9f2b6 commit 6585f90

16 files changed

+1250
-1295
lines changed

build.gradle

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,13 @@ libraries.optional = [
121121

122122
// Add dependencies to "libraries.test" that are used only in unit tests.
123123
libraries.test = [
124-
// Note that the okhttp3 test deps must be kept in sync with the okhttp version used in okhttp-eventsource
125-
"com.squareup.okhttp3:mockwebserver:${versions.okhttp}",
126-
"com.squareup.okhttp3:okhttp-tls:${versions.okhttp}",
127124
"org.hamcrest:hamcrest-all:1.3",
128125
"org.easymock:easymock:3.4",
129126
"junit:junit:4.12",
130127
"ch.qos.logback:logback-classic:1.1.7",
131128
"com.fasterxml.jackson.core:jackson-core:${versions.jackson}",
132-
"com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
129+
"com.fasterxml.jackson.core:jackson-databind:${versions.jackson}",
130+
"com.launchdarkly:test-helpers:1.0.0"
133131
]
134132

135133
configurations {

src/main/java/com/launchdarkly/sdk/server/Components.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ public static PollingDataSourceBuilder pollingDataSource() {
187187
return new PollingDataSourceBuilderImpl();
188188
}
189189

190+
// For testing only - allows us to override the minimum polling interval
191+
static PollingDataSourceBuilderImpl pollingDataSourceInternal() {
192+
return new PollingDataSourceBuilderImpl();
193+
}
194+
190195
/**
191196
* Returns a configuration object that disables a direct connection with LaunchDarkly for feature flag updates.
192197
* <p>

src/main/java/com/launchdarkly/sdk/server/ComponentsImpl.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.net.InetSocketAddress;
3434
import java.net.Proxy;
3535
import java.net.URI;
36+
import java.time.Duration;
3637
import java.util.concurrent.CompletableFuture;
3738
import java.util.concurrent.Future;
3839

@@ -143,7 +144,6 @@ public DataSource createDataSource(ClientContext context, DataSourceUpdates data
143144
return new StreamProcessor(
144145
context.getHttp(),
145146
dataSourceUpdates,
146-
null,
147147
context.getBasic().getThreadPriority(),
148148
ClientContextImpl.get(context).diagnosticAccumulator,
149149
streamUri,
@@ -165,6 +165,12 @@ public LDValue describeConfiguration(BasicConfiguration basicConfiguration) {
165165
}
166166

167167
static final class PollingDataSourceBuilderImpl extends PollingDataSourceBuilder implements DiagnosticDescription {
168+
// for testing only
169+
PollingDataSourceBuilderImpl pollIntervalWithNoMinimum(Duration pollInterval) {
170+
this.pollInterval = pollInterval;
171+
return this;
172+
}
173+
168174
@Override
169175
public DataSource createDataSource(ClientContext context, DataSourceUpdates dataSourceUpdates) {
170176
// Note, we log startup messages under the LDClient class to keep logs more readable

src/main/java/com/launchdarkly/sdk/server/PollingProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ private void poll() {
111111
} else {
112112
dataSourceUpdates.updateStatus(State.OFF, errorInfo);
113113
initFuture.complete(null); // if client is initializing, make it stop waiting; has no effect if already inited
114+
if (task != null) {
115+
task.cancel(true);
116+
task = null;
117+
}
114118
}
115119
} catch (IOException e) {
116120
checkIfErrorIsRecoverableAndLog(logger, e.toString(), ERROR_CONTEXT_MESSAGE, 0, WILL_RETRY_MESSAGE);

src/main/java/com/launchdarkly/sdk/server/StreamProcessor.java

Lines changed: 20 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ final class StreamProcessor implements DataSource {
8484
@VisibleForTesting final URI streamUri;
8585
@VisibleForTesting final Duration initialReconnectDelay;
8686
private final DiagnosticAccumulator diagnosticAccumulator;
87-
private final EventSourceCreator eventSourceCreator;
8887
private final int threadPriority;
8988
private final DataStoreStatusProvider.StatusListener statusListener;
9089
private volatile EventSource es;
@@ -94,34 +93,9 @@ final class StreamProcessor implements DataSource {
9493

9594
ConnectionErrorHandler connectionErrorHandler = createDefaultConnectionErrorHandler(); // exposed for testing
9695

97-
static final class EventSourceParams {
98-
final EventHandler handler;
99-
final URI streamUri;
100-
final Duration initialReconnectDelay;
101-
final ConnectionErrorHandler errorHandler;
102-
final Headers headers;
103-
final HttpConfiguration httpConfig;
104-
105-
EventSourceParams(EventHandler handler, URI streamUri, Duration initialReconnectDelay,
106-
ConnectionErrorHandler errorHandler, Headers headers, HttpConfiguration httpConfig) {
107-
this.handler = handler;
108-
this.streamUri = streamUri;
109-
this.initialReconnectDelay = initialReconnectDelay;
110-
this.errorHandler = errorHandler;
111-
this.headers = headers;
112-
this.httpConfig = httpConfig;
113-
}
114-
}
115-
116-
@FunctionalInterface
117-
static interface EventSourceCreator {
118-
EventSource createEventSource(EventSourceParams params);
119-
}
120-
12196
StreamProcessor(
12297
HttpConfiguration httpConfig,
12398
DataSourceUpdates dataSourceUpdates,
124-
EventSourceCreator eventSourceCreator,
12599
int threadPriority,
126100
DiagnosticAccumulator diagnosticAccumulator,
127101
URI streamUri,
@@ -130,7 +104,6 @@ static interface EventSourceCreator {
130104
this.dataSourceUpdates = dataSourceUpdates;
131105
this.httpConfig = httpConfig;
132106
this.diagnosticAccumulator = diagnosticAccumulator;
133-
this.eventSourceCreator = eventSourceCreator != null ? eventSourceCreator : this::defaultEventSourceCreator;
134107
this.threadPriority = threadPriority;
135108
this.streamUri = streamUri;
136109
this.initialReconnectDelay = initialReconnectDelay;
@@ -202,13 +175,26 @@ public Future<Void> start() {
202175
};
203176

204177
EventHandler handler = new StreamEventHandler(initFuture);
205-
206-
es = eventSourceCreator.createEventSource(new EventSourceParams(handler,
207-
concatenateUriPath(streamUri, STREAM_URI_PATH),
208-
initialReconnectDelay,
209-
wrappedConnectionErrorHandler,
210-
headers,
211-
httpConfig));
178+
URI endpointUri = concatenateUriPath(streamUri, STREAM_URI_PATH);
179+
180+
EventSource.Builder builder = new EventSource.Builder(handler, endpointUri)
181+
.threadPriority(threadPriority)
182+
.loggerBaseName(Loggers.DATA_SOURCE_LOGGER_NAME)
183+
.clientBuilderActions(new EventSource.Builder.ClientConfigurer() {
184+
public void configure(OkHttpClient.Builder builder) {
185+
configureHttpClientBuilder(httpConfig, builder);
186+
}
187+
})
188+
.connectionErrorHandler(wrappedConnectionErrorHandler)
189+
.headers(headers)
190+
.reconnectTime(initialReconnectDelay)
191+
.readTimeout(DEAD_CONNECTION_INTERVAL);
192+
// Note that this is not the same read timeout that can be set in LDConfig. We default to a smaller one
193+
// there because we don't expect long delays within any *non*-streaming response that the LD client gets.
194+
// A read timeout on the stream will result in the connection being cycled, so we set this to be slightly
195+
// more than the expected interval between heartbeat signals.
196+
197+
es = builder.build();
212198
esStarted = System.currentTimeMillis();
213199
es.start();
214200
return initFuture;
@@ -356,27 +342,6 @@ public void onError(Throwable throwable) {
356342
}
357343
}
358344

359-
private EventSource defaultEventSourceCreator(EventSourceParams params) {
360-
EventSource.Builder builder = new EventSource.Builder(params.handler, params.streamUri)
361-
.threadPriority(threadPriority)
362-
.loggerBaseName(Loggers.DATA_SOURCE_LOGGER_NAME)
363-
.clientBuilderActions(new EventSource.Builder.ClientConfigurer() {
364-
public void configure(OkHttpClient.Builder builder) {
365-
configureHttpClientBuilder(params.httpConfig, builder);
366-
}
367-
})
368-
.connectionErrorHandler(params.errorHandler)
369-
.headers(params.headers)
370-
.reconnectTime(params.initialReconnectDelay)
371-
.readTimeout(DEAD_CONNECTION_INTERVAL);
372-
// Note that this is not the same read timeout that can be set in LDConfig. We default to a smaller one
373-
// there because we don't expect long delays within any *non*-streaming response that the LD client gets.
374-
// A read timeout on the stream will result in the connection being cycled, so we set this to be slightly
375-
// more than the expected interval between heartbeat signals.
376-
377-
return builder.build();
378-
}
379-
380345
private static Map.Entry<DataKind, String> getKindAndKeyFromStreamApiPath(String path) throws StreamInputException {
381346
if (path == null) {
382347
throw new StreamInputException("missing item path");

src/main/java/com/launchdarkly/sdk/server/integrations/FileData.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.launchdarkly.sdk.server.integrations;
22

3-
import com.launchdarkly.sdk.server.LDConfig;
4-
53
/**
64
* Integration between the LaunchDarkly SDK and file data.
75
* <p>
@@ -59,9 +57,9 @@ public enum DuplicateKeysHandling {
5957
* This will cause the client <i>not</i> to connect to LaunchDarkly to get feature flags. The
6058
* client may still make network connections to send analytics events, unless you have disabled
6159
* this with {@link com.launchdarkly.sdk.server.Components#noEvents()}. IMPORTANT: Do <i>not</i>
62-
* set {@link LDConfig.Builder#offline(boolean)} to {@code true}; doing so would not just put the
63-
* SDK "offline" with regard to LaunchDarkly, but will completely turn off all flag data sources
64-
* to the SDK <i>including the file data source</i>.
60+
* set {@link com.launchdarkly.sdk.server.LDConfig.Builder#offline(boolean)} to {@code true}; doing so
61+
* would not just put the SDK "offline" with regard to LaunchDarkly, but will completely turn off
62+
* all flag data sources to the SDK <i>including the file data source</i>.
6563
* <p>
6664
* Flag data files can be either JSON or YAML. They contain an object with three possible
6765
* properties:

src/test/java/com/launchdarkly/sdk/server/DataStoreTestTypes.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.google.common.collect.ImmutableList;
44
import com.google.common.collect.ImmutableMap;
55
import com.google.common.collect.Maps;
6+
import com.launchdarkly.sdk.LDValue;
7+
import com.launchdarkly.sdk.ObjectBuilder;
68
import com.launchdarkly.sdk.server.DataModel.VersionedData;
79
import com.launchdarkly.sdk.server.interfaces.DataStoreTypes.DataKind;
810
import com.launchdarkly.sdk.server.interfaces.DataStoreTypes.FullDataSet;
@@ -137,6 +139,15 @@ private static ItemDescriptor deserializeTestItem(String s) {
137139
public static class DataBuilder {
138140
private Map<DataKind, Map<String, ItemDescriptor>> data = new HashMap<>();
139141

142+
public static DataBuilder forStandardTypes() {
143+
// This just ensures that we use realistic-looking data sets in our tests when simulating
144+
// an LD service response, which will always include "flags" and "segments" even if empty.
145+
DataBuilder ret = new DataBuilder();
146+
ret.add(DataModel.FEATURES);
147+
ret.add(DataModel.SEGMENTS);
148+
return ret;
149+
}
150+
140151
public DataBuilder add(DataKind kind, TestItem... items) {
141152
return addAny(kind, items);
142153
}
@@ -182,5 +193,19 @@ public FullDataSet<SerializedItemDescriptor> buildSerialized() {
182193
)
183194
).entrySet());
184195
}
196+
197+
public LDValue buildJson() {
198+
FullDataSet<SerializedItemDescriptor> allData = buildSerialized();
199+
ObjectBuilder allBuilder = LDValue.buildObject();
200+
for (Map.Entry<DataKind, KeyedItems<SerializedItemDescriptor>> coll: allData.getData()) {
201+
String namespace = coll.getKey().getName().equals("features") ? "flags" : coll.getKey().getName();
202+
ObjectBuilder itemsBuilder = LDValue.buildObject();
203+
for (Map.Entry<String, SerializedItemDescriptor> item: coll.getValue().getItems()) {
204+
itemsBuilder.put(item.getKey(), LDValue.parse(item.getValue().getSerializedItem()));
205+
}
206+
allBuilder.put(namespace, itemsBuilder.build());
207+
}
208+
return allBuilder.build();
209+
}
185210
}
186211
}

0 commit comments

Comments
 (0)