From 82172f27e849ff3b81d51b2d69a9a2bbfbf2f89b Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Mon, 1 Apr 2024 17:40:22 -0700 Subject: [PATCH 1/6] add custom httpclient to AsyncEventHandler and ODPApiManager --- core-httpclient-impl/build.gradle | 1 + .../com/optimizely/ab/OptimizelyFactory.java | 44 ++++++++++++--- .../ab/event/AsyncEventHandler.java | 55 ++++++++++++++----- .../ab/odp/DefaultODPApiManager.java | 19 +++++-- 4 files changed, 93 insertions(+), 26 deletions(-) diff --git a/core-httpclient-impl/build.gradle b/core-httpclient-impl/build.gradle index b43c70269..f871de2bc 100644 --- a/core-httpclient-impl/build.gradle +++ b/core-httpclient-impl/build.gradle @@ -1,4 +1,5 @@ dependencies { + implementation 'org.jetbrains:annotations:24.0.0' compile project(':core-api') compileOnly group: 'com.google.code.gson', name: 'gson', version: gsonVersion diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java index 1c6ee2820..f49c4a5d3 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java @@ -279,23 +279,39 @@ public static Optimizely newDefaultInstance(String sdkKey, String fallback, Stri * @param customHttpClient Customizable CloseableHttpClient to build OptimizelyHttpClient. * @return A new Optimizely instance */ - public static Optimizely newDefaultInstance(String sdkKey, String fallback, String datafileAccessToken, CloseableHttpClient customHttpClient) { + public static Optimizely newDefaultInstance( + String sdkKey, + String fallback, + String datafileAccessToken, + CloseableHttpClient customHttpClient + ) { + OptimizelyHttpClient optimizelyHttpClient = customHttpClient == null ? null : new OptimizelyHttpClient(customHttpClient); + NotificationCenter notificationCenter = new NotificationCenter(); - OptimizelyHttpClient optimizelyHttpClient = new OptimizelyHttpClient(customHttpClient); - HttpProjectConfigManager.Builder builder; - builder = HttpProjectConfigManager.builder() + + HttpProjectConfigManager.Builder builder = HttpProjectConfigManager.builder() .withDatafile(fallback) .withNotificationCenter(notificationCenter) - .withOptimizelyHttpClient(customHttpClient == null ? null : optimizelyHttpClient) + .withOptimizelyHttpClient(optimizelyHttpClient) .withSdkKey(sdkKey); if (datafileAccessToken != null) { builder.withDatafileAccessToken(datafileAccessToken); } - return newDefaultInstance(builder.build(), notificationCenter); + ProjectConfigManager configManager = builder.build(); + + EventHandler eventHandler = AsyncEventHandler.builder() + .withOptimizelyHttpClient(optimizelyHttpClient) + .build(); + + ODPApiManager odpApiManager = new DefaultODPApiManager(optimizelyHttpClient); + + return newDefaultInstance(configManager, notificationCenter, eventHandler, odpApiManager); } + + /** * Returns a new Optimizely instance based on preset configuration. * EventHandler - {@link AsyncEventHandler} @@ -329,6 +345,19 @@ public static Optimizely newDefaultInstance(ProjectConfigManager configManager, * @return A new Optimizely instance * */ public static Optimizely newDefaultInstance(ProjectConfigManager configManager, NotificationCenter notificationCenter, EventHandler eventHandler) { + return newDefaultInstance(configManager, notificationCenter, eventHandler, null); + } + + /** + * Returns a new Optimizely instance based on preset configuration. + * + * @param configManager The {@link ProjectConfigManager} supplied to Optimizely instance. + * @param notificationCenter The {@link ProjectConfigManager} supplied to Optimizely instance. + * @param eventHandler The {@link EventHandler} supplied to Optimizely instance. + * @param odpApiManager The {@link ODPApiManager} supplied to Optimizely instance. + * @return A new Optimizely instance + * */ + public static Optimizely newDefaultInstance(ProjectConfigManager configManager, NotificationCenter notificationCenter, EventHandler eventHandler, ODPApiManager odpApiManager) { if (notificationCenter == null) { notificationCenter = new NotificationCenter(); } @@ -338,9 +367,8 @@ public static Optimizely newDefaultInstance(ProjectConfigManager configManager, .withNotificationCenter(notificationCenter) .build(); - ODPApiManager defaultODPApiManager = new DefaultODPApiManager(); ODPManager odpManager = ODPManager.builder() - .withApiManager(defaultODPApiManager) + .withApiManager(odpApiManager != null ? odpApiManager : new DefaultODPApiManager()) .build(); return Optimizely.builder() diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java index 391f89b57..0ec696ca6 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java @@ -20,6 +20,7 @@ import com.optimizely.ab.OptimizelyHttpClient; import com.optimizely.ab.annotations.VisibleForTesting; +import com.optimizely.ab.config.HttpProjectConfigManager; import com.optimizely.ab.internal.PropertyUtils; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; @@ -29,6 +30,7 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -108,21 +110,41 @@ public AsyncEventHandler(int queueCapacity, int validateAfter, long closeTimeout, TimeUnit closeTimeoutUnit) { + this(queueCapacity, + numWorkers, + maxConnections, + connectionsPerRoute, + validateAfter, + closeTimeout, + closeTimeoutUnit, + null); + } + + public AsyncEventHandler(int queueCapacity, + int numWorkers, + int maxConnections, + int connectionsPerRoute, + int validateAfter, + long closeTimeout, + TimeUnit closeTimeoutUnit, + @Nullable OptimizelyHttpClient httpClient) { + if (httpClient != null) { + this.httpClient = httpClient; + } else { + maxConnections = validateInput("maxConnections", maxConnections, DEFAULT_MAX_CONNECTIONS); + connectionsPerRoute = validateInput("connectionsPerRoute", connectionsPerRoute, DEFAULT_MAX_PER_ROUTE); + validateAfter = validateInput("validateAfter", validateAfter, DEFAULT_VALIDATE_AFTER_INACTIVITY); + this.httpClient = OptimizelyHttpClient.builder() + .withMaxTotalConnections(maxConnections) + .withMaxPerRoute(connectionsPerRoute) + .withValidateAfterInactivity(validateAfter) + // infrequent event discards observed. staled connections force-closed after a long idle time. + .withEvictIdleConnections(1L, TimeUnit.MINUTES) + .build(); + } queueCapacity = validateInput("queueCapacity", queueCapacity, DEFAULT_QUEUE_CAPACITY); numWorkers = validateInput("numWorkers", numWorkers, DEFAULT_NUM_WORKERS); - maxConnections = validateInput("maxConnections", maxConnections, DEFAULT_MAX_CONNECTIONS); - connectionsPerRoute = validateInput("connectionsPerRoute", connectionsPerRoute, DEFAULT_MAX_PER_ROUTE); - validateAfter = validateInput("validateAfter", validateAfter, DEFAULT_VALIDATE_AFTER_INACTIVITY); - - this.httpClient = OptimizelyHttpClient.builder() - .withMaxTotalConnections(maxConnections) - .withMaxPerRoute(connectionsPerRoute) - .withValidateAfterInactivity(validateAfter) - // infrequent event discards observed. staled connections force-closed after a long idle time. - .withEvictIdleConnections(1L, TimeUnit.MINUTES) - .build(); - this.workerExecutor = new ThreadPoolExecutor(numWorkers, numWorkers, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueCapacity), @@ -287,6 +309,7 @@ public static class Builder { int validateAfterInactivity = PropertyUtils.getInteger(CONFIG_VALIDATE_AFTER_INACTIVITY, DEFAULT_VALIDATE_AFTER_INACTIVITY); private long closeTimeout = Long.MAX_VALUE; private TimeUnit closeTimeoutUnit = TimeUnit.MILLISECONDS; + private OptimizelyHttpClient httpClient; public Builder withQueueCapacity(int queueCapacity) { if (queueCapacity <= 0) { @@ -329,6 +352,11 @@ public Builder withCloseTimeout(long closeTimeout, TimeUnit unit) { return this; } + public Builder withOptimizelyHttpClient(OptimizelyHttpClient httpClient) { + this.httpClient = httpClient; + return this; + } + public AsyncEventHandler build() { return new AsyncEventHandler( queueCapacity, @@ -337,7 +365,8 @@ public AsyncEventHandler build() { maxPerRoute, validateAfterInactivity, closeTimeout, - closeTimeoutUnit + closeTimeoutUnit, + httpClient ); } } diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 3a7ae3291..276928530 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -15,8 +15,12 @@ */ package com.optimizely.ab.odp; +import com.optimizely.ab.Optimizely; import com.optimizely.ab.OptimizelyHttpClient; import com.optimizely.ab.annotations.VisibleForTesting; +import com.optimizely.ab.config.HttpProjectConfigManager; +import com.optimizely.ab.event.AsyncEventHandler; +import com.optimizely.ab.internal.PropertyUtils; import com.optimizely.ab.odp.parser.ResponseJsonParser; import com.optimizely.ab.odp.parser.ResponseJsonParserFactory; import org.apache.http.StatusLine; @@ -27,11 +31,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; public class DefaultODPApiManager implements ODPApiManager { private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class); @@ -40,7 +46,7 @@ public class DefaultODPApiManager implements ODPApiManager { private final OptimizelyHttpClient httpClientEvents; public DefaultODPApiManager() { - this(OptimizelyHttpClient.builder().build()); + this(null); } public DefaultODPApiManager(int segmentFetchTimeoutMillis, int eventDispatchTimeoutMillis) { @@ -53,10 +59,13 @@ public DefaultODPApiManager(int segmentFetchTimeoutMillis, int eventDispatchTime } } - @VisibleForTesting - DefaultODPApiManager(OptimizelyHttpClient httpClient) { - this.httpClientSegments = httpClient; - this.httpClientEvents = httpClient; + public DefaultODPApiManager(@Nullable OptimizelyHttpClient httpClient) { + OptimizelyHttpClient customHttpClient = httpClient; + if (httpClient == null) { + customHttpClient = OptimizelyHttpClient.builder().build(); + } + this.httpClientSegments = customHttpClient; + this.httpClientEvents = customHttpClient; } @VisibleForTesting From ca66b36f959194ce7fafb5b38e9ce7d09bf94865 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Mon, 1 Apr 2024 17:42:43 -0700 Subject: [PATCH 2/6] clean up --- core-httpclient-impl/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/core-httpclient-impl/build.gradle b/core-httpclient-impl/build.gradle index f871de2bc..b43c70269 100644 --- a/core-httpclient-impl/build.gradle +++ b/core-httpclient-impl/build.gradle @@ -1,5 +1,4 @@ dependencies { - implementation 'org.jetbrains:annotations:24.0.0' compile project(':core-api') compileOnly group: 'com.google.code.gson', name: 'gson', version: gsonVersion From bf66394e3a6dcf0470488fe79483839ceb20bdb1 Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Tue, 2 Apr 2024 15:36:02 -0700 Subject: [PATCH 3/6] new tests --- .../java/com/optimizely/ab/Optimizely.java | 4 +- .../ab/event/BatchEventProcessor.java | 4 +- .../optimizely/ab/odp/ODPEventManager.java | 4 +- .../com/optimizely/ab/odp/ODPManager.java | 1 + .../optimizely/ab/odp/ODPSegmentManager.java | 5 +- .../optimizely/ab/OptimizelyHttpClient.java | 1 - .../ab/config/HttpProjectConfigManager.java | 3 +- .../ab/event/AsyncEventHandler.java | 5 +- .../ab/odp/DefaultODPApiManager.java | 6 ++- .../optimizely/ab/OptimizelyFactoryTest.java | 47 +++++++++++++------ .../config/HttpProjectConfigManagerTest.java | 2 - .../ab/event/AsyncEventHandlerTest.java | 6 --- .../ab/odp/DefaultODPApiManagerTest.java | 1 - 13 files changed, 52 insertions(+), 37 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/Optimizely.java b/core-api/src/main/java/com/optimizely/ab/Optimizely.java index 3524cb24a..9ede95ec8 100644 --- a/core-api/src/main/java/com/optimizely/ab/Optimizely.java +++ b/core-api/src/main/java/com/optimizely/ab/Optimizely.java @@ -77,7 +77,6 @@ public class Optimizely implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(Optimizely.class); final DecisionService decisionService; - @VisibleForTesting @Deprecated final EventHandler eventHandler; @VisibleForTesting @@ -87,7 +86,8 @@ public class Optimizely implements AutoCloseable { public final List defaultDecideOptions; - private final ProjectConfigManager projectConfigManager; + @VisibleForTesting + final ProjectConfigManager projectConfigManager; @Nullable private final OptimizelyConfigManager optimizelyConfigManager; diff --git a/core-api/src/main/java/com/optimizely/ab/event/BatchEventProcessor.java b/core-api/src/main/java/com/optimizely/ab/event/BatchEventProcessor.java index daf81d71a..9ec0a1dce 100644 --- a/core-api/src/main/java/com/optimizely/ab/event/BatchEventProcessor.java +++ b/core-api/src/main/java/com/optimizely/ab/event/BatchEventProcessor.java @@ -16,6 +16,7 @@ */ package com.optimizely.ab.event; +import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.config.ProjectConfig; import com.optimizely.ab.event.internal.EventFactory; import com.optimizely.ab.event.internal.UserEvent; @@ -57,7 +58,8 @@ public class BatchEventProcessor implements EventProcessor, AutoCloseable { private static final Object FLUSH_SIGNAL = new Object(); private final BlockingQueue eventQueue; - private final EventHandler eventHandler; + @VisibleForTesting + public final EventHandler eventHandler; final int batchSize; final long flushInterval; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPEventManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPEventManager.java index efcdd6cda..a05b22aa4 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/ODPEventManager.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPEventManager.java @@ -53,8 +53,8 @@ public class ODPEventManager { // needs to see the change immediately. private volatile ODPConfig odpConfig; private EventDispatcherThread eventDispatcherThread; - - private final ODPApiManager apiManager; + @VisibleForTesting + public final ODPApiManager apiManager; // The eventQueue needs to be thread safe. We are not doing anything extra for thread safety here // because `LinkedBlockingQueue` itself is thread safe. diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java index 4f1ddc52d..252db12dc 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java @@ -16,6 +16,7 @@ */ package com.optimizely.ab.odp; +import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.internal.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPSegmentManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPSegmentManager.java index 8cd917269..6caae29ca 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/ODPSegmentManager.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPSegmentManager.java @@ -16,6 +16,7 @@ */ package com.optimizely.ab.odp; +import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.internal.Cache; import com.optimizely.ab.internal.DefaultLRUCache; import com.optimizely.ab.odp.parser.ResponseJsonParser; @@ -31,8 +32,8 @@ public class ODPSegmentManager { private static final Logger logger = LoggerFactory.getLogger(ODPSegmentManager.class); private static final String SEGMENT_URL_PATH = "/v3/graphql"; - - private final ODPApiManager apiManager; + @VisibleForTesting + public final ODPApiManager apiManager; private volatile ODPConfig odpConfig; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyHttpClient.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyHttpClient.java index f4040276f..363bce59c 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyHttpClient.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyHttpClient.java @@ -41,7 +41,6 @@ public class OptimizelyHttpClient implements Closeable { private static final Logger logger = LoggerFactory.getLogger(OptimizelyHttpClient.class); - private final CloseableHttpClient httpClient; OptimizelyHttpClient(CloseableHttpClient httpClient) { diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/config/HttpProjectConfigManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/config/HttpProjectConfigManager.java index 15325350f..7840d92c4 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/config/HttpProjectConfigManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/config/HttpProjectConfigManager.java @@ -58,7 +58,8 @@ public class HttpProjectConfigManager extends PollingProjectConfigManager { private static final Logger logger = LoggerFactory.getLogger(HttpProjectConfigManager.class); - private final OptimizelyHttpClient httpClient; + @VisibleForTesting + public final OptimizelyHttpClient httpClient; private final URI uri; private final String datafileAccessToken; private String datafileLastModified; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java index 0ec696ca6..402dc4fbf 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java @@ -30,7 +30,6 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.StringEntity; -import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +44,7 @@ import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; /** * {@link EventHandler} implementation that queues events and has a separate pool of threads responsible @@ -67,7 +67,8 @@ public class AsyncEventHandler implements EventHandler, AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(AsyncEventHandler.class); private static final ProjectConfigResponseHandler EVENT_RESPONSE_HANDLER = new ProjectConfigResponseHandler(); - private final OptimizelyHttpClient httpClient; + @VisibleForTesting + public final OptimizelyHttpClient httpClient; private final ExecutorService workerExecutor; private final long closeTimeout; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 276928530..93f0aa6f3 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -42,8 +42,10 @@ public class DefaultODPApiManager implements ODPApiManager { private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class); - private final OptimizelyHttpClient httpClientSegments; - private final OptimizelyHttpClient httpClientEvents; + @VisibleForTesting + public final OptimizelyHttpClient httpClientSegments; + @VisibleForTesting + public final OptimizelyHttpClient httpClientEvents; public DefaultODPApiManager() { this(null); diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java index aaa3a67fa..b7440bc9f 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java @@ -25,12 +25,11 @@ import com.optimizely.ab.event.BatchEventProcessor; import com.optimizely.ab.internal.PropertyUtils; import com.optimizely.ab.notification.NotificationCenter; -import org.apache.http.HttpHost; -import org.apache.http.conn.routing.HttpRoutePlanner; +import com.optimizely.ab.odp.DefaultODPApiManager; +import com.optimizely.ab.odp.ODPManager; +import org.apache.http.client.HttpClient; import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -247,21 +246,39 @@ public void newDefaultInstanceWithDatafileAccessToken() throws Exception { @Test public void newDefaultInstanceWithDatafileAccessTokenAndCustomHttpClient() throws Exception { - // Add custom Proxy and Port here - int port = 443; - String proxyHostName = "someProxy.com"; - HttpHost proxyHost = new HttpHost(proxyHostName, port); + CloseableHttpClient customHttpClient = HttpClients.custom().build(); - HttpRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxyHost); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder = clientBuilder.setRoutePlanner(routePlanner); - - CloseableHttpClient httpClient = clientBuilder.build(); String datafileString = Resources.toString(Resources.getResource("valid-project-config-v4.json"), Charsets.UTF_8); - optimizely = OptimizelyFactory.newDefaultInstance("sdk-key", datafileString, "auth-token", httpClient); + optimizely = OptimizelyFactory.newDefaultInstance("sdk-key", datafileString, "auth-token", customHttpClient); assertTrue(optimizely.isValid()); + + // HttpProjectConfigManager should be using the customHttpClient + + HttpProjectConfigManager projectConfigManager = (HttpProjectConfigManager) optimizely.projectConfigManager; + assert(doesUseCustomHttpClient(projectConfigManager.httpClient, customHttpClient)); + + // AsyncEventHandler should be using the customHttpClient + + BatchEventProcessor eventProcessor = (BatchEventProcessor) optimizely.eventProcessor; + AsyncEventHandler eventHandler = (AsyncEventHandler)eventProcessor.eventHandler; + assert(doesUseCustomHttpClient(eventHandler.httpClient, customHttpClient)); + + // ODPManager should be using the customHttpClient + + ODPManager odpManager = optimizely.getODPManager(); + assert odpManager != null; + DefaultODPApiManager odpApiManager = (DefaultODPApiManager) odpManager.getEventManager().apiManager; + assert(doesUseCustomHttpClient(odpApiManager.httpClientSegments, customHttpClient)); + assert(doesUseCustomHttpClient(odpApiManager.httpClientEvents, customHttpClient)); + } + + boolean doesUseCustomHttpClient(OptimizelyHttpClient optimizelyHttpClient, HttpClient customHttpClient) { + if (optimizelyHttpClient == null) { + return false; + } + return optimizelyHttpClient.getHttpClient() == customHttpClient; } + public ProjectConfigManager projectConfigManagerReturningNull = new ProjectConfigManager() { @Override public ProjectConfig getConfig() { diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/config/HttpProjectConfigManagerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/config/HttpProjectConfigManagerTest.java index 9cbc0bb01..77960d518 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/config/HttpProjectConfigManagerTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/config/HttpProjectConfigManagerTest.java @@ -20,7 +20,6 @@ import com.google.common.io.Resources; import com.optimizely.ab.OptimizelyHttpClient; import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; @@ -44,7 +43,6 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static org.junit.Assert.*; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java index 79a4105a1..d3e7ad846 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java @@ -22,14 +22,9 @@ import com.optimizely.ab.event.internal.payload.EventBatch; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; import java.io.IOException; import java.util.HashMap; @@ -38,7 +33,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.mockito.runners.MockitoJUnitRunner; import static com.optimizely.ab.event.AsyncEventHandler.builder; diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java index a268cacc7..780831ff2 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java @@ -33,7 +33,6 @@ import java.util.List; import static org.junit.Assert.*; -import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; public class DefaultODPApiManagerTest { From f1c35f4e90c8ad14776e53dfe0b2e97d6bc5286e Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Tue, 2 Apr 2024 16:21:00 -0700 Subject: [PATCH 4/6] clean up --- .../java/com/optimizely/ab/odp/ODPManager.java | 2 -- .../optimizely/ab/event/AsyncEventHandler.java | 1 - .../optimizely/ab/odp/DefaultODPApiManager.java | 15 +++++---------- .../com/optimizely/ab/OptimizelyFactoryTest.java | 3 +-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java b/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java index 252db12dc..3a47e3f04 100644 --- a/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java +++ b/core-api/src/main/java/com/optimizely/ab/odp/ODPManager.java @@ -16,13 +16,11 @@ */ package com.optimizely.ab.odp; -import com.optimizely.ab.annotations.VisibleForTesting; import com.optimizely.ab.internal.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java index 402dc4fbf..94c72ff2e 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/event/AsyncEventHandler.java @@ -20,7 +20,6 @@ import com.optimizely.ab.OptimizelyHttpClient; import com.optimizely.ab.annotations.VisibleForTesting; -import com.optimizely.ab.config.HttpProjectConfigManager; import com.optimizely.ab.internal.PropertyUtils; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java index 93f0aa6f3..b733427de 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java @@ -15,12 +15,8 @@ */ package com.optimizely.ab.odp; -import com.optimizely.ab.Optimizely; import com.optimizely.ab.OptimizelyHttpClient; import com.optimizely.ab.annotations.VisibleForTesting; -import com.optimizely.ab.config.HttpProjectConfigManager; -import com.optimizely.ab.event.AsyncEventHandler; -import com.optimizely.ab.internal.PropertyUtils; import com.optimizely.ab.odp.parser.ResponseJsonParser; import com.optimizely.ab.odp.parser.ResponseJsonParserFactory; import org.apache.http.StatusLine; @@ -37,7 +33,6 @@ import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.concurrent.TimeUnit; public class DefaultODPApiManager implements ODPApiManager { private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class); @@ -61,13 +56,13 @@ public DefaultODPApiManager(int segmentFetchTimeoutMillis, int eventDispatchTime } } - public DefaultODPApiManager(@Nullable OptimizelyHttpClient httpClient) { - OptimizelyHttpClient customHttpClient = httpClient; + public DefaultODPApiManager(@Nullable OptimizelyHttpClient customHttpClient) { + OptimizelyHttpClient httpClient = customHttpClient; if (httpClient == null) { - customHttpClient = OptimizelyHttpClient.builder().build(); + httpClient = OptimizelyHttpClient.builder().build(); } - this.httpClientSegments = customHttpClient; - this.httpClientEvents = customHttpClient; + this.httpClientSegments = httpClient; + this.httpClientEvents = httpClient; } @VisibleForTesting diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java index b7440bc9f..a15085645 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/OptimizelyFactoryTest.java @@ -27,7 +27,6 @@ import com.optimizely.ab.notification.NotificationCenter; import com.optimizely.ab.odp.DefaultODPApiManager; import com.optimizely.ab.odp.ODPManager; -import org.apache.http.client.HttpClient; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.junit.After; @@ -272,7 +271,7 @@ public void newDefaultInstanceWithDatafileAccessTokenAndCustomHttpClient() throw assert(doesUseCustomHttpClient(odpApiManager.httpClientEvents, customHttpClient)); } - boolean doesUseCustomHttpClient(OptimizelyHttpClient optimizelyHttpClient, HttpClient customHttpClient) { + boolean doesUseCustomHttpClient(OptimizelyHttpClient optimizelyHttpClient, CloseableHttpClient customHttpClient) { if (optimizelyHttpClient == null) { return false; } From 5acff945c1624ed8125ac2ce289bb9a8312e054c Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 3 Apr 2024 14:40:36 -0700 Subject: [PATCH 5/6] add more tests --- .../ab/event/AsyncEventHandlerTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java b/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java index d3e7ad846..f87811b96 100644 --- a/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java +++ b/core-httpclient-impl/src/test/java/com/optimizely/ab/event/AsyncEventHandlerTest.java @@ -118,6 +118,30 @@ public void testShutdownAndForcedTermination() throws Exception { verify(mockHttpClient).close(); } + @Test + public void testBuilderWithCustomHttpClient() { + OptimizelyHttpClient customHttpClient = OptimizelyHttpClient.builder().build(); + + AsyncEventHandler eventHandler = builder() + .withOptimizelyHttpClient(customHttpClient) + .withMaxTotalConnections(1) + .withMaxPerRoute(2) + .withCloseTimeout(10, TimeUnit.SECONDS) + .build(); + + assert eventHandler.httpClient == customHttpClient; + } + + @Test + public void testBuilderWithDefaultHttpClient() { + AsyncEventHandler eventHandler = builder() + .withMaxTotalConnections(3) + .withMaxPerRoute(4) + .withCloseTimeout(10, TimeUnit.SECONDS) + .build(); + assert(eventHandler.httpClient != null); + } + @Test public void testInvalidQueueCapacity() { AsyncEventHandler.Builder builder = builder(); From e8977b982655a1a8faf6a08dc0f7e8536cdb72df Mon Sep 17 00:00:00 2001 From: Jae Kim Date: Wed, 3 Apr 2024 15:09:27 -0700 Subject: [PATCH 6/6] clean up --- .../src/main/java/com/optimizely/ab/OptimizelyFactory.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java index f49c4a5d3..f26851375 100644 --- a/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java +++ b/core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyFactory.java @@ -309,9 +309,7 @@ public static Optimizely newDefaultInstance( return newDefaultInstance(configManager, notificationCenter, eventHandler, odpApiManager); } - - - + /** * Returns a new Optimizely instance based on preset configuration. * EventHandler - {@link AsyncEventHandler}