Skip to content

Commit 4a041b0

Browse files
authored
feat!: use sdk-maintained state, require 1.12 (#964)
Signed-off-by: Todd Baert <[email protected]>
1 parent 096d407 commit 4a041b0

File tree

25 files changed

+602
-957
lines changed

25 files changed

+602
-957
lines changed

hooks/open-telemetry/pom.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@
3838
</dependencyManagement>
3939

4040
<dependencies>
41-
<dependency>
42-
<groupId>dev.openfeature</groupId>
43-
<artifactId>sdk</artifactId>
44-
<version>[1.4,2.0)</version>
45-
<scope>provided</scope>
46-
</dependency>
47-
4841
<dependency>
4942
<groupId>io.opentelemetry</groupId>
5043
<artifactId>opentelemetry-semconv</artifactId>

pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@
6262
<dependencies>
6363
<dependency>
6464
<!-- provided -->
65+
<!-- this can be overriden in child POMs to support specific SDK requirements -->
6566
<groupId>dev.openfeature</groupId>
6667
<artifactId>sdk</artifactId>
67-
<!-- 1.0 <= v < 2.0 -->
68-
<version>[1.0,2.0)</version>
68+
<!-- 1.12 <= v < 2.0 -->
69+
<version>[1.12,2.0)</version>
6970
<!-- use the version provided at runtime -->
7071
<scope>provided</scope>
7172
</dependency>

providers/configcat/src/main/java/dev/openfeature/contrib/providers/configcat/ConfigCatProvider.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
package dev.openfeature.contrib.providers.configcat;
22

3+
import java.util.ArrayList;
4+
import java.util.concurrent.atomic.AtomicBoolean;
5+
36
import com.configcat.ConfigCatClient;
47
import com.configcat.EvaluationDetails;
58
import com.configcat.User;
9+
610
import dev.openfeature.sdk.EvaluationContext;
711
import dev.openfeature.sdk.EventProvider;
812
import dev.openfeature.sdk.Metadata;
913
import dev.openfeature.sdk.ProviderEvaluation;
1014
import dev.openfeature.sdk.ProviderEventDetails;
11-
import dev.openfeature.sdk.ProviderState;
1215
import dev.openfeature.sdk.Value;
1316
import dev.openfeature.sdk.exceptions.GeneralError;
14-
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
1517
import lombok.Getter;
1618
import lombok.SneakyThrows;
1719
import lombok.extern.slf4j.Slf4j;
1820

19-
import java.util.ArrayList;
20-
import java.util.concurrent.atomic.AtomicBoolean;
21-
2221
/**
2322
* Provider implementation for ConfigCat.
2423
*/
@@ -36,9 +35,6 @@ public class ConfigCatProvider extends EventProvider {
3635
@Getter
3736
private ConfigCatClient configCatClient;
3837

39-
@Getter
40-
private ProviderState state = ProviderState.NOT_READY;
41-
4238
private AtomicBoolean isInitialized = new AtomicBoolean(false);
4339

4440
/**
@@ -64,8 +60,7 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
6460
configCatClient = ConfigCatClient.get(configCatProviderConfig.getSdkKey(),
6561
configCatProviderConfig.getOptions());
6662
configCatProviderConfig.postInit();
67-
state = ProviderState.READY;
68-
log.info("finished initializing provider, state: {}", state);
63+
log.info("finished initializing provider");
6964

7065
configCatClient.getHooks().addOnClientReady(() -> {
7166
ProviderEventDetails providerEventDetails = ProviderEventDetails.builder()
@@ -123,12 +118,6 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa
123118

124119
private <T> ProviderEvaluation<T> getEvaluation(Class<T> classOfT, String key, T defaultValue,
125120
EvaluationContext ctx) {
126-
if (!ProviderState.READY.equals(state)) {
127-
if (ProviderState.NOT_READY.equals(state)) {
128-
throw new ProviderNotReadyError(PROVIDER_NOT_YET_INITIALIZED);
129-
}
130-
throw new GeneralError(UNKNOWN_ERROR);
131-
}
132121
User user = ctx == null ? null : ContextTransformer.transform(ctx);
133122
EvaluationDetails<T> evaluationDetails;
134123
T evaluatedValue;
@@ -157,6 +146,5 @@ public void shutdown() {
157146
if (configCatClient != null) {
158147
configCatClient.close();
159148
}
160-
state = ProviderState.NOT_READY;
161149
}
162150
}

providers/configcat/src/test/java/dev/openfeature/contrib/providers/configcat/ConfigCatProviderTest.java

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
11
package dev.openfeature.contrib.providers.configcat;
22

3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
6+
import java.util.HashMap;
7+
8+
import org.junit.jupiter.api.AfterAll;
9+
import org.junit.jupiter.api.BeforeAll;
10+
import org.junit.jupiter.api.Test;
11+
312
import com.configcat.OverrideBehaviour;
413
import com.configcat.OverrideDataSourceBuilder;
514
import com.configcat.User;
15+
616
import dev.openfeature.sdk.Client;
717
import dev.openfeature.sdk.ImmutableContext;
818
import dev.openfeature.sdk.MutableContext;
919
import dev.openfeature.sdk.OpenFeatureAPI;
1020
import dev.openfeature.sdk.ProviderEventDetails;
1121
import dev.openfeature.sdk.Value;
12-
import dev.openfeature.sdk.exceptions.GeneralError;
13-
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
1422
import lombok.SneakyThrows;
1523
import lombok.extern.slf4j.Slf4j;
16-
import org.junit.jupiter.api.AfterAll;
17-
import org.junit.jupiter.api.BeforeAll;
18-
import org.junit.jupiter.api.Test;
19-
20-
import java.util.HashMap;
21-
22-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
23-
import static org.junit.jupiter.api.Assertions.assertEquals;
24-
import static org.junit.jupiter.api.Assertions.assertThrows;
2524

2625
/**
2726
* ConfigCatProvider test, based on local config file evaluation.
@@ -181,27 +180,6 @@ void getStringEvaluationByUser() {
181180
assertEquals("fallback", client.getStringValue(USERS_FLAG_NAME + "Str", "111", evaluationContext));
182181
}
183182

184-
@SneakyThrows
185-
@Test
186-
void shouldThrowIfNotInitialized() {
187-
ConfigCatProviderConfig configCatProviderConfig = ConfigCatProviderConfig.builder().sdkKey("configcat-sdk-1/TEST_KEY-0123456789012/1234567890123456789012").build();
188-
ConfigCatProvider tempConfigCatProvider = new ConfigCatProvider(configCatProviderConfig);
189-
190-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext()));
191-
192-
OpenFeatureAPI.getInstance().setProviderAndWait("tempConfigCatProvider", tempConfigCatProvider);
193-
194-
assertThrows(GeneralError.class, ()-> tempConfigCatProvider.initialize(null));
195-
196-
tempConfigCatProvider.shutdown();
197-
198-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getBooleanEvaluation("fail_not_initialized", false, new ImmutableContext()));
199-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getDoubleEvaluation("fail_not_initialized", 0.1, new ImmutableContext()));
200-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getIntegerEvaluation("fail_not_initialized", 3, new ImmutableContext()));
201-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getObjectEvaluation("fail_not_initialized", null, new ImmutableContext()));
202-
assertThrows(ProviderNotReadyError.class, ()-> tempConfigCatProvider.getStringEvaluation("fail_not_initialized", "", new ImmutableContext()));
203-
}
204-
205183
@Test
206184
void eventsTest() {
207185
configCatProvider.emitProviderReady(ProviderEventDetails.builder().build());

providers/flagd/pom.xml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,7 @@
3232
</developers>
3333

3434
<dependencies>
35-
3635
<!-- we inherent dev.openfeature.javasdk and the test dependencies from the parent pom -->
37-
<!-- override parent definition -->
38-
<dependency>
39-
<groupId>dev.openfeature</groupId>
40-
<artifactId>sdk</artifactId>
41-
<version>[1.4,2.0)</version>
42-
<scope>provided</scope>
43-
</dependency>
44-
4536
<dependency>
4637
<groupId>io.grpc</groupId>
4738
<artifactId>grpc-netty</artifactId>

providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java

Lines changed: 23 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package dev.openfeature.contrib.providers.flagd;
22

3+
import java.util.List;
4+
35
import dev.openfeature.contrib.providers.flagd.resolver.Resolver;
46
import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcResolver;
57
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache;
@@ -9,27 +11,19 @@
911
import dev.openfeature.sdk.Metadata;
1012
import dev.openfeature.sdk.ProviderEvaluation;
1113
import dev.openfeature.sdk.ProviderEventDetails;
12-
import dev.openfeature.sdk.ProviderState;
1314
import dev.openfeature.sdk.Value;
1415
import lombok.extern.slf4j.Slf4j;
1516

16-
import java.util.List;
17-
import java.util.concurrent.locks.Lock;
18-
import java.util.concurrent.locks.ReadWriteLock;
19-
import java.util.concurrent.locks.ReentrantReadWriteLock;
20-
2117
/**
2218
* OpenFeature provider for flagd.
2319
*/
2420
@Slf4j
25-
@SuppressWarnings({"PMD.TooManyStaticImports", "checkstyle:NoFinalizer"})
21+
@SuppressWarnings({ "PMD.TooManyStaticImports", "checkstyle:NoFinalizer" })
2622
public class FlagdProvider extends EventProvider {
2723
private static final String FLAGD_PROVIDER = "flagD Provider";
28-
29-
private final ReadWriteLock lock = new ReentrantReadWriteLock();
3024
private final Resolver flagResolver;
31-
private ProviderState state = ProviderState.NOT_READY;
32-
private boolean initialized = false;
25+
private volatile boolean initialized = false;
26+
private volatile boolean connected = false;
3327

3428
private EvaluationContext evaluationContext;
3529

@@ -52,14 +46,14 @@ public FlagdProvider() {
5246
public FlagdProvider(final FlagdOptions options) {
5347
switch (options.getResolverType().asString()) {
5448
case Config.RESOLVER_IN_PROCESS:
55-
this.flagResolver = new InProcessResolver(options, this::setState);
49+
this.flagResolver = new InProcessResolver(options, this::isConnected,
50+
this::onResolverConnectionChanged);
5651
break;
5752
case Config.RESOLVER_RPC:
58-
this.flagResolver =
59-
new GrpcResolver(options,
60-
new Cache(options.getCacheType(), options.getMaxCacheSize()),
61-
this::getState,
62-
this::setState);
53+
this.flagResolver = new GrpcResolver(options,
54+
new Cache(options.getCacheType(), options.getMaxCacheSize()),
55+
this::isConnected,
56+
this::onResolverConnectionChanged);
6357
break;
6458
default:
6559
throw new IllegalStateException(
@@ -86,24 +80,13 @@ public synchronized void shutdown() {
8680

8781
try {
8882
this.flagResolver.shutdown();
89-
this.initialized = false;
9083
} catch (Exception e) {
9184
log.error("Error during shutdown {}", FLAGD_PROVIDER, e);
92-
}
93-
}
94-
95-
@Override
96-
public ProviderState getState() {
97-
Lock l = this.lock.readLock();
98-
try {
99-
l.lock();
100-
return this.state;
10185
} finally {
102-
l.unlock();
86+
this.initialized = false;
10387
}
10488
}
10589

106-
10790
@Override
10891
public Metadata getMetadata() {
10992
return () -> FLAGD_PROVIDER;
@@ -142,49 +125,32 @@ private EvaluationContext mergeContext(final EvaluationContext clientCallCtx) {
142125
return clientCallCtx;
143126
}
144127

145-
private void setState(ProviderState newState, List<String> changedFlagsKeys) {
146-
ProviderState oldState;
147-
Lock l = this.lock.writeLock();
148-
try {
149-
l.lock();
150-
oldState = this.state;
151-
this.state = newState;
152-
} finally {
153-
l.unlock();
154-
}
155-
this.handleStateTransition(oldState, newState, changedFlagsKeys);
128+
private boolean isConnected() {
129+
return this.connected;
156130
}
157131

158-
private void handleStateTransition(ProviderState oldState, ProviderState newState, List<String> changedFlagKeys) {
159-
// we got initialized
160-
if (ProviderState.NOT_READY.equals(oldState) && ProviderState.READY.equals(newState)) {
161-
// nothing to do, the SDK emits the events
162-
log.debug("Init completed");
163-
return;
164-
}
165-
// we got shutdown, not checking oldState as behavior remains the same for shutdown
166-
if (ProviderState.NOT_READY.equals(newState)) {
167-
// nothing to do
168-
log.debug("shutdown completed");
169-
return;
170-
}
132+
private void onResolverConnectionChanged(boolean newConnectedState, List<String> changedFlagKeys) {
133+
boolean previous = connected;
134+
boolean current = newConnectedState;
135+
this.connected = newConnectedState;
136+
171137
// configuration changed
172-
if (ProviderState.READY.equals(oldState) && ProviderState.READY.equals(newState)) {
138+
if (initialized && previous && current) {
173139
log.debug("Configuration changed");
174140
ProviderEventDetails details = ProviderEventDetails.builder().flagsChanged(changedFlagKeys)
175141
.message("configuration changed").build();
176142
this.emitProviderConfigurationChanged(details);
177143
return;
178144
}
179145
// there was an error
180-
if (ProviderState.READY.equals(oldState) && ProviderState.ERROR.equals(newState)) {
146+
if (initialized && previous && !current) {
181147
log.debug("There has been an error");
182148
ProviderEventDetails details = ProviderEventDetails.builder().message("there has been an error").build();
183149
this.emitProviderError(details);
184150
return;
185151
}
186-
// we recover from an error
187-
if (ProviderState.ERROR.equals(oldState) && ProviderState.READY.equals(newState)) {
152+
// we recovered from an error
153+
if (initialized && !previous && current) {
188154
log.debug("Recovered from error");
189155
ProviderEventDetails details = ProviderEventDetails.builder().message("recovered from error").build();
190156
this.emitProviderReady(details);
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dev.openfeature.contrib.providers.flagd.resolver.common;
22

3-
import java.util.concurrent.atomic.AtomicBoolean;
3+
import java.util.function.Supplier;
44

55
import dev.openfeature.sdk.exceptions.GeneralError;
66

@@ -14,20 +14,23 @@ private Util() {
1414

1515
/**
1616
* A helper to block the caller for given conditions.
17-
*
18-
* @param deadline number of milliseconds to block
19-
* @param check {@link AtomicBoolean} to check for status true
17+
*
18+
* @param deadline number of milliseconds to block
19+
* @param connectedSupplier func to check for status true
20+
* @throws InterruptedException if interrupted
2021
*/
21-
public static void busyWaitAndCheck(final Long deadline, final AtomicBoolean check) throws InterruptedException {
22+
public static void busyWaitAndCheck(final Long deadline, final Supplier<Boolean> connectedSupplier)
23+
throws InterruptedException {
2224
long start = System.currentTimeMillis();
2325

2426
do {
2527
if (deadline <= System.currentTimeMillis() - start) {
2628
throw new GeneralError(
27-
String.format("Deadline exceeded. Condition did not complete within the %d deadline", deadline));
29+
String.format("Deadline exceeded. Condition did not complete within the %d deadline",
30+
deadline));
2831
}
2932

3033
Thread.sleep(50L);
31-
} while (!check.get());
34+
} while (!connectedSupplier.get());
3235
}
3336
}

0 commit comments

Comments
 (0)