From 1f158356b02952076b7f0d3f14905e8e12174e9c Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:16:40 -0800 Subject: [PATCH 01/14] (4.x) add component-scoped configuration for polling and streaming --- .../com/launchdarkly/client/Components.java | 252 ++++++++++++++---- .../client/DefaultFeatureRequestor.java | 14 +- .../com/launchdarkly/client/LDClient.java | 8 +- .../com/launchdarkly/client/LDConfig.java | 106 +++++--- .../launchdarkly/client/PollingProcessor.java | 12 +- .../launchdarkly/client/StreamProcessor.java | 45 ++-- .../launchdarkly/client/UpdateProcessor.java | 4 +- .../PollingDataSourceBuilder.java | 73 +++++ .../StreamingDataSourceBuilder.java | 88 ++++++ .../client/FeatureRequestorTest.java | 38 +-- .../client/LDClientEndToEndTest.java | 24 +- .../client/LDClientEvaluationTest.java | 4 +- .../client/LDClientEventTest.java | 2 +- ...a => LDClientExternalUpdatesOnlyTest.java} | 51 +++- .../client/LDClientOfflineTest.java | 3 +- .../com/launchdarkly/client/LDClientTest.java | 12 +- .../client/PollingProcessorTest.java | 10 +- .../client/StreamProcessorTest.java | 91 +++---- .../com/launchdarkly/client/TestHttpUtil.java | 15 +- 19 files changed, 640 insertions(+), 212 deletions(-) create mode 100644 src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java create mode 100644 src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java rename src/test/java/com/launchdarkly/client/{LDClientLddModeTest.java => LDClientExternalUpdatesOnlyTest.java} (50%) diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index e673b8f20..39a700c67 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -1,12 +1,15 @@ package com.launchdarkly.client; import com.launchdarkly.client.integrations.PersistentDataStoreBuilder; +import com.launchdarkly.client.integrations.PollingDataSourceBuilder; +import com.launchdarkly.client.integrations.StreamingDataSourceBuilder; import com.launchdarkly.client.interfaces.PersistentDataStoreFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import java.io.IOException; import java.net.URI; +import java.util.concurrent.Future; + +import static com.google.common.util.concurrent.Futures.immediateFuture; /** * Provides factories for the standard implementations of LaunchDarkly component interfaces. @@ -20,21 +23,24 @@ public abstract class Components { private static final UpdateProcessorFactory nullUpdateProcessorFactory = new NullUpdateProcessorFactory(); /** - * Returns a factory for the default in-memory implementation of a data store. + * Returns a configuration object for using the default in-memory implementation of a data store. + *

+ * Since it is the default, you do not normally need to call this method, unless you need to create + * a data store instance for testing purposes. *

* Note that the interface is still named {@link FeatureStoreFactory}, but in a future version it * will be renamed to {@code DataStoreFactory}. * * @return a factory object * @see LDConfig.Builder#dataStore(FeatureStoreFactory) - * @since 4.11.0 + * @since 4.12.0 */ public static FeatureStoreFactory inMemoryDataStore() { return inMemoryFeatureStoreFactory; } /** - * Returns a configurable factory for some implementation of a persistent data store. + * Returns a configuration builder for some implementation of a persistent data store. *

* This method is used in conjunction with another factory object provided by specific components * such as the Redis integration. The latter provides builder methods for options that are specific @@ -57,7 +63,7 @@ public static FeatureStoreFactory inMemoryDataStore() { * @return a {@link PersistentDataStoreBuilder} * @see LDConfig.Builder#dataStore(FeatureStoreFactory) * @see com.launchdarkly.client.integrations.Redis - * @since 4.11.0 + * @since 4.12.0 */ public static PersistentDataStoreBuilder persistentDataStore(PersistentDataStoreFactory storeFactory) { return new PersistentDataStoreBuilder(storeFactory); @@ -117,27 +123,79 @@ public static EventProcessorFactory defaultEventProcessor() { public static EventProcessorFactory nullEventProcessor() { return nullEventProcessorFactory; } + + /** + * Returns a configuration object for using streaming mode to get feature flag data. + *

+ * By default, the SDK uses a streaming connection to receive feature flag data from LaunchDarkly. To use the + * default behavior, you do not need to call this method. However, if you want to customize the behavior of + * the connection, call this method to obtain a builder, change its properties with the + * {@link StreamingDataSourceBuilder} methods, and pass it to {@link LDConfig.Builder#dataSource(UpdateProcessorFactory)}: + *

 
+   *     LDConfig config = new LDConfig.Builder()
+   *         .dataSource(Components.streamingDataSource().initialReconnectDelayMillis(500))
+   *         .build();
+   * 
+ *

+ * These properties will override any equivalent deprecated properties that were set with {@code LDConfig.Builder}, + * such as {@link LDConfig.Builder#reconnectTimeMs(long)}. + *

+ * (Note that the interface is still named {@link UpdateProcessorFactory}, but in a future version it + * will be renamed to {@code DataSourceFactory}.) + * + * @return a builder for setting streaming connection properties + * @since 4.12.0 + */ + public static StreamingDataSourceBuilder streamingDataSource() { + return new StreamingDataSourceBuilderImpl(); + } /** - * Returns a factory for the default implementation of the component for receiving feature flag data - * from LaunchDarkly. Based on your configuration, this implementation uses either streaming or - * polling, or does nothing if the client is offline, or in LDD mode. + * Returns a configurable factory for using polling mode to get feature flag data. + *

+ * This is not the default behavior; by default, the SDK uses a streaming connection to receive feature flag + * data from LaunchDarkly. In polling mode, the SDK instead makes a new HTTP request to LaunchDarkly at regular + * intervals. HTTP caching allows it to avoid redundantly downloading data if there have been no changes, but + * polling is still less efficient than streaming and should only be used on the advice of LaunchDarkly support. + *

+ * To use polling mode, call this method to obtain a builder, change its properties with the + * {@link PollingDataSourceBuilder} methods, and pass it to {@link LDConfig.Builder#dataSource(UpdateProcessorFactory)}: + *

+   *     LDConfig config = new LDConfig.Builder()
+   *         .dataSource(Components.pollingDataSource().pollIntervalMillis(45000))
+   *         .build();
+   * 
+ *

+ * These properties will override any equivalent deprecated properties that were set with {@code LDConfig.Builder}, + * such as {@link LDConfig.Builder#pollingIntervalMillis(long)}. However, setting {@link LDConfig.Builder#offline(boolean)} + * to {@code true} will supersede this setting and completely disable network requests. + *

+ * (Note that the interface is still named {@link UpdateProcessorFactory}, but in a future version it + * will be renamed to {@code DataSourceFactory}.) * - * Note that the interface is still named {@link UpdateProcessorFactory}, but in a future version it - * will be renamed to {@code DataSourceFactory}. - * - * @return a factory object - * @since 4.11.0 - * @see LDConfig.Builder#dataSource(UpdateProcessorFactory) + * @return a builder for setting polling properties + * @since 4.12.0 */ - public static UpdateProcessorFactory defaultDataSource() { - return defaultUpdateProcessorFactory; + public static PollingDataSourceBuilder pollingDataSource() { + return new PollingDataSourceBuilderImpl(); } - + /** - * Deprecated name for {@link #defaultDataSource()}. + * Deprecated method for using the default data source implementation. + *

+ * If you pass the return value of this method to {@link LDConfig.Builder#dataSource(UpdateProcessorFactory)}, + * the behavior is as follows: + *

* * @return a factory object * @deprecated Use {@link #streamingDataSource()}, {@link #pollingDataSource()}, or {@link #externalUpdatesOnly()}. @@ -213,12 +214,12 @@ public static UpdateProcessorFactory defaultUpdateProcessor() { * another process that is running the LaunchDarkly SDK. If there is no external process updating * the data store, then the SDK will not have any feature flag data and will return application * default values only. - *
+   * 

    *     LDConfig config = new LDConfig.Builder()
    *         .dataSource(Components.externalUpdatesOnly())
    *         .dataStore(Components.persistentDataStore(Redis.dataStore())) // assuming the Relay Proxy is using Redis
    *         .build();
-   * 
+ *
*

* (Note that the interface is still named {@link UpdateProcessorFactory}, but in a future version it * will be renamed to {@code DataSourceFactory}.) diff --git a/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java b/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java index 2950a7c2e..1628e6b4a 100644 --- a/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java @@ -15,11 +15,11 @@ *

* To use polling mode, create a builder with {@link Components#pollingDataSource()}, * change its properties with the methods of this class, and pass it to {@link com.launchdarkly.client.LDConfig.Builder#dataSource(UpdateProcessorFactory)}: - *

+ * 

  *     LDConfig config = new LDConfig.Builder()
  *         .dataSource(Components.pollingDataSource().pollIntervalMillis(45000))
  *         .build();
- * 
+ *
*

* These properties will override any equivalent deprecated properties that were set with {@code LDConfig.Builder}, * such as {@link com.launchdarkly.client.LDConfig.Builder#pollingIntervalMillis(long)}. diff --git a/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java b/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java index c0da8e640..0c54c8cdc 100644 --- a/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java @@ -11,11 +11,11 @@ * By default, the SDK uses a streaming connection to receive feature flag data from LaunchDarkly. If you want * to customize the behavior of the connection, create a builder with {@link Components#streamingDataSource()}, * change its properties with the methods of this class, and pass it to {@link com.launchdarkly.client.LDConfig.Builder#dataSource(UpdateProcessorFactory)}: - *

+ * 

  *     LDConfig config = new LDConfig.Builder()
  *         .dataSource(Components.streamingDataSource().initialReconnectDelayMillis(500))
  *         .build();
- * 
+ *
*

* These properties will override any equivalent deprecated properties that were set with {@code LDConfig.Builder}, * such as {@link com.launchdarkly.client.LDConfig.Builder#reconnectTimeMs(long)}. From 0b915b219b55466182284ddbfce10c14fd152c9b Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:29:48 -0800 Subject: [PATCH 04/14] javadoc fixes --- .../java/com/launchdarkly/client/FeatureStoreCacheConfig.java | 4 ++-- .../com/launchdarkly/client/integrations/CacheMonitor.java | 4 ++-- .../java/com/launchdarkly/client/integrations/FileData.java | 4 ++-- .../client/integrations/FileDataSourceBuilder.java | 2 +- .../client/integrations/PersistentDataStoreBuilder.java | 2 +- src/main/java/com/launchdarkly/client/integrations/Redis.java | 2 +- .../client/integrations/RedisDataStoreBuilder.java | 2 +- .../client/interfaces/PersistentDataStoreFactory.java | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/launchdarkly/client/FeatureStoreCacheConfig.java b/src/main/java/com/launchdarkly/client/FeatureStoreCacheConfig.java index 010886791..4c73d2f53 100644 --- a/src/main/java/com/launchdarkly/client/FeatureStoreCacheConfig.java +++ b/src/main/java/com/launchdarkly/client/FeatureStoreCacheConfig.java @@ -96,7 +96,7 @@ public enum StaleValuesPolicy { /** * Used internally for backward compatibility. * @return the equivalent enum value - * @since 4.11.0 + * @since 4.12.0 */ public PersistentDataStoreBuilder.StaleValuesPolicy toNewEnum() { switch (this) { @@ -113,7 +113,7 @@ public PersistentDataStoreBuilder.StaleValuesPolicy toNewEnum() { * Used internally for backward compatibility. * @param policy the enum value in the new API * @return the equivalent enum value - * @since 4.11.0 + * @since 4.12.0 */ public static StaleValuesPolicy fromNewEnum(PersistentDataStoreBuilder.StaleValuesPolicy policy) { switch (policy) { diff --git a/src/main/java/com/launchdarkly/client/integrations/CacheMonitor.java b/src/main/java/com/launchdarkly/client/integrations/CacheMonitor.java index 618edc63d..977982d9f 100644 --- a/src/main/java/com/launchdarkly/client/integrations/CacheMonitor.java +++ b/src/main/java/com/launchdarkly/client/integrations/CacheMonitor.java @@ -7,7 +7,7 @@ * A conduit that an application can use to monitor caching behavior of a persistent data store. * * @see PersistentDataStoreBuilder#cacheMonitor(CacheMonitor) - * @since 4.11.0 + * @since 4.12.0 */ public final class CacheMonitor { private Callable source; @@ -48,7 +48,7 @@ public CacheStats getCacheStats() { * internally, but is not guaranteed to always do so, and to avoid embedding Guava API details in * the SDK API this is provided as a separate class. * - * @since 4.11.0 + * @since 4.12.0 */ public static final class CacheStats { private final long hitCount; diff --git a/src/main/java/com/launchdarkly/client/integrations/FileData.java b/src/main/java/com/launchdarkly/client/integrations/FileData.java index a6f65f3e2..9771db552 100644 --- a/src/main/java/com/launchdarkly/client/integrations/FileData.java +++ b/src/main/java/com/launchdarkly/client/integrations/FileData.java @@ -7,7 +7,7 @@ * typically be used in a test environment, to operate using a predetermined feature flag state * without an actual LaunchDarkly connection. See {@link #dataSource()} for details. * - * @since 4.11.0 + * @since 4.12.0 */ public abstract class FileData { /** @@ -106,7 +106,7 @@ public abstract class FileData { * duplicate key-- it will not load flags from any of the files. * * @return a data source configuration object - * @since 4.11.0 + * @since 4.12.0 */ public static FileDataSourceBuilder dataSource() { return new FileDataSourceBuilder(); diff --git a/src/main/java/com/launchdarkly/client/integrations/FileDataSourceBuilder.java b/src/main/java/com/launchdarkly/client/integrations/FileDataSourceBuilder.java index 4c3cd1993..49df9e3de 100644 --- a/src/main/java/com/launchdarkly/client/integrations/FileDataSourceBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/FileDataSourceBuilder.java @@ -18,7 +18,7 @@ *

* For more details, see {@link FileData}. * - * @since 4.11.0 + * @since 4.12.0 */ public final class FileDataSourceBuilder implements UpdateProcessorFactory { private final List sources = new ArrayList<>(); diff --git a/src/main/java/com/launchdarkly/client/integrations/PersistentDataStoreBuilder.java b/src/main/java/com/launchdarkly/client/integrations/PersistentDataStoreBuilder.java index 43a1b42b3..f3c5a9261 100644 --- a/src/main/java/com/launchdarkly/client/integrations/PersistentDataStoreBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/PersistentDataStoreBuilder.java @@ -33,7 +33,7 @@ * In this example, {@code .url()} is an option specifically for the Redis integration, whereas * {@code ttlSeconds()} is an option that can be used for any persistent data store. * - * @since 4.11.0 + * @since 4.12.0 */ @SuppressWarnings("deprecation") public final class PersistentDataStoreBuilder implements FeatureStoreFactory { diff --git a/src/main/java/com/launchdarkly/client/integrations/Redis.java b/src/main/java/com/launchdarkly/client/integrations/Redis.java index 7a167ae9d..90bed1e00 100644 --- a/src/main/java/com/launchdarkly/client/integrations/Redis.java +++ b/src/main/java/com/launchdarkly/client/integrations/Redis.java @@ -3,7 +3,7 @@ /** * Integration between the LaunchDarkly SDK and Redis. * - * @since 4.11.0 + * @since 4.12.0 */ public abstract class Redis { /** diff --git a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java index 70b25b792..8ea2dbd82 100644 --- a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java @@ -32,7 +32,7 @@ * .build(); * * - * @since 4.11.0 + * @since 4.12.0 */ public final class RedisDataStoreBuilder implements PersistentDataStoreFactory { /** diff --git a/src/main/java/com/launchdarkly/client/interfaces/PersistentDataStoreFactory.java b/src/main/java/com/launchdarkly/client/interfaces/PersistentDataStoreFactory.java index 8931247d3..16a5b5544 100644 --- a/src/main/java/com/launchdarkly/client/interfaces/PersistentDataStoreFactory.java +++ b/src/main/java/com/launchdarkly/client/interfaces/PersistentDataStoreFactory.java @@ -10,7 +10,7 @@ * {@link com.launchdarkly.client.Components#persistentDataStore}. * * @see com.launchdarkly.client.Components - * @since 4.11.0 + * @since 4.12.0 */ public interface PersistentDataStoreFactory { /** From ffce8a4f25db3c11abce53f231006da7fa3cea7d Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:33:59 -0800 Subject: [PATCH 05/14] misc cleanup --- src/main/java/com/launchdarkly/client/Components.java | 7 +++++-- src/main/java/com/launchdarkly/client/LDConfig.java | 4 ++-- .../client/integrations/RedisDataStoreImpl.java | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index 0c3a960ce..c0e1ce774 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -22,6 +22,8 @@ public abstract class Components { private static final UpdateProcessorFactory defaultUpdateProcessorFactory = new DefaultUpdateProcessorFactory(); private static final UpdateProcessorFactory nullUpdateProcessorFactory = new NullUpdateProcessorFactory(); + private Components() {} + /** * Returns a configuration object for using the default in-memory implementation of a data store. *

@@ -300,6 +302,7 @@ public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, Fea } } + // Package-private for visibility in tests static final class NullUpdateProcessor implements UpdateProcessor { @Override public Future start() { @@ -315,7 +318,7 @@ public boolean initialized() { public void close() throws IOException {} } - static final class StreamingDataSourceBuilderImpl extends StreamingDataSourceBuilder { + private static final class StreamingDataSourceBuilderImpl extends StreamingDataSourceBuilder { @Override public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, FeatureStore featureStore) { // Note, we log startup messages under the LDClient class to keep logs more readable @@ -356,7 +359,7 @@ public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, Fea } } - static final class PollingDataSourceBuilderImpl extends PollingDataSourceBuilder { + private static final class PollingDataSourceBuilderImpl extends PollingDataSourceBuilder { @Override public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, FeatureStore featureStore) { // Note, we log startup messages under the LDClient class to keep logs more readable diff --git a/src/main/java/com/launchdarkly/client/LDConfig.java b/src/main/java/com/launchdarkly/client/LDConfig.java index bcca40cef..68f74e48e 100644 --- a/src/main/java/com/launchdarkly/client/LDConfig.java +++ b/src/main/java/com/launchdarkly/client/LDConfig.java @@ -40,12 +40,12 @@ public final class LDConfig { private static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 2000; private static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 10000; private static final int DEFAULT_FLUSH_INTERVAL_SECONDS = 5; - private static final long MIN_POLLING_INTERVAL_MILLIS = 30000L; + private static final long MIN_POLLING_INTERVAL_MILLIS = PollingDataSourceBuilder.DEFAULT_POLL_INTERVAL_MILLIS; private static final long DEFAULT_START_WAIT_MILLIS = 5000L; private static final int DEFAULT_SAMPLING_INTERVAL = 0; private static final int DEFAULT_USER_KEYS_CAPACITY = 1000; private static final int DEFAULT_USER_KEYS_FLUSH_INTERVAL_SECONDS = 60 * 5; - private static final long DEFAULT_RECONNECT_TIME_MILLIS = 1000; + private static final long DEFAULT_RECONNECT_TIME_MILLIS = StreamingDataSourceBuilder.DEFAULT_INITIAL_RECONNECT_DELAY_MILLIS; protected static final LDConfig DEFAULT = new Builder().build(); diff --git a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreImpl.java b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreImpl.java index 24e3968b7..c81a02762 100644 --- a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreImpl.java +++ b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreImpl.java @@ -22,7 +22,7 @@ import redis.clients.jedis.Transaction; import redis.clients.util.JedisURIHelper; -class RedisDataStoreImpl implements FeatureStoreCore { +final class RedisDataStoreImpl implements FeatureStoreCore { private static final Logger logger = LoggerFactory.getLogger(RedisDataStoreImpl.class); private final JedisPool pool; From 73f07b11a1d7b7fa32f20c13083d8c3dc221cbce Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:39:41 -0800 Subject: [PATCH 06/14] misc cleanup --- .../java/com/launchdarkly/client/DefaultFeatureRequestor.java | 2 +- src/main/java/com/launchdarkly/client/EventOutputFormatter.java | 2 +- src/main/java/com/launchdarkly/client/HttpErrorException.java | 2 +- src/main/java/com/launchdarkly/client/PollingProcessor.java | 2 +- src/main/java/com/launchdarkly/client/SemanticVersion.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java b/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java index c7cae3d35..dd484ef50 100644 --- a/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java +++ b/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java @@ -25,7 +25,7 @@ /** * Implementation of getting flag data via a polling request. Used by both streaming and polling components. */ -class DefaultFeatureRequestor implements FeatureRequestor { +final class DefaultFeatureRequestor implements FeatureRequestor { private static final Logger logger = LoggerFactory.getLogger(DefaultFeatureRequestor.class); private static final String GET_LATEST_FLAGS_PATH = "/sdk/latest-flags"; private static final String GET_LATEST_SEGMENTS_PATH = "/sdk/latest-segments"; diff --git a/src/main/java/com/launchdarkly/client/EventOutputFormatter.java b/src/main/java/com/launchdarkly/client/EventOutputFormatter.java index e1db41097..a8428ca69 100644 --- a/src/main/java/com/launchdarkly/client/EventOutputFormatter.java +++ b/src/main/java/com/launchdarkly/client/EventOutputFormatter.java @@ -13,7 +13,7 @@ * Rather than creating intermediate objects to represent this schema, we use the Gson streaming * output API to construct JSON directly. */ -class EventOutputFormatter { +final class EventOutputFormatter { private final LDConfig config; EventOutputFormatter(LDConfig config) { diff --git a/src/main/java/com/launchdarkly/client/HttpErrorException.java b/src/main/java/com/launchdarkly/client/HttpErrorException.java index 8f6672bbb..8450e260f 100644 --- a/src/main/java/com/launchdarkly/client/HttpErrorException.java +++ b/src/main/java/com/launchdarkly/client/HttpErrorException.java @@ -1,7 +1,7 @@ package com.launchdarkly.client; @SuppressWarnings("serial") -class HttpErrorException extends Exception { +final class HttpErrorException extends Exception { private final int status; public HttpErrorException(int status) { diff --git a/src/main/java/com/launchdarkly/client/PollingProcessor.java b/src/main/java/com/launchdarkly/client/PollingProcessor.java index d1a0088bf..6cc20cb87 100644 --- a/src/main/java/com/launchdarkly/client/PollingProcessor.java +++ b/src/main/java/com/launchdarkly/client/PollingProcessor.java @@ -17,7 +17,7 @@ import static com.launchdarkly.client.Util.httpErrorMessage; import static com.launchdarkly.client.Util.isHttpErrorRecoverable; -class PollingProcessor implements UpdateProcessor { +final class PollingProcessor implements UpdateProcessor { private static final Logger logger = LoggerFactory.getLogger(PollingProcessor.class); private final FeatureRequestor requestor; diff --git a/src/main/java/com/launchdarkly/client/SemanticVersion.java b/src/main/java/com/launchdarkly/client/SemanticVersion.java index 29a124fd4..7e0ef034c 100644 --- a/src/main/java/com/launchdarkly/client/SemanticVersion.java +++ b/src/main/java/com/launchdarkly/client/SemanticVersion.java @@ -7,7 +7,7 @@ * Simple implementation of semantic version parsing and comparison according to the Semantic * Versions 2.0.0 standard (http://semver.org). */ -class SemanticVersion implements Comparable { +final class SemanticVersion implements Comparable { private static Pattern VERSION_REGEX = Pattern.compile( "^(?0|[1-9]\\d*)(\\.(?0|[1-9]\\d*))?(\\.(?0|[1-9]\\d*))?" + From adeb7a8ae7a6b72cc952a794ba329d759214941a Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:42:53 -0800 Subject: [PATCH 07/14] rename deprecated LDConfig fields to make it obvious when we're using them --- .../com/launchdarkly/client/Components.java | 9 +++++---- .../java/com/launchdarkly/client/LDConfig.java | 18 +++++++++--------- .../com/launchdarkly/client/LDConfigTest.java | 6 ++++-- .../client/StreamProcessorTest.java | 4 ++-- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index c0e1ce774..3efdafc13 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -276,13 +276,14 @@ public UpdateProcessor createUpdateProcessor(String sdkKey, LDConfig config, Fea // into using externalUpdatesOnly() by LDConfig.Builder. if (config.stream) { return streamingDataSource() - .baseUri(config.streamURI) - .initialReconnectDelayMillis(config.reconnectTimeMs) + .baseUri(config.deprecatedStreamURI) + .pollingBaseUri(config.deprecatedBaseURI) + .initialReconnectDelayMillis(config.deprecatedReconnectTimeMs) .createUpdateProcessor(sdkKey, config, featureStore); } else { return pollingDataSource() - .baseUri(config.baseURI) - .pollIntervalMillis(config.pollingIntervalMillis) + .baseUri(config.deprecatedBaseURI) + .pollIntervalMillis(config.deprecatedPollingIntervalMillis) .createUpdateProcessor(sdkKey, config, featureStore); } } diff --git a/src/main/java/com/launchdarkly/client/LDConfig.java b/src/main/java/com/launchdarkly/client/LDConfig.java index 68f74e48e..fedbbc94a 100644 --- a/src/main/java/com/launchdarkly/client/LDConfig.java +++ b/src/main/java/com/launchdarkly/client/LDConfig.java @@ -49,9 +49,9 @@ public final class LDConfig { protected static final LDConfig DEFAULT = new Builder().build(); - final URI baseURI; + final URI deprecatedBaseURI; final URI eventsURI; - final URI streamURI; + final URI deprecatedStreamURI; final int capacity; final int flushInterval; final Proxy proxy; @@ -65,10 +65,10 @@ public final class LDConfig { final boolean allAttributesPrivate; final Set privateAttrNames; final boolean sendEvents; - final long pollingIntervalMillis; + final long deprecatedPollingIntervalMillis; final long startWaitMillis; final int samplingInterval; - final long reconnectTimeMs; + final long deprecatedReconnectTimeMs; final int userKeysCapacity; final int userKeysFlushInterval; final boolean inlineUsersInEvents; @@ -80,13 +80,13 @@ public final class LDConfig { final TimeUnit socketTimeoutUnit; protected LDConfig(Builder builder) { - this.baseURI = builder.baseURI; + this.deprecatedBaseURI = builder.baseURI; this.eventsURI = builder.eventsURI; this.capacity = builder.capacity; this.flushInterval = builder.flushIntervalSeconds; this.proxy = builder.proxy(); this.proxyAuthenticator = builder.proxyAuthenticator(); - this.streamURI = builder.streamURI; + this.deprecatedStreamURI = builder.streamURI; this.stream = builder.stream; this.deprecatedFeatureStore = builder.featureStore; this.dataStoreFactory = builder.dataStoreFactory; @@ -97,13 +97,13 @@ protected LDConfig(Builder builder) { this.privateAttrNames = new HashSet<>(builder.privateAttrNames); this.sendEvents = builder.sendEvents; if (builder.pollingIntervalMillis < MIN_POLLING_INTERVAL_MILLIS) { - this.pollingIntervalMillis = MIN_POLLING_INTERVAL_MILLIS; + this.deprecatedPollingIntervalMillis = MIN_POLLING_INTERVAL_MILLIS; } else { - this.pollingIntervalMillis = builder.pollingIntervalMillis; + this.deprecatedPollingIntervalMillis = builder.pollingIntervalMillis; } this.startWaitMillis = builder.startWaitMillis; this.samplingInterval = builder.samplingInterval; - this.reconnectTimeMs = builder.reconnectTimeMillis; + this.deprecatedReconnectTimeMs = builder.reconnectTimeMillis; this.userKeysCapacity = builder.userKeysCapacity; this.userKeysFlushInterval = builder.userKeysFlushInterval; this.inlineUsersInEvents = builder.inlineUsersInEvents; diff --git a/src/test/java/com/launchdarkly/client/LDConfigTest.java b/src/test/java/com/launchdarkly/client/LDConfigTest.java index f7bfb689a..fbbc881d5 100644 --- a/src/test/java/com/launchdarkly/client/LDConfigTest.java +++ b/src/test/java/com/launchdarkly/client/LDConfigTest.java @@ -71,14 +71,16 @@ public void testProxyAuthPartialConfig() { @Test public void testMinimumPollingIntervalIsEnforcedProperly(){ + @SuppressWarnings("deprecation") LDConfig config = new LDConfig.Builder().pollingIntervalMillis(10L).build(); - assertEquals(30000L, config.pollingIntervalMillis); + assertEquals(30000L, config.deprecatedPollingIntervalMillis); } @Test public void testPollingIntervalIsEnforcedProperly(){ + @SuppressWarnings("deprecation") LDConfig config = new LDConfig.Builder().pollingIntervalMillis(30001L).build(); - assertEquals(30001L, config.pollingIntervalMillis); + assertEquals(30001L, config.deprecatedPollingIntervalMillis); } @Test diff --git a/src/test/java/com/launchdarkly/client/StreamProcessorTest.java b/src/test/java/com/launchdarkly/client/StreamProcessorTest.java index 081f490e5..66608462b 100644 --- a/src/test/java/com/launchdarkly/client/StreamProcessorTest.java +++ b/src/test/java/com/launchdarkly/client/StreamProcessorTest.java @@ -435,7 +435,7 @@ private void testRecoverableHttpError(int status) throws Exception { private StreamProcessor createStreamProcessor(LDConfig config, URI streamUri) { return new StreamProcessor(SDK_KEY, config, mockRequestor, featureStore, new StubEventSourceCreator(), - streamUri, config.reconnectTimeMs); + streamUri, config.deprecatedReconnectTimeMs); } private StreamProcessor createStreamProcessor(URI streamUri) { @@ -444,7 +444,7 @@ private StreamProcessor createStreamProcessor(URI streamUri) { private StreamProcessor createStreamProcessorWithRealHttp(LDConfig config, URI streamUri) { return new StreamProcessor(SDK_KEY, config, mockRequestor, featureStore, null, - streamUri, config.reconnectTimeMs); + streamUri, config.deprecatedReconnectTimeMs); } private String featureJson(String key, int version) { From a1e6f58b1f7b63849bf7c64857b1700142b43eaa Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 11:43:29 -0800 Subject: [PATCH 08/14] comment --- src/main/java/com/launchdarkly/client/LDClient.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/launchdarkly/client/LDClient.java b/src/main/java/com/launchdarkly/client/LDClient.java index 6d783a3cd..8e713fd0f 100644 --- a/src/main/java/com/launchdarkly/client/LDClient.java +++ b/src/main/java/com/launchdarkly/client/LDClient.java @@ -31,7 +31,9 @@ * a single {@code LDClient} for the lifetime of their application. */ public final class LDClient implements LDClientInterface { + // Package-private so other classes can log under the top-level logger's tag static final Logger logger = LoggerFactory.getLogger(LDClient.class); + private static final String HMAC_ALGORITHM = "HmacSHA256"; static final String CLIENT_VERSION = getClientVersion(); From 29c8ea35cc11013ab411bfd9211b890bdf73f750 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 12:32:11 -0800 Subject: [PATCH 09/14] fix deprecated property reference --- .../java/com/launchdarkly/client/DefaultFeatureRequestor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java b/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java index dd484ef50..b0bfdc02a 100644 --- a/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java +++ b/src/main/java/com/launchdarkly/client/DefaultFeatureRequestor.java @@ -36,11 +36,13 @@ final class DefaultFeatureRequestor implements FeatureRequestor { private final LDConfig config; private final URI baseUri; private final OkHttpClient httpClient; + private final boolean useCache; DefaultFeatureRequestor(String sdkKey, LDConfig config, URI baseUri, boolean useCache) { this.sdkKey = sdkKey; this.config = config; // this is no longer the source of truth for baseURI, but it can still affect HTTP behavior this.baseUri = baseUri; + this.useCache = useCache; OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder(); configureHttpClientBuilder(config, httpBuilder); @@ -98,7 +100,7 @@ private String get(String path) throws IOException, HttpErrorException { } logger.debug("Get flag(s) response: " + response.toString() + " with body: " + body); logger.debug("Network response: " + response.networkResponse()); - if(!config.stream) { + if (useCache) { logger.debug("Cache hit count: " + httpClient.cache().hitCount() + " Cache network Count: " + httpClient.cache().networkCount()); logger.debug("Cache response: " + response.cacheResponse()); } From 774f328538ed3d6b44d0115cbac53e229fd98b0e Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 12:32:30 -0800 Subject: [PATCH 10/14] javadoc fix --- .../client/integrations/StreamingDataSourceBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java b/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java index 0c54c8cdc..b62ca4367 100644 --- a/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/StreamingDataSourceBuilder.java @@ -20,7 +20,7 @@ * These properties will override any equivalent deprecated properties that were set with {@code LDConfig.Builder}, * such as {@link com.launchdarkly.client.LDConfig.Builder#reconnectTimeMs(long)}. *

- * Note that this class is abstract; the actual implementation is created by calling {@link Components#pollingDataSource()}. + * Note that this class is abstract; the actual implementation is created by calling {@link Components#streamingDataSource()}. * * @since 4.12.0 */ From e43b4b080daa4f00c1731c71d14b24e337307260 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 13:11:54 -0800 Subject: [PATCH 11/14] fix example code --- .../integrations/RedisDataStoreBuilder.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java index 8ea2dbd82..5c63ca819 100644 --- a/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/RedisDataStoreBuilder.java @@ -23,13 +23,15 @@ * Builder calls can be chained, for example: * *


- * LDConfig config = new LDConfig.Builder()
- *      .dataStore(
- *           Redis.dataStore()
- *               .database(1)
- *               .caching(FeatureStoreCacheConfig.enabled().ttlSeconds(60))
- *      )
- *      .build();
+ *     LDConfig config = new LDConfig.Builder()
+ *         .dataStore(
+ *             Components.persistentDataStore(
+ *                 Redis.dataStore()
+ *                     .url("redis://my-redis-host")
+ *                     .database(1)
+ *             ).cacheSeconds(15)
+ *         )
+ *         .build();
  * 
* * @since 4.12.0 From 85d0c7a82980c77a3e6310b6f8bd257dc1a90b88 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Wed, 22 Jan 2020 14:45:18 -0800 Subject: [PATCH 12/14] doc comment copyedit --- src/main/java/com/launchdarkly/client/Components.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index 3efdafc13..9dc560fc1 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -206,7 +206,7 @@ public static UpdateProcessorFactory defaultUpdateProcessor() { } /** - * Returns a configuration object that disables connecting for feature flag updates. + * Returns a configuration object that disables a direct connection with LaunchDarkly for feature flag updates. *

* Passing this to {@link LDConfig.Builder#dataSource(UpdateProcessorFactory)} causes the SDK * not to retrieve feature flag data from LaunchDarkly, regardless of any other configuration. From 0d9053915f6d40de7c2a9ea79bd66c4314704280 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Jan 2020 15:02:40 -0800 Subject: [PATCH 13/14] consistent verbiage --- src/main/java/com/launchdarkly/client/Components.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/launchdarkly/client/Components.java b/src/main/java/com/launchdarkly/client/Components.java index 9dc560fc1..a700a0810 100644 --- a/src/main/java/com/launchdarkly/client/Components.java +++ b/src/main/java/com/launchdarkly/client/Components.java @@ -127,7 +127,7 @@ public static EventProcessorFactory nullEventProcessor() { } /** - * Returns a configuration object for using streaming mode to get feature flag data. + * Returns a configurable factory for using streaming mode to get feature flag data. *

* By default, the SDK uses a streaming connection to receive feature flag data from LaunchDarkly. To use the * default behavior, you do not need to call this method. However, if you want to customize the behavior of From a8c4c0223d1171f523981fe123342ee925d3894f Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Thu, 23 Jan 2020 15:42:34 -0800 Subject: [PATCH 14/14] Copyedit --- .../client/integrations/PollingDataSourceBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java b/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java index 1628e6b4a..7999b63c1 100644 --- a/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java +++ b/src/main/java/com/launchdarkly/client/integrations/PollingDataSourceBuilder.java @@ -8,7 +8,7 @@ /** * Contains methods for configuring the polling data source. *

- * This is not the default behavior; by default, the SDK uses a streaming connection to receive feature flag + * Polling is not the default behavior; by default, the SDK uses a streaming connection to receive feature flag * data from LaunchDarkly. In polling mode, the SDK instead makes a new HTTP request to LaunchDarkly at regular * intervals. HTTP caching allows it to avoid redundantly downloading data if there have been no changes, but * polling is still less efficient than streaming and should only be used on the advice of LaunchDarkly support.