diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml
index bcdd87a0..6ef83234 100644
--- a/.github/workflows/merge.yml
+++ b/.github/workflows/merge.yml
@@ -20,9 +20,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb
+ - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
- name: Set up JDK 8
- uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b
+ uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12
with:
java-version: '8'
distribution: 'temurin'
@@ -32,7 +32,7 @@ jobs:
server-password: ${{ secrets.OSSRH_PASSWORD }}
- name: Cache local Maven repository
- uses: actions/cache@36f1e144e1c8edb0a652766b484448563d8baf46
+ uses: actions/cache@9fa7e61ec7e1f44ac75218e7aaea81da8856fd11
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -49,7 +49,7 @@ jobs:
run: mvn --batch-mode --update-snapshots verify
- name: Upload coverage to Codecov
- uses: codecov/codecov-action@v5.1.2
+ uses: codecov/codecov-action@v5.3.1
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
flags: unittests # optional
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index f3a625b1..6e3c40f4 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -10,22 +10,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out the code
- uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb
+ uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
- name: Set up JDK 8
- uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b
+ uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12
with:
java-version: '8'
distribution: 'temurin'
cache: maven
- name: Initialize CodeQL
- uses: github/codeql-action/init@e83e0a4f58f2ca25f7dd222e8689519a74bf26fc
+ uses: github/codeql-action/init@1c15a48f3fb49ce535e9ee4e57e127315f669361
with:
languages: java
- name: Cache local Maven repository
- uses: actions/cache@36f1e144e1c8edb0a652766b484448563d8baf46
+ uses: actions/cache@9fa7e61ec7e1f44ac75218e7aaea81da8856fd11
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
@@ -36,7 +36,7 @@ jobs:
run: mvn --batch-mode --update-snapshots --activate-profiles e2e verify
- name: Upload coverage to Codecov
- uses: codecov/codecov-action@v5.1.2
+ uses: codecov/codecov-action@v5.3.1
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
flags: unittests # optional
@@ -45,4 +45,4 @@ jobs:
verbose: true # optional (default = false)
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@e83e0a4f58f2ca25f7dd222e8689519a74bf26fc
+ uses: github/codeql-action/analyze@1c15a48f3fb49ce535e9ee4e57e127315f669361
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2b1eb9e7..7342889d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,10 +28,10 @@ jobs:
# These steps are only run if this was a merged release-please PR
- name: checkout
if: ${{ steps.release.outputs.release_created }}
- uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb
+ uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
- name: Set up JDK 8
if: ${{ steps.release.outputs.release_created }}
- uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b
+ uses: actions/setup-java@3a4f6e1af504cf6a31855fa899c6aa5355ba6c12
with:
java-version: '8'
distribution: 'temurin'
diff --git a/.github/workflows/static-code-scanning.yaml b/.github/workflows/static-code-scanning.yaml
index 40b0511a..85313855 100644
--- a/.github/workflows/static-code-scanning.yaml
+++ b/.github/workflows/static-code-scanning.yaml
@@ -29,16 +29,16 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@cbb722410c2e876e24abbe8de2cc27693e501dcb
+ uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@e83e0a4f58f2ca25f7dd222e8689519a74bf26fc
+ uses: github/codeql-action/init@1c15a48f3fb49ce535e9ee4e57e127315f669361
with:
languages: java
- name: Autobuild
- uses: github/codeql-action/autobuild@e83e0a4f58f2ca25f7dd222e8689519a74bf26fc
+ uses: github/codeql-action/autobuild@1c15a48f3fb49ce535e9ee4e57e127315f669361
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@e83e0a4f58f2ca25f7dd222e8689519a74bf26fc
+ uses: github/codeql-action/analyze@1c15a48f3fb49ce535e9ee4e57e127315f669361
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index e2d18dc1..f459d7af 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1 +1 @@
-{".":"1.14.0"}
\ No newline at end of file
+{".":"1.14.1"}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f7000d4..6301fce0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,60 @@
# Changelog
+## [1.14.1](https://github.com/open-feature/java-sdk/compare/v1.14.0...v1.14.1) (2025-02-14)
+
+
+### ๐ Bug Fixes
+
+* **deps:** update dependency io.cucumber:cucumber-bom to v7.21.0 ([#1312](https://github.com/open-feature/java-sdk/issues/1312)) ([208411e](https://github.com/open-feature/java-sdk/commit/208411e72338e37bf477ac0b784bbbbe0309b922))
+* **deps:** update dependency io.cucumber:cucumber-bom to v7.21.1 ([#1317](https://github.com/open-feature/java-sdk/issues/1317)) ([b797883](https://github.com/open-feature/java-sdk/commit/b7978832b786fe081169ff0efeb702218300c622))
+* possible event-related deadlocks with some providers ([#1314](https://github.com/open-feature/java-sdk/issues/1314)) ([c33ac2d](https://github.com/open-feature/java-sdk/commit/c33ac2d9b2e91b85fffb3c21653912fe82006351))
+* TrackingEventDetails interface to include numeric getValue() call ([#1328](https://github.com/open-feature/java-sdk/issues/1328)) ([08c38fb](https://github.com/open-feature/java-sdk/commit/08c38fb553d82a42682c3eb9239329f770063898))
+
+
+### ๐งน Chore
+
+* **deps:** update actions/cache digest to 9fa7e61 ([#1324](https://github.com/open-feature/java-sdk/issues/1324)) ([69cdc77](https://github.com/open-feature/java-sdk/commit/69cdc772a639470dd223bf70ef6e9f8bc4d93dea))
+* **deps:** update actions/checkout digest to 85e6279 ([#1287](https://github.com/open-feature/java-sdk/issues/1287)) ([640e35e](https://github.com/open-feature/java-sdk/commit/640e35e85375e3098f61b7397432d80a95502bdd))
+* **deps:** update actions/setup-java digest to 28b532b ([#1296](https://github.com/open-feature/java-sdk/issues/1296)) ([874e86d](https://github.com/open-feature/java-sdk/commit/874e86df5c22a1e5771ca16c76aa13039b5f9b65))
+* **deps:** update actions/setup-java digest to 3a4f6e1 ([#1306](https://github.com/open-feature/java-sdk/issues/1306)) ([ba9cc4b](https://github.com/open-feature/java-sdk/commit/ba9cc4b85a1082d638d49b9d2d0a4ed5a45f09ee))
+* **deps:** update actions/setup-java digest to 51ab6d2 ([#1288](https://github.com/open-feature/java-sdk/issues/1288)) ([c69d3a4](https://github.com/open-feature/java-sdk/commit/c69d3a4bd137c1d6baa47c14228bfe8f96555676))
+* **deps:** update actions/setup-java digest to 99d3141 ([#1285](https://github.com/open-feature/java-sdk/issues/1285)) ([32a3933](https://github.com/open-feature/java-sdk/commit/32a39335de8e61650905fc96dc1a73e65f1fe9f8))
+* **deps:** update codecov/codecov-action action to v5.2.0 ([#1298](https://github.com/open-feature/java-sdk/issues/1298)) ([531fc38](https://github.com/open-feature/java-sdk/commit/531fc385b662c5b7b334fee298fc9fe1283c78fb))
+* **deps:** update codecov/codecov-action action to v5.3.0 ([#1301](https://github.com/open-feature/java-sdk/issues/1301)) ([f7f6586](https://github.com/open-feature/java-sdk/commit/f7f6586d72e3f112a7dafc8f77de273ed49ccc4b))
+* **deps:** update codecov/codecov-action action to v5.3.1 ([#1303](https://github.com/open-feature/java-sdk/issues/1303)) ([f9fa54b](https://github.com/open-feature/java-sdk/commit/f9fa54be493e1d0843b709008eb0f047e7580d47))
+* **deps:** update dependency net.bytebuddy:byte-buddy to v1.16.0 ([#1289](https://github.com/open-feature/java-sdk/issues/1289)) ([0b5b423](https://github.com/open-feature/java-sdk/commit/0b5b423bdd378bb1db3e10fe5da7fa2c937a4610))
+* **deps:** update dependency net.bytebuddy:byte-buddy to v1.16.1 ([#1292](https://github.com/open-feature/java-sdk/issues/1292)) ([0af9f29](https://github.com/open-feature/java-sdk/commit/0af9f2901f88b5ef9bed0c570d426939a55af3cf))
+* **deps:** update dependency net.bytebuddy:byte-buddy to v1.17.0 ([#1309](https://github.com/open-feature/java-sdk/issues/1309)) ([cda3405](https://github.com/open-feature/java-sdk/commit/cda34053f7e39318205a181ef93c825bab2ed9fc))
+* **deps:** update dependency net.bytebuddy:byte-buddy to v1.17.1 ([#1329](https://github.com/open-feature/java-sdk/issues/1329)) ([9ab2618](https://github.com/open-feature/java-sdk/commit/9ab26182eae4974b60d166777c51dfcb07957150))
+* **deps:** update dependency net.bytebuddy:byte-buddy-agent to v1.16.0 ([#1290](https://github.com/open-feature/java-sdk/issues/1290)) ([6c4205a](https://github.com/open-feature/java-sdk/commit/6c4205a00817af260ef9b90f54ce878cad33f75a))
+* **deps:** update dependency net.bytebuddy:byte-buddy-agent to v1.16.1 ([#1293](https://github.com/open-feature/java-sdk/issues/1293)) ([6071932](https://github.com/open-feature/java-sdk/commit/6071932cb4207dc83cdedfa67c8a69ed71d9c26a))
+* **deps:** update dependency net.bytebuddy:byte-buddy-agent to v1.17.0 ([#1310](https://github.com/open-feature/java-sdk/issues/1310)) ([40fa173](https://github.com/open-feature/java-sdk/commit/40fa1733382f4c476a1228c6499044ad83c8f3c4))
+* **deps:** update dependency net.bytebuddy:byte-buddy-agent to v1.17.1 ([#1330](https://github.com/open-feature/java-sdk/issues/1330)) ([4ba5695](https://github.com/open-feature/java-sdk/commit/4ba5695eeea6a7ab2fe1d2c595fa482d4b7868dc))
+* **deps:** update dependency org.assertj:assertj-core to v3.27.3 ([#1291](https://github.com/open-feature/java-sdk/issues/1291)) ([a5eb21d](https://github.com/open-feature/java-sdk/commit/a5eb21d1a2e6945a4455cacde898bc913bddb96d))
+* **deps:** update github/codeql-action digest to 0701025 ([#1311](https://github.com/open-feature/java-sdk/issues/1311)) ([9a1e9ab](https://github.com/open-feature/java-sdk/commit/9a1e9abd64220c8d8706f2a64e041ef3f37e1a43))
+* **deps:** update github/codeql-action digest to 08bc0cf ([#1313](https://github.com/open-feature/java-sdk/issues/1313)) ([37ed6a4](https://github.com/open-feature/java-sdk/commit/37ed6a424cdc013ed74c9881826cc56c93ae8228))
+* **deps:** update github/codeql-action digest to 0a35e8f ([#1316](https://github.com/open-feature/java-sdk/issues/1316)) ([26e1d7f](https://github.com/open-feature/java-sdk/commit/26e1d7fff342a32880542efa87b017aec506667e))
+* **deps:** update github/codeql-action digest to 0f1559a ([#1286](https://github.com/open-feature/java-sdk/issues/1286)) ([882d2dd](https://github.com/open-feature/java-sdk/commit/882d2dd5bdac007e8a3783efc54fa45faed22054))
+* **deps:** update github/codeql-action digest to 10a3f07 ([#1280](https://github.com/open-feature/java-sdk/issues/1280)) ([a3854d6](https://github.com/open-feature/java-sdk/commit/a3854d6ab1dba99f4db18f868e89fcc04418e306))
+* **deps:** update github/codeql-action digest to 1c15a48 ([#1325](https://github.com/open-feature/java-sdk/issues/1325)) ([3baf0df](https://github.com/open-feature/java-sdk/commit/3baf0df966f8212864aa7e57bc3d3d09d324fe11))
+* **deps:** update github/codeql-action digest to 1efc6bb ([#1281](https://github.com/open-feature/java-sdk/issues/1281)) ([8a1ab7e](https://github.com/open-feature/java-sdk/commit/8a1ab7ea18aff4ee5a6a2fdd1f805b08e51a50a3))
+* **deps:** update github/codeql-action digest to 24e1c2d ([#1315](https://github.com/open-feature/java-sdk/issues/1315)) ([46903c6](https://github.com/open-feature/java-sdk/commit/46903c6f275e5f9dc8884acf3f76f76efcfc58bd))
+* **deps:** update github/codeql-action digest to 3b4f4d9 ([#1282](https://github.com/open-feature/java-sdk/issues/1282)) ([b390d5f](https://github.com/open-feature/java-sdk/commit/b390d5f0b0945948cd6b87e6486725d095d5ac8a))
+* **deps:** update github/codeql-action digest to 43cffee ([#1304](https://github.com/open-feature/java-sdk/issues/1304)) ([6874de6](https://github.com/open-feature/java-sdk/commit/6874de64ce589e853f5523019bfa9e1d60840baf))
+* **deps:** update github/codeql-action digest to 54b1c84 ([#1307](https://github.com/open-feature/java-sdk/issues/1307)) ([6f36434](https://github.com/open-feature/java-sdk/commit/6f36434c520dcef27deb04e04941693dc15acb2f))
+* **deps:** update github/codeql-action digest to 5f4f998 ([#1305](https://github.com/open-feature/java-sdk/issues/1305)) ([7916d76](https://github.com/open-feature/java-sdk/commit/7916d76635c5ab59dafe6d72058aad9cfcf05f4b))
+* **deps:** update github/codeql-action digest to 6063925 ([#1320](https://github.com/open-feature/java-sdk/issues/1320)) ([538140d](https://github.com/open-feature/java-sdk/commit/538140dfe713a421623b179e69b399f82200fe61))
+* **deps:** update github/codeql-action digest to 7e3036b ([#1300](https://github.com/open-feature/java-sdk/issues/1300)) ([3491956](https://github.com/open-feature/java-sdk/commit/34919561b73faa0cca489ad480e93cca9a854167))
+* **deps:** update github/codeql-action digest to 87fc816 ([#1277](https://github.com/open-feature/java-sdk/issues/1277)) ([c2a82db](https://github.com/open-feature/java-sdk/commit/c2a82dbdbafa134fae4b0c9aef88cf589e09aefa))
+* **deps:** update github/codeql-action digest to 93da9f2 ([#1283](https://github.com/open-feature/java-sdk/issues/1283)) ([45b3995](https://github.com/open-feature/java-sdk/commit/45b3995bdad9f1b05abb01455a9c8f57028cfde5))
+* **deps:** update github/codeql-action digest to affec20 ([#1323](https://github.com/open-feature/java-sdk/issues/1323)) ([8f3ced5](https://github.com/open-feature/java-sdk/commit/8f3ced590764760244cc81ac10c939ca62504dfe))
+* **deps:** update github/codeql-action digest to b44b19f ([#1297](https://github.com/open-feature/java-sdk/issues/1297)) ([305e032](https://github.com/open-feature/java-sdk/commit/305e0329e78116fe697240e420879ac85012d698))
+* **deps:** update github/codeql-action digest to d90e07f ([#1294](https://github.com/open-feature/java-sdk/issues/1294)) ([5671184](https://github.com/open-feature/java-sdk/commit/5671184e7f76f979d631c18bb2ebfb15dccfb207))
+* **deps:** update github/codeql-action digest to db7177a ([#1279](https://github.com/open-feature/java-sdk/issues/1279)) ([b997946](https://github.com/open-feature/java-sdk/commit/b997946db1c7663b7ebb775ad45cdb2b0aaeb291))
+* **deps:** update github/codeql-action digest to e7c0c9d ([#1302](https://github.com/open-feature/java-sdk/issues/1302)) ([78adc77](https://github.com/open-feature/java-sdk/commit/78adc77c23da6116e1f58b3a45dc283c3c58837b))
+* **deps:** update github/codeql-action digest to e9987ad ([#1308](https://github.com/open-feature/java-sdk/issues/1308)) ([99d8185](https://github.com/open-feature/java-sdk/commit/99d818572a3407ca6b25f6e91f69ef3e83bdc657))
+* **deps:** update github/codeql-action digest to f89b8a7 ([#1295](https://github.com/open-feature/java-sdk/issues/1295)) ([122e82f](https://github.com/open-feature/java-sdk/commit/122e82f8431fb116ae3b147f7e2245d7f90b1c77))
+
## [1.14.0](https://github.com/open-feature/java-sdk/compare/v1.13.0...v1.14.0) (2025-01-10)
diff --git a/README.md b/README.md
index cbb9d9f1..49d5562e 100644
--- a/README.md
+++ b/README.md
@@ -18,8 +18,8 @@
-
-
+
+
@@ -59,7 +59,7 @@ Note that this library is intended to be used in server-side contexts and has no
dev.openfeature
sdk
- 1.14.0
+ 1.14.1
```
@@ -84,7 +84,7 @@ If you would like snapshot builds, this is the relevant repository information:
```groovy
dependencies {
- implementation 'dev.openfeature:sdk:1.14.0'
+ implementation 'dev.openfeature:sdk:1.14.1'
}
```
diff --git a/pom.xml b/pom.xml
index 5d8ea34c..a6a54e5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
dev.openfeature
sdk
- 1.14.0
+ 1.14.1
UTF-8
@@ -77,7 +77,7 @@
org.assertj
assertj-core
- 3.27.2
+ 3.27.3
test
@@ -167,14 +167,14 @@
net.bytebuddy
byte-buddy
- 1.15.11
+ 1.17.1
test
net.bytebuddy
byte-buddy-agent
- 1.15.11
+ 1.17.1
test
@@ -182,7 +182,7 @@
io.cucumber
cucumber-bom
- 7.20.1
+ 7.21.1
pom
import
@@ -265,6 +265,8 @@
maven-surefire-plugin
3.5.2
+ 1
+ false
${surefireArgLine}
diff --git a/src/main/java/dev/openfeature/sdk/EventProvider.java b/src/main/java/dev/openfeature/sdk/EventProvider.java
index e9cdae55..659c6ad4 100644
--- a/src/main/java/dev/openfeature/sdk/EventProvider.java
+++ b/src/main/java/dev/openfeature/sdk/EventProvider.java
@@ -1,6 +1,10 @@
package dev.openfeature.sdk;
import dev.openfeature.sdk.internal.TriConsumer;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
/**
* Abstract EventProvider. Providers must extend this class to support events.
@@ -14,8 +18,10 @@
*
* @see FeatureProvider
*/
+@Slf4j
public abstract class EventProvider implements FeatureProvider {
private EventProviderListener eventProviderListener;
+ private final ExecutorService emitterExecutor = Executors.newCachedThreadPool();
void setEventProviderListener(EventProviderListener eventProviderListener) {
this.eventProviderListener = eventProviderListener;
@@ -46,6 +52,24 @@ void detach() {
this.onEmit = null;
}
+ /**
+ * Stop the event emitter executor and block until either termination has completed
+ * or timeout period has elapsed.
+ */
+ @Override
+ public void shutdown() {
+ emitterExecutor.shutdown();
+ try {
+ if (!emitterExecutor.awaitTermination(EventSupport.SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ log.warn("Emitter executor did not terminate before the timeout period had elapsed");
+ emitterExecutor.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ emitterExecutor.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ }
+
/**
* Emit the specified {@link ProviderEvent}.
*
@@ -56,8 +80,10 @@ public void emit(ProviderEvent event, ProviderEventDetails details) {
if (eventProviderListener != null) {
eventProviderListener.onEmit(event, details);
}
- if (this.onEmit != null) {
- this.onEmit.accept(this, event, details);
+
+ final TriConsumer localOnEmit = this.onEmit;
+ if (localOnEmit != null) {
+ emitterExecutor.submit(() -> localOnEmit.accept(this, event, details));
}
}
diff --git a/src/main/java/dev/openfeature/sdk/EventSupport.java b/src/main/java/dev/openfeature/sdk/EventSupport.java
index d3af4599..5ebe90a4 100644
--- a/src/main/java/dev/openfeature/sdk/EventSupport.java
+++ b/src/main/java/dev/openfeature/sdk/EventSupport.java
@@ -19,15 +19,15 @@
@Slf4j
class EventSupport {
+ public static final int SHUTDOWN_TIMEOUT_SECONDS = 3;
+
// we use a v4 uuid as a "placeholder" for anonymous clients, since
// ConcurrentHashMap doesn't support nulls
private static final String defaultClientUuid = UUID.randomUUID().toString();
- private static final int SHUTDOWN_TIMEOUT_SECONDS = 3;
private final Map handlerStores = new ConcurrentHashMap<>();
private final HandlerStore globalHandlerStore = new HandlerStore();
private final ExecutorService taskExecutor = Executors.newCachedThreadPool(runnable -> {
final Thread thread = new Thread(runnable);
- thread.setDaemon(true);
return thread;
});
diff --git a/src/main/java/dev/openfeature/sdk/TrackingEventDetails.java b/src/main/java/dev/openfeature/sdk/TrackingEventDetails.java
index 15b0208c..484672d8 100644
--- a/src/main/java/dev/openfeature/sdk/TrackingEventDetails.java
+++ b/src/main/java/dev/openfeature/sdk/TrackingEventDetails.java
@@ -1,6 +1,14 @@
package dev.openfeature.sdk;
+import java.util.Optional;
+
/**
* Data pertinent to a particular tracking event.
*/
-public interface TrackingEventDetails extends Structure {}
+public interface TrackingEventDetails extends Structure {
+
+ /**
+ * Returns the optional numeric tracking value.
+ */
+ Optional getValue();
+}
diff --git a/src/test/java/dev/openfeature/sdk/EventProviderTest.java b/src/test/java/dev/openfeature/sdk/EventProviderTest.java
index d8af6e8d..a159877f 100644
--- a/src/test/java/dev/openfeature/sdk/EventProviderTest.java
+++ b/src/test/java/dev/openfeature/sdk/EventProviderTest.java
@@ -5,13 +5,18 @@
import static org.mockito.Mockito.*;
import dev.openfeature.sdk.internal.TriConsumer;
+import dev.openfeature.sdk.testutils.TestStackedEmitCallsProvider;
+import io.cucumber.java.AfterAll;
import lombok.SneakyThrows;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
class EventProviderTest {
+ private static final int TIMEOUT = 300;
+
private TestEventProvider eventProvider;
@BeforeEach
@@ -21,6 +26,11 @@ void setup() {
eventProvider.initialize(null);
}
+ @AfterAll
+ public static void resetDefaultProvider() {
+ OpenFeatureAPI.getInstance().setProviderAndWait(new NoOpProvider());
+ }
+
@Test
@DisplayName("should run attached onEmit with emitters")
void emitsEventsWhenAttached() {
@@ -34,10 +44,10 @@ void emitsEventsWhenAttached() {
eventProvider.emitProviderStale(details);
eventProvider.emitProviderError(details);
- verify(onEmit, times(2)).accept(eventProvider, ProviderEvent.PROVIDER_READY, details);
- verify(onEmit, times(1)).accept(eventProvider, ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
- verify(onEmit, times(1)).accept(eventProvider, ProviderEvent.PROVIDER_STALE, details);
- verify(onEmit, times(1)).accept(eventProvider, ProviderEvent.PROVIDER_ERROR, details);
+ verify(onEmit, timeout(TIMEOUT).times(2)).accept(eventProvider, ProviderEvent.PROVIDER_READY, details);
+ verify(onEmit, timeout(TIMEOUT)).accept(eventProvider, ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
+ verify(onEmit, timeout(TIMEOUT)).accept(eventProvider, ProviderEvent.PROVIDER_STALE, details);
+ verify(onEmit, timeout(TIMEOUT)).accept(eventProvider, ProviderEvent.PROVIDER_ERROR, details);
}
@Test
@@ -75,6 +85,15 @@ void doesNotThrowWhenOnEmitSame() {
eventProvider.attach(onEmit2); // should not throw, same instance. noop
}
+ @Test
+ @SneakyThrows
+ @Timeout(value = 2, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
+ @DisplayName("should not deadlock on emit called during emit")
+ void doesNotDeadlockOnEmitStackedCalls() {
+ TestStackedEmitCallsProvider provider = new TestStackedEmitCallsProvider();
+ OpenFeatureAPI.getInstance().setProviderAndWait(provider);
+ }
+
static class TestEventProvider extends EventProvider {
private static final String NAME = "TestEventProvider";
diff --git a/src/test/java/dev/openfeature/sdk/EventsTest.java b/src/test/java/dev/openfeature/sdk/EventsTest.java
index 02a5953b..e5902465 100644
--- a/src/test/java/dev/openfeature/sdk/EventsTest.java
+++ b/src/test/java/dev/openfeature/sdk/EventsTest.java
@@ -19,7 +19,7 @@
class EventsTest {
- private static final int TIMEOUT = 300;
+ private static final int TIMEOUT = 500;
private static final int INIT_DELAY = TIMEOUT / 2;
@AfterAll
@@ -601,13 +601,13 @@ void matchingStaleEventsMustRunImmediately() {
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
// provider which is already stale
- TestEventsProvider provider = TestEventsProvider.newInitializedTestEventsProvider();
+ TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
Client client = api.getClient(name);
api.setProviderAndWait(name, provider);
provider.emitProviderStale(ProviderEventDetails.builder().build());
assertThat(client.getProviderState()).isEqualTo(ProviderState.STALE);
- // should run even thought handler was added after stale
+ // should run even though handler was added after stale
client.onProviderStale(handler);
verify(handler, timeout(TIMEOUT)).accept(any());
}
@@ -623,13 +623,13 @@ void matchingErrorEventsMustRunImmediately() {
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
// provider which is already in error
- TestEventsProvider provider = new TestEventsProvider();
+ TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
Client client = api.getClient(name);
api.setProviderAndWait(name, provider);
provider.emitProviderError(ProviderEventDetails.builder().build());
assertThat(client.getProviderState()).isEqualTo(ProviderState.ERROR);
- // should run even thought handler was added after error
+ // should run even though handler was added after error
client.onProviderError(handler);
verify(handler, timeout(TIMEOUT)).accept(any());
}
diff --git a/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java b/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java
new file mode 100644
index 00000000..d1bf65c5
--- /dev/null
+++ b/src/test/java/dev/openfeature/sdk/testutils/TestStackedEmitCallsProvider.java
@@ -0,0 +1,103 @@
+package dev.openfeature.sdk.testutils;
+
+import dev.openfeature.sdk.EvaluationContext;
+import dev.openfeature.sdk.EventProvider;
+import dev.openfeature.sdk.Metadata;
+import dev.openfeature.sdk.ProviderEvaluation;
+import dev.openfeature.sdk.ProviderEvent;
+import dev.openfeature.sdk.ProviderEventDetails;
+import dev.openfeature.sdk.Value;
+import java.util.function.Consumer;
+
+public class TestStackedEmitCallsProvider extends EventProvider {
+ private final NestedBlockingEmitter nestedBlockingEmitter = new NestedBlockingEmitter(this::onProviderEvent);
+
+ @Override
+ public Metadata getMetadata() {
+ return () -> getClass().getSimpleName();
+ }
+
+ @Override
+ public void initialize(EvaluationContext evaluationContext) throws Exception {
+ synchronized (nestedBlockingEmitter) {
+ nestedBlockingEmitter.init();
+ while (!nestedBlockingEmitter.isReady()) {
+ try {
+ nestedBlockingEmitter.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ private void onProviderEvent(ProviderEvent providerEvent) {
+ synchronized (nestedBlockingEmitter) {
+ if (providerEvent == ProviderEvent.PROVIDER_READY) {
+ nestedBlockingEmitter.setReady();
+ /*
+ * This line deadlocked in the original implementation without the emitterExecutor see
+ * https://github.com/open-feature/java-sdk/issues/1299
+ */
+ emitProviderReady(ProviderEventDetails.builder().build());
+ }
+ }
+ }
+
+ @Override
+ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
+ throw new UnsupportedOperationException("Unimplemented method 'getBooleanEvaluation'");
+ }
+
+ @Override
+ public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
+ throw new UnsupportedOperationException("Unimplemented method 'getStringEvaluation'");
+ }
+
+ @Override
+ public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
+ throw new UnsupportedOperationException("Unimplemented method 'getIntegerEvaluation'");
+ }
+
+ @Override
+ public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
+ throw new UnsupportedOperationException("Unimplemented method 'getDoubleEvaluation'");
+ }
+
+ @Override
+ public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) {
+ throw new UnsupportedOperationException("Unimplemented method 'getObjectEvaluation'");
+ }
+
+ static class NestedBlockingEmitter {
+
+ private final Consumer emitProviderEvent;
+ private volatile boolean isReady;
+
+ public NestedBlockingEmitter(Consumer emitProviderEvent) {
+ this.emitProviderEvent = emitProviderEvent;
+ }
+
+ public void init() {
+ // run init outside monitored thread
+ new Thread(() -> {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ emitProviderEvent.accept(ProviderEvent.PROVIDER_READY);
+ })
+ .start();
+ }
+
+ public boolean isReady() {
+ return isReady;
+ }
+
+ public synchronized void setReady() {
+ isReady = true;
+ this.notifyAll();
+ }
+ }
+}
diff --git a/version.txt b/version.txt
index 850e7424..63e799cf 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.14.0
+1.14.1