diff --git a/client-java-core/src/main/java/io/featurehub/client/EdgeFeatureHubConfig.java b/client-java-core/src/main/java/io/featurehub/client/EdgeFeatureHubConfig.java index 3205f64..80e0053 100644 --- a/client-java-core/src/main/java/io/featurehub/client/EdgeFeatureHubConfig.java +++ b/client-java-core/src/main/java/io/featurehub/client/EdgeFeatureHubConfig.java @@ -8,6 +8,7 @@ import java.util.ServiceLoader; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class EdgeFeatureHubConfig implements FeatureHubConfig { @@ -81,6 +82,16 @@ public void init() { } } + @Override + public void init(long timeout, TimeUnit unit) { + try { + final Future futureContext = newContext().build(); + futureContext.get(timeout, unit); + } catch (Exception e) { + log.error("Failed to initialize FeatureHub client", e); + } + } + @Override public boolean isServerEvaluation() { return serverEvaluation; diff --git a/client-java-core/src/main/java/io/featurehub/client/FeatureHubConfig.java b/client-java-core/src/main/java/io/featurehub/client/FeatureHubConfig.java index 6ed3633..9aa3830 100644 --- a/client-java-core/src/main/java/io/featurehub/client/FeatureHubConfig.java +++ b/client-java-core/src/main/java/io/featurehub/client/FeatureHubConfig.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public interface FeatureHubConfig { @@ -21,6 +22,13 @@ public interface FeatureHubConfig { */ void init(); + /** + * If you are using a client evaluated feature context, this will initialise the service and block until + * the provided timeout or until you have received your first set of features. Server Evaluated contexts + * should not use it because it needs to re-request data from the server each time you change your context. + */ + void init(long timeout, TimeUnit unit); + /** * The API Key indicates this is going to be server based evaluation */ diff --git a/client-java-core/src/test/groovy/io/featurehub/client/EdgeFeatureHubConfigSpec.groovy b/client-java-core/src/test/groovy/io/featurehub/client/EdgeFeatureHubConfigSpec.groovy index 714f650..4c66a85 100644 --- a/client-java-core/src/test/groovy/io/featurehub/client/EdgeFeatureHubConfigSpec.groovy +++ b/client-java-core/src/test/groovy/io/featurehub/client/EdgeFeatureHubConfigSpec.groovy @@ -143,4 +143,41 @@ class EdgeFeatureHubConfigSpec extends Specification { 1 * mockRequest.get() >> Readyness.Ready 0 * _ } + + def "init completes successfully if future resolves within the given time"() { + given: "A client eval feature config" + def config = new EdgeFeatureHubConfig("http://localhost/", "123*abc") + and: "A mock future that completes successfully" + def futureContext = Mock(Future) + def mockContext = Mock(ClientContext) + and: "I mock the context and future" + def clientContext = Mock(ClientContext) + clientContext.build() >> futureContext + config = Spy(config) { + newContext() >> clientContext + } + when: "init is called with a reasonable timeout" + config.init(100, TimeUnit.MILLISECONDS) + then: "The get method on the future should be called with timeout" + 1 * futureContext.get(100, TimeUnit.MILLISECONDS) >> mockContext + 0 * _ + } + + def "init should timeout if future does not complete within the given time"() { + given: "A client eval feature config" + def config = new EdgeFeatureHubConfig("http://localhost/", "123*abc") + and: "A mock future that does not complete in time" + def futureContext = Mock(Future) + and: "I mock the context and future" + def clientContext = Mock(ClientContext) + clientContext.build() >> futureContext + config = Spy(config) { + newContext() >> clientContext + } + when: "init is called with a very short timeout" + config.init(1, TimeUnit.MILLISECONDS) + then: "The get method on the future should be called with timeout" + 1 * futureContext.get(1, TimeUnit.MILLISECONDS) >> { throw new java.util.concurrent.TimeoutException() } + 0 * _ + } }