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

Commit

Permalink
prepare 5.6.0 release (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
LaunchDarklyCI authored Jul 2, 2021
1 parent 3f9f2b6 commit 6585f90
Show file tree
Hide file tree
Showing 16 changed files with 1,250 additions and 1,295 deletions.
6 changes: 2 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,13 @@ libraries.optional = [

// Add dependencies to "libraries.test" that are used only in unit tests.
libraries.test = [
// Note that the okhttp3 test deps must be kept in sync with the okhttp version used in okhttp-eventsource
"com.squareup.okhttp3:mockwebserver:${versions.okhttp}",
"com.squareup.okhttp3:okhttp-tls:${versions.okhttp}",
"org.hamcrest:hamcrest-all:1.3",
"org.easymock:easymock:3.4",
"junit:junit:4.12",
"ch.qos.logback:logback-classic:1.1.7",
"com.fasterxml.jackson.core:jackson-core:${versions.jackson}",
"com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
"com.fasterxml.jackson.core:jackson-databind:${versions.jackson}",
"com.launchdarkly:test-helpers:1.0.0"
]

configurations {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/launchdarkly/sdk/server/Components.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ public static PollingDataSourceBuilder pollingDataSource() {
return new PollingDataSourceBuilderImpl();
}

// For testing only - allows us to override the minimum polling interval
static PollingDataSourceBuilderImpl pollingDataSourceInternal() {
return new PollingDataSourceBuilderImpl();
}

/**
* Returns a configuration object that disables a direct connection with LaunchDarkly for feature flag updates.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

Expand Down Expand Up @@ -143,7 +144,6 @@ public DataSource createDataSource(ClientContext context, DataSourceUpdates data
return new StreamProcessor(
context.getHttp(),
dataSourceUpdates,
null,
context.getBasic().getThreadPriority(),
ClientContextImpl.get(context).diagnosticAccumulator,
streamUri,
Expand All @@ -165,6 +165,12 @@ public LDValue describeConfiguration(BasicConfiguration basicConfiguration) {
}

static final class PollingDataSourceBuilderImpl extends PollingDataSourceBuilder implements DiagnosticDescription {
// for testing only
PollingDataSourceBuilderImpl pollIntervalWithNoMinimum(Duration pollInterval) {
this.pollInterval = pollInterval;
return this;
}

@Override
public DataSource createDataSource(ClientContext context, DataSourceUpdates dataSourceUpdates) {
// Note, we log startup messages under the LDClient class to keep logs more readable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ private void poll() {
} else {
dataSourceUpdates.updateStatus(State.OFF, errorInfo);
initFuture.complete(null); // if client is initializing, make it stop waiting; has no effect if already inited
if (task != null) {
task.cancel(true);
task = null;
}
}
} catch (IOException e) {
checkIfErrorIsRecoverableAndLog(logger, e.toString(), ERROR_CONTEXT_MESSAGE, 0, WILL_RETRY_MESSAGE);
Expand Down
75 changes: 20 additions & 55 deletions src/main/java/com/launchdarkly/sdk/server/StreamProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ final class StreamProcessor implements DataSource {
@VisibleForTesting final URI streamUri;
@VisibleForTesting final Duration initialReconnectDelay;
private final DiagnosticAccumulator diagnosticAccumulator;
private final EventSourceCreator eventSourceCreator;
private final int threadPriority;
private final DataStoreStatusProvider.StatusListener statusListener;
private volatile EventSource es;
Expand All @@ -94,34 +93,9 @@ final class StreamProcessor implements DataSource {

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

static final class EventSourceParams {
final EventHandler handler;
final URI streamUri;
final Duration initialReconnectDelay;
final ConnectionErrorHandler errorHandler;
final Headers headers;
final HttpConfiguration httpConfig;

EventSourceParams(EventHandler handler, URI streamUri, Duration initialReconnectDelay,
ConnectionErrorHandler errorHandler, Headers headers, HttpConfiguration httpConfig) {
this.handler = handler;
this.streamUri = streamUri;
this.initialReconnectDelay = initialReconnectDelay;
this.errorHandler = errorHandler;
this.headers = headers;
this.httpConfig = httpConfig;
}
}

@FunctionalInterface
static interface EventSourceCreator {
EventSource createEventSource(EventSourceParams params);
}

StreamProcessor(
HttpConfiguration httpConfig,
DataSourceUpdates dataSourceUpdates,
EventSourceCreator eventSourceCreator,
int threadPriority,
DiagnosticAccumulator diagnosticAccumulator,
URI streamUri,
Expand All @@ -130,7 +104,6 @@ static interface EventSourceCreator {
this.dataSourceUpdates = dataSourceUpdates;
this.httpConfig = httpConfig;
this.diagnosticAccumulator = diagnosticAccumulator;
this.eventSourceCreator = eventSourceCreator != null ? eventSourceCreator : this::defaultEventSourceCreator;
this.threadPriority = threadPriority;
this.streamUri = streamUri;
this.initialReconnectDelay = initialReconnectDelay;
Expand Down Expand Up @@ -202,13 +175,26 @@ public Future<Void> start() {
};

EventHandler handler = new StreamEventHandler(initFuture);

es = eventSourceCreator.createEventSource(new EventSourceParams(handler,
concatenateUriPath(streamUri, STREAM_URI_PATH),
initialReconnectDelay,
wrappedConnectionErrorHandler,
headers,
httpConfig));
URI endpointUri = concatenateUriPath(streamUri, STREAM_URI_PATH);

EventSource.Builder builder = new EventSource.Builder(handler, endpointUri)
.threadPriority(threadPriority)
.loggerBaseName(Loggers.DATA_SOURCE_LOGGER_NAME)
.clientBuilderActions(new EventSource.Builder.ClientConfigurer() {
public void configure(OkHttpClient.Builder builder) {
configureHttpClientBuilder(httpConfig, builder);
}
})
.connectionErrorHandler(wrappedConnectionErrorHandler)
.headers(headers)
.reconnectTime(initialReconnectDelay)
.readTimeout(DEAD_CONNECTION_INTERVAL);
// Note that this is not the same read timeout that can be set in LDConfig. We default to a smaller one
// there because we don't expect long delays within any *non*-streaming response that the LD client gets.
// A read timeout on the stream will result in the connection being cycled, so we set this to be slightly
// more than the expected interval between heartbeat signals.

es = builder.build();
esStarted = System.currentTimeMillis();
es.start();
return initFuture;
Expand Down Expand Up @@ -356,27 +342,6 @@ public void onError(Throwable throwable) {
}
}

private EventSource defaultEventSourceCreator(EventSourceParams params) {
EventSource.Builder builder = new EventSource.Builder(params.handler, params.streamUri)
.threadPriority(threadPriority)
.loggerBaseName(Loggers.DATA_SOURCE_LOGGER_NAME)
.clientBuilderActions(new EventSource.Builder.ClientConfigurer() {
public void configure(OkHttpClient.Builder builder) {
configureHttpClientBuilder(params.httpConfig, builder);
}
})
.connectionErrorHandler(params.errorHandler)
.headers(params.headers)
.reconnectTime(params.initialReconnectDelay)
.readTimeout(DEAD_CONNECTION_INTERVAL);
// Note that this is not the same read timeout that can be set in LDConfig. We default to a smaller one
// there because we don't expect long delays within any *non*-streaming response that the LD client gets.
// A read timeout on the stream will result in the connection being cycled, so we set this to be slightly
// more than the expected interval between heartbeat signals.

return builder.build();
}

private static Map.Entry<DataKind, String> getKindAndKeyFromStreamApiPath(String path) throws StreamInputException {
if (path == null) {
throw new StreamInputException("missing item path");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.launchdarkly.sdk.server.integrations;

import com.launchdarkly.sdk.server.LDConfig;

/**
* Integration between the LaunchDarkly SDK and file data.
* <p>
Expand Down Expand Up @@ -59,9 +57,9 @@ public enum DuplicateKeysHandling {
* This will cause the client <i>not</i> to connect to LaunchDarkly to get feature flags. The
* client may still make network connections to send analytics events, unless you have disabled
* this with {@link com.launchdarkly.sdk.server.Components#noEvents()}. IMPORTANT: Do <i>not</i>
* set {@link LDConfig.Builder#offline(boolean)} to {@code true}; doing so would not just put the
* SDK "offline" with regard to LaunchDarkly, but will completely turn off all flag data sources
* to the SDK <i>including the file data source</i>.
* set {@link com.launchdarkly.sdk.server.LDConfig.Builder#offline(boolean)} to {@code true}; doing so
* would not just put the SDK "offline" with regard to LaunchDarkly, but will completely turn off
* all flag data sources to the SDK <i>including the file data source</i>.
* <p>
* Flag data files can be either JSON or YAML. They contain an object with three possible
* properties:
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/com/launchdarkly/sdk/server/DataStoreTestTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.launchdarkly.sdk.LDValue;
import com.launchdarkly.sdk.ObjectBuilder;
import com.launchdarkly.sdk.server.DataModel.VersionedData;
import com.launchdarkly.sdk.server.interfaces.DataStoreTypes.DataKind;
import com.launchdarkly.sdk.server.interfaces.DataStoreTypes.FullDataSet;
Expand Down Expand Up @@ -137,6 +139,15 @@ private static ItemDescriptor deserializeTestItem(String s) {
public static class DataBuilder {
private Map<DataKind, Map<String, ItemDescriptor>> data = new HashMap<>();

public static DataBuilder forStandardTypes() {
// This just ensures that we use realistic-looking data sets in our tests when simulating
// an LD service response, which will always include "flags" and "segments" even if empty.
DataBuilder ret = new DataBuilder();
ret.add(DataModel.FEATURES);
ret.add(DataModel.SEGMENTS);
return ret;
}

public DataBuilder add(DataKind kind, TestItem... items) {
return addAny(kind, items);
}
Expand Down Expand Up @@ -182,5 +193,19 @@ public FullDataSet<SerializedItemDescriptor> buildSerialized() {
)
).entrySet());
}

public LDValue buildJson() {
FullDataSet<SerializedItemDescriptor> allData = buildSerialized();
ObjectBuilder allBuilder = LDValue.buildObject();
for (Map.Entry<DataKind, KeyedItems<SerializedItemDescriptor>> coll: allData.getData()) {
String namespace = coll.getKey().getName().equals("features") ? "flags" : coll.getKey().getName();
ObjectBuilder itemsBuilder = LDValue.buildObject();
for (Map.Entry<String, SerializedItemDescriptor> item: coll.getValue().getItems()) {
itemsBuilder.put(item.getKey(), LDValue.parse(item.getValue().getSerializedItem()));
}
allBuilder.put(namespace, itemsBuilder.build());
}
return allBuilder.build();
}
}
}
Loading

0 comments on commit 6585f90

Please sign in to comment.