Compare commits
70 Commits
Author | SHA1 | Date |
---|---|---|
|
ac3344c7f6 | |
|
ecc8f7e3ad | |
|
cbf7a58622 | |
|
1382b367d9 | |
|
5b3e3656f6 | |
|
1d3fab6184 | |
|
006ae75e2b | |
|
aa0569379b | |
|
bf68cbdedf | |
|
62738f7f16 | |
|
1e8f5c880c | |
|
488196656a | |
|
26716a51cf | |
|
908755c2c2 | |
|
d2b1dc3a41 | |
|
85d89ee79a | |
|
6194186b3e | |
|
5425a34a12 | |
|
957c0d1ba3 | |
|
ebea0fdf1c | |
|
08f549afd1 | |
|
a5d1cbced4 | |
|
0fd9d3dcfb | |
|
0515ad54c4 | |
|
69519b1ef7 | |
|
2e3b479cb1 | |
|
49214b7282 | |
|
fc430c3e1d | |
|
86a5916f0d | |
|
34b22e8d93 | |
|
300a705e0a | |
|
b64efe82d9 | |
|
6f67b06f71 | |
|
e67f598357 | |
|
312b6df5d2 | |
|
8fad544b17 | |
|
c3eaecdb8b | |
|
7c2af57a36 | |
|
8bf777a7e9 | |
|
936ff60fac | |
|
99a3006de8 | |
|
8e51e6fe10 | |
|
844d5e244b | |
|
0b57bcafc1 | |
|
3dd7d5d426 | |
|
6cca721be5 | |
|
4481537ceb | |
|
b5d873e44d | |
|
545d6aac09 | |
|
2dcd6a1dd0 | |
|
3ed65cfb0c | |
|
6597de7a98 | |
|
376f81f5c3 | |
|
1558a86249 | |
|
f8260a1c3a | |
|
50a6b168a7 | |
|
b6ceff2ecb | |
|
2de7616676 | |
|
f10aaaa357 | |
|
40b319c5de | |
|
5e922cf3ef | |
|
6a95c008e9 | |
|
dcbfd265a3 | |
|
e17b0b2975 | |
|
b45a937017 | |
|
36eed065e7 | |
|
e3379395e6 | |
|
b667aa3251 | |
|
1714efe81a | |
|
d9a72d2aaf |
|
@ -21,19 +21,19 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4
|
||||
uses: actions/setup-java@ae2b61dbc685e60e4427b2e8ed4f0135c6ea8597
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: maven
|
||||
server-id: ossrh
|
||||
server-username: ${{ secrets.OSSRH_USERNAME }}
|
||||
server-password: ${{ secrets.OSSRH_PASSWORD }}
|
||||
server-id: central
|
||||
server-username: ${{ secrets.CENTRAL_USERNAME }}
|
||||
server-password: ${{ secrets.CENTRAL_PASSWORD }}
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
uses: actions/cache@640a1c2554105b57832a23eea0b4672fc7a790d5
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-17-maven-${{ hashFiles('**/pom.xml') }}
|
||||
|
@ -50,7 +50,7 @@ jobs:
|
|||
run: mvn --batch-mode --update-snapshots verify
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5.4.2
|
||||
uses: codecov/codecov-action@v5.4.3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
||||
flags: unittests # optional
|
||||
|
@ -65,7 +65,7 @@ jobs:
|
|||
- name: Deploy
|
||||
run: |
|
||||
mvn --batch-mode \
|
||||
--settings release/m2-settings.xml clean deploy
|
||||
--settings release/m2-settings.xml -DskipTests clean deploy
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
|
||||
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
|
||||
|
|
|
@ -19,22 +19,22 @@ jobs:
|
|||
runs-on: ${{ matrix.os}}
|
||||
steps:
|
||||
- name: Check out the code
|
||||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4
|
||||
uses: actions/setup-java@ae2b61dbc685e60e4427b2e8ed4f0135c6ea8597
|
||||
with:
|
||||
java-version: ${{ matrix.build.java }}
|
||||
distribution: 'temurin'
|
||||
cache: maven
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@15bce5bb14748fcfd6fe32738ca1cba36e5f218f
|
||||
uses: github/codeql-action/init@7710ed11e398ea99c7f7004c2b2e0f580458db42
|
||||
with:
|
||||
languages: java
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684
|
||||
uses: actions/cache@640a1c2554105b57832a23eea0b4672fc7a790d5
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}${{ matrix.build.java }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
|
@ -46,7 +46,7 @@ jobs:
|
|||
|
||||
- if: matrix.build.java == '17'
|
||||
name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5.4.2
|
||||
uses: codecov/codecov-action@v5.4.3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
||||
flags: unittests # optional
|
||||
|
@ -55,4 +55,4 @@ jobs:
|
|||
verbose: true # optional (default = false)
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@15bce5bb14748fcfd6fe32738ca1cba36e5f218f
|
||||
uses: github/codeql-action/analyze@7710ed11e398ea99c7f7004c2b2e0f580458db42
|
||||
|
|
|
@ -12,11 +12,11 @@ permissions: # added using https://github.com/step-security/secure-workflows
|
|||
|
||||
jobs:
|
||||
release-please:
|
||||
environment: publish
|
||||
permissions:
|
||||
contents: write # for google-github-actions/release-please-action to create release commit
|
||||
pull-requests: write # for google-github-actions/release-please-action to create release PR
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # for googleapis/release-please-action to create release commit
|
||||
pull-requests: write # for googleapis/release-please-action to create release PR
|
||||
issues: write # for googleapis/release-please-action to create labels
|
||||
|
||||
# Release-please creates a PR that tracks all changes
|
||||
steps:
|
||||
|
@ -24,34 +24,41 @@ jobs:
|
|||
id: release
|
||||
with:
|
||||
token: ${{secrets.RELEASE_PLEASE_ACTION_TOKEN}}
|
||||
outputs:
|
||||
release_created: ${{ fromJSON(steps.release.outputs.paths_released)[0] != null }} # if we have a single release path, do the release
|
||||
|
||||
publish:
|
||||
environment: publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
needs: release-please
|
||||
if: ${{ fromJSON(needs.release-please.outputs.release_created || false) }}
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
|
||||
# These steps are only run if this was a merged release-please PR
|
||||
- name: checkout
|
||||
if: ${{ steps.release.outputs.release_created }}
|
||||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
- name: Set up JDK 17
|
||||
if: ${{ steps.release.outputs.release_created }}
|
||||
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4
|
||||
uses: actions/setup-java@ae2b61dbc685e60e4427b2e8ed4f0135c6ea8597
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
cache: maven
|
||||
server-id: ossrh
|
||||
server-username: ${{ secrets.OSSRH_USERNAME }}
|
||||
server-password: ${{ secrets.OSSRH_PASSWORD }}
|
||||
server-id: central
|
||||
server-username: ${{ secrets.CENTRAL_USERNAME }}
|
||||
server-password: ${{ secrets.CENTRAL_PASSWORD }}
|
||||
|
||||
- name: Configure GPG Key
|
||||
if: ${{ steps.release.outputs.release_created }}
|
||||
run: |
|
||||
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import
|
||||
env:
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||
|
||||
- name: Deploy
|
||||
if: ${{ steps.release.outputs.release_created }}
|
||||
run: |
|
||||
mvn --batch-mode \
|
||||
--settings release/m2-settings.xml clean deploy
|
||||
--settings release/m2-settings.xml -DskipTests clean deploy
|
||||
env:
|
||||
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
|
||||
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
|
||||
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
|
||||
|
|
|
@ -29,16 +29,16 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
|
||||
uses: actions/checkout@09d2acae674a48949e3602304ab46fd20ae0c42f
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@15bce5bb14748fcfd6fe32738ca1cba36e5f218f
|
||||
uses: github/codeql-action/init@7710ed11e398ea99c7f7004c2b2e0f580458db42
|
||||
with:
|
||||
languages: java
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@15bce5bb14748fcfd6fe32738ca1cba36e5f218f
|
||||
uses: github/codeql-action/autobuild@7710ed11e398ea99c7f7004c2b2e0f580458db42
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@15bce5bb14748fcfd6fe32738ca1cba36e5f218f
|
||||
uses: github/codeql-action/analyze@7710ed11e398ea99c7f7004c2b2e0f580458db42
|
||||
|
|
|
@ -16,4 +16,4 @@
|
|||
# under the License.
|
||||
wrapperVersion=3.3.2
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
|
|
|
@ -1 +1 @@
|
|||
{".":"1.15.1"}
|
||||
{".":"1.16.0"}
|
||||
|
|
69
CHANGELOG.md
69
CHANGELOG.md
|
@ -1,5 +1,74 @@
|
|||
# Changelog
|
||||
|
||||
## [1.16.0](https://github.com/open-feature/java-sdk/compare/v1.15.1...v1.16.0) (2025-07-07)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
* **deps:** update dependency io.cucumber:cucumber-bom to v7.23.0 ([#1466](https://github.com/open-feature/java-sdk/issues/1466)) ([50a6b16](https://github.com/open-feature/java-sdk/commit/50a6b168a7de40337aa51ef3d79d122030956cb9))
|
||||
* **deps:** update dependency org.junit:junit-bom to v5.13.1 ([#1475](https://github.com/open-feature/java-sdk/issues/1475)) ([545d6aa](https://github.com/open-feature/java-sdk/commit/545d6aac09dbc74c00a0a4e5c26f4ef80be22379))
|
||||
* **deps:** update dependency org.junit:junit-bom to v5.13.2 ([#1492](https://github.com/open-feature/java-sdk/issues/1492)) ([34b22e8](https://github.com/open-feature/java-sdk/commit/34b22e8d93a986fdb81500ab539b4d2fe038b618))
|
||||
* **deps:** update dependency org.junit:junit-bom to v5.13.3 ([#1505](https://github.com/open-feature/java-sdk/issues/1505)) ([957c0d1](https://github.com/open-feature/java-sdk/commit/957c0d1ba38ecc758c1ec164e40070ac93a01d68))
|
||||
* **deps:** update junit5 monorepo ([#1467](https://github.com/open-feature/java-sdk/issues/1467)) ([f8260a1](https://github.com/open-feature/java-sdk/commit/f8260a1c3a345c877eba95bfe41184ad11f6555e))
|
||||
* Reduce locking and concurrency issues ([#1478](https://github.com/open-feature/java-sdk/issues/1478)) ([ebea0fd](https://github.com/open-feature/java-sdk/commit/ebea0fdf1cf3e6f4d2e8aebf2dcb7c7e1f31acc2))
|
||||
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
* add means of awaiting event emission, fix flaky build ([#1463](https://github.com/open-feature/java-sdk/issues/1463)) ([3dd7d5d](https://github.com/open-feature/java-sdk/commit/3dd7d5d4262f1f4461e13c13a7d64d2fa8bfd764)), closes [#1449](https://github.com/open-feature/java-sdk/issues/1449)
|
||||
|
||||
|
||||
### 🧹 Chore
|
||||
|
||||
* **deps:** update actions/cache digest to 640a1c2 ([#1485](https://github.com/open-feature/java-sdk/issues/1485)) ([7c2af57](https://github.com/open-feature/java-sdk/commit/7c2af57a362ee11f757a431ee17eff3ee448bf6c))
|
||||
* **deps:** update actions/checkout digest to 09d2aca ([#1473](https://github.com/open-feature/java-sdk/issues/1473)) ([b5d873e](https://github.com/open-feature/java-sdk/commit/b5d873e44d3c41b42f11569b0fafccc0a002ebdd))
|
||||
* **deps:** update actions/setup-java digest to 67aec00 ([#1504](https://github.com/open-feature/java-sdk/issues/1504)) ([08f549a](https://github.com/open-feature/java-sdk/commit/08f549afd1fd26581b2a8e063832ec986c5e3267))
|
||||
* **deps:** update actions/setup-java digest to ebb356c ([#1490](https://github.com/open-feature/java-sdk/issues/1490)) ([e67f598](https://github.com/open-feature/java-sdk/commit/e67f5983573afff805a56ef18584d1a7291ccafc))
|
||||
* **deps:** update codecov/codecov-action action to v5.4.3 ([#1454](https://github.com/open-feature/java-sdk/issues/1454)) ([e337939](https://github.com/open-feature/java-sdk/commit/e3379395e6bfb0ce811d8372761a3cb015ad2cde))
|
||||
* **deps:** update dependency com.diffplug.spotless:spotless-maven-plugin to v2.44.5 ([#1462](https://github.com/open-feature/java-sdk/issues/1462)) ([40b319c](https://github.com/open-feature/java-sdk/commit/40b319c5de0461bec13f76978ae09edc958310cd))
|
||||
* **deps:** update dependency com.github.spotbugs:spotbugs-maven-plugin to v4.9.3.1 ([#1493](https://github.com/open-feature/java-sdk/issues/1493)) ([b64efe8](https://github.com/open-feature/java-sdk/commit/b64efe82d993defe070dfeb9aa60e740ccf757cd))
|
||||
* **deps:** update dependency com.github.spotbugs:spotbugs-maven-plugin to v4.9.3.2 ([#1496](https://github.com/open-feature/java-sdk/issues/1496)) ([fc430c3](https://github.com/open-feature/java-sdk/commit/fc430c3e1d57a532d8c0c879c3e7e25c46d4ad84))
|
||||
* **deps:** update dependency com.puppycrawl.tools:checkstyle to v10.24.0 ([#1458](https://github.com/open-feature/java-sdk/issues/1458)) ([dcbfd26](https://github.com/open-feature/java-sdk/commit/dcbfd265a3875271695af760fce9870e53c69f13))
|
||||
* **deps:** update dependency com.puppycrawl.tools:checkstyle to v10.25.0 ([#1468](https://github.com/open-feature/java-sdk/issues/1468)) ([1558a86](https://github.com/open-feature/java-sdk/commit/1558a862497c0e133d11d53ff6d7f28437653d43))
|
||||
* **deps:** update dependency com.puppycrawl.tools:checkstyle to v10.25.1 ([#1489](https://github.com/open-feature/java-sdk/issues/1489)) ([312b6df](https://github.com/open-feature/java-sdk/commit/312b6df5d2c891ac758bf398f8399ecd25b7597e))
|
||||
* **deps:** update dependency com.puppycrawl.tools:checkstyle to v10.26.0 ([#1494](https://github.com/open-feature/java-sdk/issues/1494)) ([300a705](https://github.com/open-feature/java-sdk/commit/300a705e0af959da7ed0e88e9975379ff6fc4138))
|
||||
* **deps:** update dependency com.puppycrawl.tools:checkstyle to v10.26.1 ([#1498](https://github.com/open-feature/java-sdk/issues/1498)) ([2e3b479](https://github.com/open-feature/java-sdk/commit/2e3b479cb1e8b0b65652ee813eaa2e1940d53c8e))
|
||||
* **deps:** update dependency maven to v3.9.10 ([#1474](https://github.com/open-feature/java-sdk/issues/1474)) ([4481537](https://github.com/open-feature/java-sdk/commit/4481537cebc213dcfe19bb8cd9b70a4c91a682b2))
|
||||
* **deps:** update dependency net.bytebuddy:byte-buddy to v1.17.6 ([#1482](https://github.com/open-feature/java-sdk/issues/1482)) ([8e51e6f](https://github.com/open-feature/java-sdk/commit/8e51e6fe101882184a5d09be31fa65563d82c673))
|
||||
* **deps:** update dependency net.bytebuddy:byte-buddy-agent to v1.17.6 ([#1483](https://github.com/open-feature/java-sdk/issues/1483)) ([936ff60](https://github.com/open-feature/java-sdk/commit/936ff60fac471a83a7c14412d2e825b2a7f9704c))
|
||||
* **deps:** update dependency org.apache.maven.plugins:maven-gpg-plugin to v3.2.8 ([#1501](https://github.com/open-feature/java-sdk/issues/1501)) ([0515ad5](https://github.com/open-feature/java-sdk/commit/0515ad54c4f71863373eb1b7f429393923b27d90))
|
||||
* **deps:** update dependency org.codehaus.mojo:exec-maven-plugin to v3.5.1 ([#1461](https://github.com/open-feature/java-sdk/issues/1461)) ([b6ceff2](https://github.com/open-feature/java-sdk/commit/b6ceff2ecb0e34be2ccdb83f7f37c1177de6f27e))
|
||||
* **deps:** update dependency org.mockito:mockito-core to v5.18.0 ([#1457](https://github.com/open-feature/java-sdk/issues/1457)) ([e17b0b2](https://github.com/open-feature/java-sdk/commit/e17b0b29758ae7cdbdac9ddb2178382c55eb1277))
|
||||
* **deps:** update github/codeql-action digest to 075e08a ([#1470](https://github.com/open-feature/java-sdk/issues/1470)) ([6597de7](https://github.com/open-feature/java-sdk/commit/6597de7a98e0fae10a541a8a9b60837623c133a8))
|
||||
* **deps:** update github/codeql-action digest to 33f8489 ([#1502](https://github.com/open-feature/java-sdk/issues/1502)) ([0fd9d3d](https://github.com/open-feature/java-sdk/commit/0fd9d3dcfb1fd65197a42885b12d40a1cc152d3b))
|
||||
* **deps:** update github/codeql-action digest to 396fd27 ([#1456](https://github.com/open-feature/java-sdk/issues/1456)) ([b45a937](https://github.com/open-feature/java-sdk/commit/b45a9370173e3d3b97c78449dfc99225fb572228))
|
||||
* **deps:** update github/codeql-action digest to 3de706a ([#1481](https://github.com/open-feature/java-sdk/issues/1481)) ([99a3006](https://github.com/open-feature/java-sdk/commit/99a3006de878ab0ba1f0e61a4cb5432914425795))
|
||||
* **deps:** update github/codeql-action digest to 466d6ce ([#1477](https://github.com/open-feature/java-sdk/issues/1477)) ([0b57bca](https://github.com/open-feature/java-sdk/commit/0b57bcafc14b946000feb4a3421d73b9616e83cb))
|
||||
* **deps:** update github/codeql-action digest to 4a00331 ([#1469](https://github.com/open-feature/java-sdk/issues/1469)) ([376f81f](https://github.com/open-feature/java-sdk/commit/376f81f5c3b66d7e3e298aac30ac7544b84e7362))
|
||||
* **deps:** update github/codeql-action digest to 4c57370 ([#1497](https://github.com/open-feature/java-sdk/issues/1497)) ([49214b7](https://github.com/open-feature/java-sdk/commit/49214b7282ddde1ee16cf80f92c11cc90ef7612a))
|
||||
* **deps:** update github/codeql-action digest to 510dfa3 ([#1450](https://github.com/open-feature/java-sdk/issues/1450)) ([d9a72d2](https://github.com/open-feature/java-sdk/commit/d9a72d2aafd787a1814132f000897ad1c94181e4))
|
||||
* **deps:** update github/codeql-action digest to 57eebf6 ([#1455](https://github.com/open-feature/java-sdk/issues/1455)) ([36eed06](https://github.com/open-feature/java-sdk/commit/36eed065e763bbfa0f8f97d704202bbd219332ca))
|
||||
* **deps:** update github/codeql-action digest to 66d7255 ([#1487](https://github.com/open-feature/java-sdk/issues/1487)) ([c3eaecd](https://github.com/open-feature/java-sdk/commit/c3eaecdb8b34d3b33946bd205ee92d49584602bd))
|
||||
* **deps:** update github/codeql-action digest to 7b0fb5a ([#1459](https://github.com/open-feature/java-sdk/issues/1459)) ([6a95c00](https://github.com/open-feature/java-sdk/commit/6a95c008e975dd3c7328c32f1d7cf626bbaecfa6))
|
||||
* **deps:** update github/codeql-action digest to 7cb9b16 ([#1476](https://github.com/open-feature/java-sdk/issues/1476)) ([6cca721](https://github.com/open-feature/java-sdk/commit/6cca721be5bc6f5926fe64668a7c03728cab3cb0))
|
||||
* **deps:** update github/codeql-action digest to 7fd6215 ([#1464](https://github.com/open-feature/java-sdk/issues/1464)) ([f10aaaa](https://github.com/open-feature/java-sdk/commit/f10aaaa357581b573895f4d6e2329abb705582aa))
|
||||
* **deps:** update github/codeql-action digest to 8ef1782 ([#1495](https://github.com/open-feature/java-sdk/issues/1495)) ([86a5916](https://github.com/open-feature/java-sdk/commit/86a5916f0dc6116b5b9e5dc897ff4b8705ac01e3))
|
||||
* **deps:** update github/codeql-action digest to 9b02dc2 ([#1491](https://github.com/open-feature/java-sdk/issues/1491)) ([6f67b06](https://github.com/open-feature/java-sdk/commit/6f67b06f712c461f331681a76f5cb2c3ddb0d36b))
|
||||
* **deps:** update github/codeql-action digest to ac30a39 ([#1488](https://github.com/open-feature/java-sdk/issues/1488)) ([8fad544](https://github.com/open-feature/java-sdk/commit/8fad544b17ee08b4280d7975073d00a874c374db))
|
||||
* **deps:** update github/codeql-action digest to b1e4dc3 ([#1471](https://github.com/open-feature/java-sdk/issues/1471)) ([2dcd6a1](https://github.com/open-feature/java-sdk/commit/2dcd6a1dd0c80ee676b9860afd6a6002d0ea4aea))
|
||||
* **deps:** update github/codeql-action digest to b694213 ([#1503](https://github.com/open-feature/java-sdk/issues/1503)) ([a5d1cbc](https://github.com/open-feature/java-sdk/commit/a5d1cbced4658fadb63f362b4512bdbd68ae7d6a))
|
||||
* **deps:** update github/codeql-action digest to b86edfc ([#1453](https://github.com/open-feature/java-sdk/issues/1453)) ([b667aa3](https://github.com/open-feature/java-sdk/commit/b667aa325136b78c01867d40342f81eeb7e16f46))
|
||||
* **deps:** update github/codeql-action digest to bc02a25 ([#1460](https://github.com/open-feature/java-sdk/issues/1460)) ([5e922cf](https://github.com/open-feature/java-sdk/commit/5e922cf3efc156135563707de92e508b0a4d19f3))
|
||||
* **deps:** update github/codeql-action digest to be30325 ([#1479](https://github.com/open-feature/java-sdk/issues/1479)) ([844d5e2](https://github.com/open-feature/java-sdk/commit/844d5e244b02703b624cf75e5bf8448c07e62d3d))
|
||||
* **deps:** update github/codeql-action digest to dcc1a66 ([#1499](https://github.com/open-feature/java-sdk/issues/1499)) ([69519b1](https://github.com/open-feature/java-sdk/commit/69519b1ef7274ceae39d6746c5a5a98dc69f562f))
|
||||
* **deps:** update github/codeql-action digest to ef36b69 ([#1484](https://github.com/open-feature/java-sdk/issues/1484)) ([8bf777a](https://github.com/open-feature/java-sdk/commit/8bf777a7e99be4dfac8917b8e61cb6c23385b8ce))
|
||||
* **deps:** update io.cucumber.version to v7.23.0 ([#1465](https://github.com/open-feature/java-sdk/issues/1465)) ([2de7616](https://github.com/open-feature/java-sdk/commit/2de76166764bacd34883b13220dd0bad824c8b1a))
|
||||
* improvements to release workflow ([#1451](https://github.com/open-feature/java-sdk/issues/1451)) ([1714efe](https://github.com/open-feature/java-sdk/commit/1714efe81aa6ae025f4f8b12c9c042561498d25e))
|
||||
* migrate to new publish ([5425a34](https://github.com/open-feature/java-sdk/commit/5425a34a12baa04f9583b83fd1bfdd7e2a6ab5e8))
|
||||
* remove unneeded version information ([#1428](https://github.com/open-feature/java-sdk/issues/1428)) ([3ed65cf](https://github.com/open-feature/java-sdk/commit/3ed65cfb0cb5ee5b70793cd68a27909c81cd4fab))
|
||||
* skip tests on publish ([6194186](https://github.com/open-feature/java-sdk/commit/6194186b3e791f3cb28da24f5acb3ff96788d65e))
|
||||
* update publish env vars ([85d89ee](https://github.com/open-feature/java-sdk/commit/85d89ee79a52d960322731fb786c0f60245f0d75))
|
||||
|
||||
## [1.15.1](https://github.com/open-feature/java-sdk/compare/v1.14.2...v1.15.1) (2025-05-14)
|
||||
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
</a>
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
<a href="https://github.com/open-feature/java-sdk/releases/tag/v1.15.1">
|
||||
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.15.1&color=blue&style=for-the-badge" />
|
||||
<a href="https://github.com/open-feature/java-sdk/releases/tag/v1.16.0">
|
||||
<img alt="Release" src="https://img.shields.io/static/v1?label=release&message=v1.16.0&color=blue&style=for-the-badge" />
|
||||
</a>
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
@ -59,7 +59,7 @@ Note that this library is intended to be used in server-side contexts and has no
|
|||
<dependency>
|
||||
<groupId>dev.openfeature</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>1.15.1</version>
|
||||
<version>1.16.0</version>
|
||||
</dependency>
|
||||
```
|
||||
<!-- x-release-please-end-version -->
|
||||
|
@ -84,7 +84,7 @@ If you would like snapshot builds, this is the relevant repository information:
|
|||
<!-- x-release-please-start-version -->
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'dev.openfeature:sdk:1.15.1'
|
||||
implementation 'dev.openfeature:sdk:1.16.0'
|
||||
}
|
||||
```
|
||||
<!-- x-release-please-end-version -->
|
||||
|
|
49
pom.xml
49
pom.xml
|
@ -5,16 +5,14 @@
|
|||
|
||||
<groupId>dev.openfeature</groupId>
|
||||
<artifactId>sdk</artifactId>
|
||||
<version>1.15.1</version> <!--x-release-please-version -->
|
||||
<version>1.16.0</version> <!--x-release-please-version -->
|
||||
|
||||
<properties>
|
||||
<toolchain.jdk.version>[17,)</toolchain.jdk.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
|
||||
<junit.jupiter.version>5.12.2</junit.jupiter.version>
|
||||
<io.cucumber.version>7.22.2</io.cucumber.version>
|
||||
<org.mockito.version>5.17.0</org.mockito.version>
|
||||
<org.mockito.version>5.18.0</org.mockito.version>
|
||||
<!-- exclusion expression for e2e tests -->
|
||||
<testExclusions>**/e2e/*.java</testExclusions>
|
||||
<module-name>${project.groupId}.${project.artifactId}</module-name>
|
||||
|
@ -97,56 +95,48 @@
|
|||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.jupiter.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-suite</artifactId>
|
||||
<version>1.12.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.cucumber</groupId>
|
||||
<artifactId>cucumber-java</artifactId>
|
||||
<version>${io.cucumber.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.cucumber</groupId>
|
||||
<artifactId>cucumber-junit-platform-engine</artifactId>
|
||||
<version>${io.cucumber.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.cucumber</groupId>
|
||||
<artifactId>cucumber-picocontainer</artifactId>
|
||||
<version>${io.cucumber.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -160,7 +150,7 @@
|
|||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.4.0-jre</version>
|
||||
<version>33.4.8-jre</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -189,14 +179,14 @@
|
|||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>1.17.5</version>
|
||||
<version>1.17.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<version>1.17.5</version>
|
||||
<version>1.17.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- End mockito workaround-->
|
||||
|
@ -204,7 +194,7 @@
|
|||
<dependency>
|
||||
<groupId>io.cucumber</groupId>
|
||||
<artifactId>cucumber-bom</artifactId>
|
||||
<version>7.22.2</version>
|
||||
<version>7.26.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
@ -212,7 +202,7 @@
|
|||
<dependency>
|
||||
<groupId>org.junit</groupId>
|
||||
<artifactId>junit-bom</artifactId>
|
||||
<version>5.12.2</version>
|
||||
<version>5.13.3</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
@ -413,7 +403,7 @@
|
|||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>4.9.3.0</version>
|
||||
<version>4.9.3.2</version>
|
||||
<configuration>
|
||||
<excludeFilterFile>spotbugs-exclusions.xml</excludeFilterFile>
|
||||
<plugins>
|
||||
|
@ -457,7 +447,7 @@
|
|||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.23.1</version>
|
||||
<version>10.26.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
|
@ -473,7 +463,7 @@
|
|||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
<version>2.44.4</version>
|
||||
<version>2.45.0</version>
|
||||
<configuration>
|
||||
<!-- optional: limit format enforcement to just the files changed by this feature branch -->
|
||||
<!-- <ratchetFrom>origin/main</ratchetFrom>-->
|
||||
|
@ -530,14 +520,13 @@
|
|||
<plugins>
|
||||
<!-- Begin publish to maven central -->
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<groupId>org.sonatype.central</groupId>
|
||||
<artifactId>central-publishing-maven-plugin</artifactId>
|
||||
<version>0.8.0</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
<publishingServerId>central</publishingServerId>
|
||||
<autoPublish>true</autoPublish>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- End publish to maven central -->
|
||||
|
@ -581,7 +570,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>3.2.7</version>
|
||||
<version>3.2.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
|
@ -622,7 +611,7 @@
|
|||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>update-test-harness-submodule</id>
|
||||
|
@ -721,8 +710,8 @@
|
|||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
|
||||
<id>central</id>
|
||||
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
|
|
|
@ -5,5 +5,10 @@
|
|||
<username>${env.OSSRH_USERNAME}</username>
|
||||
<password>${env.OSSRH_PASSWORD}</password>
|
||||
</server>
|
||||
<server>
|
||||
<id>central</id>
|
||||
<username>${env.CENTRAL_USERNAME}</username>
|
||||
<password>${env.CENTRAL_PASSWORD}</password>
|
||||
</server>
|
||||
</servers>
|
||||
</settings>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package dev.openfeature.sdk;
|
||||
|
||||
/**
|
||||
* A class to help with synchronization by allowing the optional awaiting of the associated action.
|
||||
*/
|
||||
public class Awaitable {
|
||||
|
||||
/**
|
||||
* An already-completed Awaitable. Awaiting this will return immediately.
|
||||
*/
|
||||
public static final Awaitable FINISHED = new Awaitable(true);
|
||||
|
||||
private boolean isDone = false;
|
||||
|
||||
public Awaitable() {}
|
||||
|
||||
private Awaitable(boolean isDone) {
|
||||
this.isDone = isDone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets the calling thread wait until some other thread calls {@link Awaitable#wakeup()}. If
|
||||
* {@link Awaitable#wakeup()} has been called before the current thread invokes this method, it will return
|
||||
* immediately.
|
||||
*/
|
||||
@SuppressWarnings("java:S2142")
|
||||
public synchronized void await() {
|
||||
while (!isDone) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException ignored) {
|
||||
// ignored, do not propagate the interrupted state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakes up all threads that have called {@link Awaitable#await()} and lets them proceed.
|
||||
*/
|
||||
public synchronized void wakeup() {
|
||||
isDone = true;
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
|
@ -76,15 +76,32 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
* @param event The event type
|
||||
* @param details The details of the event
|
||||
*/
|
||||
public void emit(ProviderEvent event, ProviderEventDetails details) {
|
||||
if (eventProviderListener != null) {
|
||||
eventProviderListener.onEmit(event, details);
|
||||
public Awaitable emit(final ProviderEvent event, final ProviderEventDetails details) {
|
||||
final var localEventProviderListener = this.eventProviderListener;
|
||||
final var localOnEmit = this.onEmit;
|
||||
|
||||
if (localEventProviderListener == null && localOnEmit == null) {
|
||||
return Awaitable.FINISHED;
|
||||
}
|
||||
|
||||
final TriConsumer<EventProvider, ProviderEvent, ProviderEventDetails> localOnEmit = this.onEmit;
|
||||
if (localOnEmit != null) {
|
||||
emitterExecutor.submit(() -> localOnEmit.accept(this, event, details));
|
||||
}
|
||||
final var awaitable = new Awaitable();
|
||||
|
||||
// These calls need to be executed on a different thread to prevent deadlocks when the provider initialization
|
||||
// relies on a ready event to be emitted
|
||||
emitterExecutor.submit(() -> {
|
||||
try (var ignored = OpenFeatureAPI.lock.readLockAutoCloseable()) {
|
||||
if (localEventProviderListener != null) {
|
||||
localEventProviderListener.onEmit(event, details);
|
||||
}
|
||||
if (localOnEmit != null) {
|
||||
localOnEmit.accept(this, event, details);
|
||||
}
|
||||
} finally {
|
||||
awaitable.wakeup();
|
||||
}
|
||||
});
|
||||
|
||||
return awaitable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,8 +110,8 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
*
|
||||
* @param details The details of the event
|
||||
*/
|
||||
public void emitProviderReady(ProviderEventDetails details) {
|
||||
emit(ProviderEvent.PROVIDER_READY, details);
|
||||
public Awaitable emitProviderReady(ProviderEventDetails details) {
|
||||
return emit(ProviderEvent.PROVIDER_READY, details);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,8 +121,8 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
*
|
||||
* @param details The details of the event
|
||||
*/
|
||||
public void emitProviderConfigurationChanged(ProviderEventDetails details) {
|
||||
emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
|
||||
public Awaitable emitProviderConfigurationChanged(ProviderEventDetails details) {
|
||||
return emit(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, details);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,8 +131,8 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
*
|
||||
* @param details The details of the event
|
||||
*/
|
||||
public void emitProviderStale(ProviderEventDetails details) {
|
||||
emit(ProviderEvent.PROVIDER_STALE, details);
|
||||
public Awaitable emitProviderStale(ProviderEventDetails details) {
|
||||
return emit(ProviderEvent.PROVIDER_STALE, details);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,7 +141,7 @@ public abstract class EventProvider implements FeatureProvider {
|
|||
*
|
||||
* @param details The details of the event
|
||||
*/
|
||||
public void emitProviderError(ProviderEventDetails details) {
|
||||
emit(ProviderEvent.PROVIDER_ERROR, details);
|
||||
public Awaitable emitProviderError(ProviderEventDetails details) {
|
||||
return emit(ProviderEvent.PROVIDER_ERROR, details);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package dev.openfeature.sdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -23,13 +23,10 @@ class EventSupport {
|
|||
|
||||
// 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 String DEFAULT_CLIENT_UUID = UUID.randomUUID().toString();
|
||||
private final Map<String, HandlerStore> handlerStores = new ConcurrentHashMap<>();
|
||||
private final HandlerStore globalHandlerStore = new HandlerStore();
|
||||
private final ExecutorService taskExecutor = Executors.newCachedThreadPool(runnable -> {
|
||||
final Thread thread = new Thread(runnable);
|
||||
return thread;
|
||||
});
|
||||
private final ExecutorService taskExecutor = Executors.newCachedThreadPool();
|
||||
|
||||
/**
|
||||
* Run all the event handlers associated with this domain.
|
||||
|
@ -40,11 +37,10 @@ class EventSupport {
|
|||
* @param eventDetails the event details
|
||||
*/
|
||||
public void runClientHandlers(String domain, ProviderEvent event, EventDetails eventDetails) {
|
||||
domain = Optional.ofNullable(domain).orElse(defaultClientUuid);
|
||||
domain = Optional.ofNullable(domain).orElse(DEFAULT_CLIENT_UUID);
|
||||
|
||||
// run handlers if they exist
|
||||
Optional.ofNullable(handlerStores.get(domain))
|
||||
.filter(store -> Optional.of(store).isPresent())
|
||||
.map(store -> store.handlerMap.get(event))
|
||||
.ifPresent(handlers -> handlers.forEach(handler -> runHandler(handler, eventDetails)));
|
||||
}
|
||||
|
@ -69,7 +65,7 @@ class EventSupport {
|
|||
* @param handler the handler function to run
|
||||
*/
|
||||
public void addClientHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
final String name = Optional.ofNullable(domain).orElse(defaultClientUuid);
|
||||
final String name = Optional.ofNullable(domain).orElse(DEFAULT_CLIENT_UUID);
|
||||
|
||||
// lazily create and cache a HandlerStore if it doesn't exist
|
||||
HandlerStore store = Optional.ofNullable(this.handlerStores.get(name)).orElseGet(() -> {
|
||||
|
@ -89,7 +85,7 @@ class EventSupport {
|
|||
* @param handler the handler ref to be removed
|
||||
*/
|
||||
public void removeClientHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
domain = Optional.ofNullable(domain).orElse(defaultClientUuid);
|
||||
domain = Optional.ofNullable(domain).orElse(DEFAULT_CLIENT_UUID);
|
||||
this.handlerStores.get(domain).removeHandler(event, handler);
|
||||
}
|
||||
|
||||
|
@ -160,14 +156,14 @@ class EventSupport {
|
|||
// instantiated when a handler is added to that client.
|
||||
static class HandlerStore {
|
||||
|
||||
private final Map<ProviderEvent, List<Consumer<EventDetails>>> handlerMap;
|
||||
private final Map<ProviderEvent, Collection<Consumer<EventDetails>>> handlerMap;
|
||||
|
||||
HandlerStore() {
|
||||
handlerMap = new ConcurrentHashMap<>();
|
||||
handlerMap.put(ProviderEvent.PROVIDER_READY, new ArrayList<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, new ArrayList<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_ERROR, new ArrayList<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_STALE, new ArrayList<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_READY, new ConcurrentLinkedQueue<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, new ConcurrentLinkedQueue<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_ERROR, new ConcurrentLinkedQueue<>());
|
||||
handlerMap.put(ProviderEvent.PROVIDER_STALE, new ConcurrentLinkedQueue<>());
|
||||
}
|
||||
|
||||
void addHandler(ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
|
|
|
@ -5,9 +5,12 @@ import dev.openfeature.sdk.internal.AutoCloseableLock;
|
|||
import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
@ -21,14 +24,14 @@ import lombok.extern.slf4j.Slf4j;
|
|||
public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
||||
// package-private multi-read/single-write lock
|
||||
static AutoCloseableReentrantReadWriteLock lock = new AutoCloseableReentrantReadWriteLock();
|
||||
private final List<Hook> apiHooks;
|
||||
private final ConcurrentLinkedQueue<Hook> apiHooks;
|
||||
private ProviderRepository providerRepository;
|
||||
private EventSupport eventSupport;
|
||||
private EvaluationContext evaluationContext;
|
||||
private final AtomicReference<EvaluationContext> evaluationContext = new AtomicReference<>();
|
||||
private TransactionContextPropagator transactionContextPropagator;
|
||||
|
||||
protected OpenFeatureAPI() {
|
||||
apiHooks = new ArrayList<>();
|
||||
apiHooks = new ConcurrentLinkedQueue<>();
|
||||
providerRepository = new ProviderRepository(this);
|
||||
eventSupport = new EventSupport();
|
||||
transactionContextPropagator = new NoOpTransactionContextPropagator();
|
||||
|
@ -115,9 +118,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @return api instance
|
||||
*/
|
||||
public OpenFeatureAPI setEvaluationContext(EvaluationContext evaluationContext) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
this.evaluationContext.set(evaluationContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -127,16 +128,14 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @return evaluation context
|
||||
*/
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
|
||||
return this.evaluationContext;
|
||||
}
|
||||
return evaluationContext.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction context propagator.
|
||||
*/
|
||||
public TransactionContextPropagator getTransactionContextPropagator() {
|
||||
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.readLockAutoCloseable()) {
|
||||
return this.transactionContextPropagator;
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +149,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
if (transactionContextPropagator == null) {
|
||||
throw new IllegalArgumentException("Transaction context propagator cannot be null");
|
||||
}
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
this.transactionContextPropagator = transactionContextPropagator;
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +175,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* Set the default provider.
|
||||
*/
|
||||
public void setProvider(FeatureProvider provider) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
providerRepository.setProvider(
|
||||
provider,
|
||||
this::attachEventProvider,
|
||||
|
@ -194,7 +193,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @param provider The provider to set.
|
||||
*/
|
||||
public void setProvider(String domain, FeatureProvider provider) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
providerRepository.setProvider(
|
||||
domain,
|
||||
provider,
|
||||
|
@ -216,7 +215,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @throws OpenFeatureError if the provider fails during initialization.
|
||||
*/
|
||||
public void setProviderAndWait(FeatureProvider provider) throws OpenFeatureError {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
providerRepository.setProvider(
|
||||
provider,
|
||||
this::attachEventProvider,
|
||||
|
@ -238,7 +237,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @throws OpenFeatureError if the provider fails during initialization.
|
||||
*/
|
||||
public void setProviderAndWait(String domain, FeatureProvider provider) throws OpenFeatureError {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
providerRepository.setProvider(
|
||||
domain,
|
||||
provider,
|
||||
|
@ -252,9 +251,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
|
||||
private void attachEventProvider(FeatureProvider provider) {
|
||||
if (provider instanceof EventProvider) {
|
||||
((EventProvider) provider).attach((p, event, details) -> {
|
||||
runHandlersForProvider(p, event, details);
|
||||
});
|
||||
((EventProvider) provider).attach(this::runHandlersForProvider);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,9 +304,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @param hooks The hook to add.
|
||||
*/
|
||||
public void addHooks(Hook... hooks) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
this.apiHooks.addAll(Arrays.asList(hooks));
|
||||
}
|
||||
this.apiHooks.addAll(Arrays.asList(hooks));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -318,18 +313,23 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @return A list of {@link Hook}s.
|
||||
*/
|
||||
public List<Hook> getHooks() {
|
||||
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
|
||||
return this.apiHooks;
|
||||
}
|
||||
return new ArrayList<>(this.apiHooks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the collection of {@link Hook}s.
|
||||
*
|
||||
* @return The collection of {@link Hook}s.
|
||||
*/
|
||||
Collection<Hook> getMutableHooks() {
|
||||
return this.apiHooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all hooks.
|
||||
*/
|
||||
public void clearHooks() {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
this.apiHooks.clear();
|
||||
}
|
||||
this.apiHooks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,7 +339,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* Once shut down is complete, API is reset and ready to use again.
|
||||
*/
|
||||
public void shutdown() {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
providerRepository.shutdown();
|
||||
eventSupport.shutdown();
|
||||
|
||||
|
@ -385,7 +385,7 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
*/
|
||||
@Override
|
||||
public OpenFeatureAPI on(ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
this.eventSupport.addGlobalHandler(event, handler);
|
||||
return this;
|
||||
}
|
||||
|
@ -396,18 +396,20 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
*/
|
||||
@Override
|
||||
public OpenFeatureAPI removeHandler(ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
this.eventSupport.removeGlobalHandler(event, handler);
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
this.eventSupport.removeGlobalHandler(event, handler);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void removeHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
eventSupport.removeClientHandler(domain, event, handler);
|
||||
}
|
||||
}
|
||||
|
||||
void addHandler(String domain, ProviderEvent event, Consumer<EventDetails> handler) {
|
||||
try (AutoCloseableLock __ = lock.writeLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.writeLockAutoCloseable()) {
|
||||
// if the provider is in the state associated with event, run immediately
|
||||
if (Optional.ofNullable(this.providerRepository.getProviderState(domain))
|
||||
.orElse(ProviderState.READY)
|
||||
|
@ -431,32 +433,28 @@ public class OpenFeatureAPI implements EventBus<OpenFeatureAPI> {
|
|||
* @param details the event details
|
||||
*/
|
||||
private void runHandlersForProvider(FeatureProvider provider, ProviderEvent event, ProviderEventDetails details) {
|
||||
try (AutoCloseableLock __ = lock.readLockAutoCloseable()) {
|
||||
try (AutoCloseableLock ignored = lock.readLockAutoCloseable()) {
|
||||
|
||||
List<String> domainsForProvider = providerRepository.getDomainsForProvider(provider);
|
||||
|
||||
final String providerName = Optional.ofNullable(provider.getMetadata())
|
||||
.map(metadata -> metadata.getName())
|
||||
.map(Metadata::getName)
|
||||
.orElse(null);
|
||||
|
||||
// run the global handlers
|
||||
eventSupport.runGlobalHandlers(event, EventDetails.fromProviderEventDetails(details, providerName));
|
||||
|
||||
// run the handlers associated with domains for this provider
|
||||
domainsForProvider.forEach(domain -> {
|
||||
eventSupport.runClientHandlers(
|
||||
domain, event, EventDetails.fromProviderEventDetails(details, providerName, domain));
|
||||
});
|
||||
domainsForProvider.forEach(domain -> eventSupport.runClientHandlers(
|
||||
domain, event, EventDetails.fromProviderEventDetails(details, providerName, domain)));
|
||||
|
||||
if (providerRepository.isDefaultProvider(provider)) {
|
||||
// run handlers for clients that have no bound providers (since this is the default)
|
||||
Set<String> allDomainNames = eventSupport.getAllDomainNames();
|
||||
Set<String> boundDomains = providerRepository.getAllBoundDomains();
|
||||
allDomainNames.removeAll(boundDomains);
|
||||
allDomainNames.forEach(domain -> {
|
||||
eventSupport.runClientHandlers(
|
||||
domain, event, EventDetails.fromProviderEventDetails(details, providerName, domain));
|
||||
});
|
||||
allDomainNames.forEach(domain -> eventSupport.runClientHandlers(
|
||||
domain, event, EventDetails.fromProviderEventDetails(details, providerName, domain)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,8 @@ import dev.openfeature.sdk.exceptions.FatalError;
|
|||
import dev.openfeature.sdk.exceptions.GeneralError;
|
||||
import dev.openfeature.sdk.exceptions.OpenFeatureError;
|
||||
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
|
||||
import dev.openfeature.sdk.internal.AutoCloseableLock;
|
||||
import dev.openfeature.sdk.internal.AutoCloseableReentrantReadWriteLock;
|
||||
import dev.openfeature.sdk.internal.ObjectUtils;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -15,6 +14,8 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -46,11 +47,9 @@ public class OpenFeatureClient implements Client {
|
|||
@Getter
|
||||
private final String version;
|
||||
|
||||
private final List<Hook> clientHooks;
|
||||
private final ConcurrentLinkedQueue<Hook> clientHooks;
|
||||
private final HookSupport hookSupport;
|
||||
AutoCloseableReentrantReadWriteLock hooksLock = new AutoCloseableReentrantReadWriteLock();
|
||||
AutoCloseableReentrantReadWriteLock contextLock = new AutoCloseableReentrantReadWriteLock();
|
||||
private EvaluationContext evaluationContext;
|
||||
private final AtomicReference<EvaluationContext> evaluationContext = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Deprecated public constructor. Use OpenFeature.API.getClient() instead.
|
||||
|
@ -68,7 +67,7 @@ public class OpenFeatureClient implements Client {
|
|||
this.openfeatureApi = openFeatureAPI;
|
||||
this.domain = domain;
|
||||
this.version = version;
|
||||
this.clientHooks = new ArrayList<>();
|
||||
this.clientHooks = new ConcurrentLinkedQueue<>();
|
||||
this.hookSupport = new HookSupport();
|
||||
}
|
||||
|
||||
|
@ -125,9 +124,7 @@ public class OpenFeatureClient implements Client {
|
|||
*/
|
||||
@Override
|
||||
public OpenFeatureClient addHooks(Hook... hooks) {
|
||||
try (AutoCloseableLock __ = this.hooksLock.writeLockAutoCloseable()) {
|
||||
this.clientHooks.addAll(Arrays.asList(hooks));
|
||||
}
|
||||
this.clientHooks.addAll(Arrays.asList(hooks));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -136,9 +133,7 @@ public class OpenFeatureClient implements Client {
|
|||
*/
|
||||
@Override
|
||||
public List<Hook> getHooks() {
|
||||
try (AutoCloseableLock __ = this.hooksLock.readLockAutoCloseable()) {
|
||||
return this.clientHooks;
|
||||
}
|
||||
return new ArrayList<>(this.clientHooks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,9 +141,7 @@ public class OpenFeatureClient implements Client {
|
|||
*/
|
||||
@Override
|
||||
public OpenFeatureClient setEvaluationContext(EvaluationContext evaluationContext) {
|
||||
try (AutoCloseableLock __ = contextLock.writeLockAutoCloseable()) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
}
|
||||
this.evaluationContext.set(evaluationContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -157,32 +150,33 @@ public class OpenFeatureClient implements Client {
|
|||
*/
|
||||
@Override
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
try (AutoCloseableLock __ = contextLock.readLockAutoCloseable()) {
|
||||
return this.evaluationContext;
|
||||
}
|
||||
return this.evaluationContext.get();
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(
|
||||
value = {"REC_CATCH_EXCEPTION"},
|
||||
justification = "We don't want to allow any exception to reach the user. "
|
||||
+ "Instead, we return an evaluation result with the appropriate error code.")
|
||||
private <T> FlagEvaluationDetails<T> evaluateFlag(
|
||||
FlagValueType type, String key, T defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) {
|
||||
FlagEvaluationOptions flagOptions = ObjectUtils.defaultIfNull(
|
||||
var flagOptions = ObjectUtils.defaultIfNull(
|
||||
options, () -> FlagEvaluationOptions.builder().build());
|
||||
Map<String, Object> hints = Collections.unmodifiableMap(flagOptions.getHookHints());
|
||||
var hints = Collections.unmodifiableMap(flagOptions.getHookHints());
|
||||
|
||||
FlagEvaluationDetails<T> details = null;
|
||||
List<Hook> mergedHooks = null;
|
||||
HookContext<T> afterHookContext = null;
|
||||
FeatureProvider provider;
|
||||
|
||||
try {
|
||||
FeatureProviderStateManager stateManager = openfeatureApi.getFeatureProviderStateManager(this.domain);
|
||||
var stateManager = openfeatureApi.getFeatureProviderStateManager(this.domain);
|
||||
// provider must be accessed once to maintain a consistent reference
|
||||
provider = stateManager.getProvider();
|
||||
ProviderState state = stateManager.getState();
|
||||
var provider = stateManager.getProvider();
|
||||
var state = stateManager.getState();
|
||||
|
||||
mergedHooks = ObjectUtils.merge(
|
||||
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getHooks());
|
||||
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks());
|
||||
|
||||
EvaluationContext mergedCtx = hookSupport.beforeHooks(
|
||||
var mergedCtx = hookSupport.beforeHooks(
|
||||
type,
|
||||
HookContext.from(
|
||||
key,
|
||||
|
@ -205,12 +199,12 @@ public class OpenFeatureClient implements Client {
|
|||
throw new FatalError("Provider is in an irrecoverable error state");
|
||||
}
|
||||
|
||||
ProviderEvaluation<T> providerEval =
|
||||
var providerEval =
|
||||
(ProviderEvaluation<T>) createProviderEvaluation(type, key, defaultValue, provider, mergedCtx);
|
||||
|
||||
details = FlagEvaluationDetails.from(providerEval, key);
|
||||
if (details.getErrorCode() != null) {
|
||||
OpenFeatureError error =
|
||||
var error =
|
||||
ExceptionUtils.instantiateErrorByErrorCode(details.getErrorCode(), details.getErrorMessage());
|
||||
enrichDetailsWithErrorDefaults(defaultValue, details);
|
||||
hookSupport.errorHooks(type, afterHookContext, error, mergedHooks, hints);
|
||||
|
@ -264,7 +258,7 @@ public class OpenFeatureClient implements Client {
|
|||
*/
|
||||
private EvaluationContext mergeEvaluationContext(EvaluationContext invocationContext) {
|
||||
final EvaluationContext apiContext = openfeatureApi.getEvaluationContext();
|
||||
final EvaluationContext clientContext = this.getEvaluationContext();
|
||||
final EvaluationContext clientContext = evaluationContext.get();
|
||||
final EvaluationContext transactionContext = openfeatureApi.getTransactionContext();
|
||||
return mergeContextMaps(apiContext, transactionContext, clientContext, invocationContext);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package dev.openfeature.sdk.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -64,9 +65,9 @@ public class ObjectUtils {
|
|||
* @return resulting object
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> List<T> merge(List<T>... sources) {
|
||||
public static <T> List<T> merge(Collection<T>... sources) {
|
||||
List<T> merged = new ArrayList<>();
|
||||
for (List<T> source : sources) {
|
||||
for (Collection<T> source : sources) {
|
||||
merged.addAll(source);
|
||||
}
|
||||
return merged;
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
package dev.openfeature.sdk;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
||||
@Timeout(value = 5, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
|
||||
class AwaitableTest {
|
||||
@Test
|
||||
void waitingForFinishedIsANoOp() {
|
||||
var startTime = System.currentTimeMillis();
|
||||
Awaitable.FINISHED.await();
|
||||
var endTime = System.currentTimeMillis();
|
||||
assertTrue(endTime - startTime < 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void waitingForNotFinishedWaitsEvenWhenInterrupted() throws InterruptedException {
|
||||
var awaitable = new Awaitable();
|
||||
var mayProceed = new AtomicBoolean(false);
|
||||
|
||||
var thread = new Thread(() -> {
|
||||
awaitable.await();
|
||||
if (!mayProceed.get()) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
|
||||
var startTime = System.currentTimeMillis();
|
||||
do {
|
||||
thread.interrupt();
|
||||
} while (startTime + 1000 > System.currentTimeMillis());
|
||||
mayProceed.set(true);
|
||||
awaitable.wakeup();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
void callingWakeUpWakesUpAllWaitingThreads() throws InterruptedException {
|
||||
var awaitable = new Awaitable();
|
||||
var isRunning = new AtomicInteger();
|
||||
|
||||
Runnable runnable = () -> {
|
||||
isRunning.incrementAndGet();
|
||||
var start = System.currentTimeMillis();
|
||||
awaitable.await();
|
||||
var end = System.currentTimeMillis();
|
||||
if (end - start > 10) {
|
||||
fail();
|
||||
}
|
||||
};
|
||||
|
||||
var numThreads = 2;
|
||||
var threads = new Thread[numThreads];
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
threads[i] = new Thread(runnable);
|
||||
threads[i].start();
|
||||
}
|
||||
|
||||
await().atMost(1, TimeUnit.SECONDS).until(() -> isRunning.get() == numThreads);
|
||||
|
||||
awaitable.wakeup();
|
||||
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -150,7 +150,7 @@ class DeveloperExperienceTest implements HookFixtures {
|
|||
api.setProviderAndWait(domain, provider);
|
||||
Client client = api.getClient(domain);
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
|
||||
provider.emitProviderError(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderError(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.ERROR);
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ class DeveloperExperienceTest implements HookFixtures {
|
|||
api.setProviderAndWait(domain, provider);
|
||||
Client client = api.getClient(domain);
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.STALE);
|
||||
}
|
||||
|
||||
|
@ -180,9 +180,9 @@ class DeveloperExperienceTest implements HookFixtures {
|
|||
api.setProviderAndWait(domain, provider);
|
||||
Client client = api.getClient(domain);
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.STALE);
|
||||
provider.emitProviderReady(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderReady(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.READY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ class EventProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 2, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
|
||||
@DisplayName("should run attached onEmit with emitters")
|
||||
void emitsEventsWhenAttached() {
|
||||
TriConsumer<EventProvider, ProviderEvent, ProviderEventDetails> onEmit = mockOnEmit();
|
||||
|
|
|
@ -24,7 +24,7 @@ class EventsTest {
|
|||
private OpenFeatureAPI api;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
void setUp() {
|
||||
api = new OpenFeatureAPI();
|
||||
}
|
||||
|
||||
|
@ -578,7 +578,7 @@ class EventsTest {
|
|||
number = "5.3.3",
|
||||
text = "Handlers attached after the provider is already in the associated state, MUST run immediately.")
|
||||
void matchingReadyEventsMustRunImmediately() {
|
||||
final String name = "matchingEventsMustRunImmediately";
|
||||
final String name = "matchingReadyEventsMustRunImmediately";
|
||||
final Consumer<EventDetails> handler = mockHandler();
|
||||
|
||||
// provider which is already ready
|
||||
|
@ -597,14 +597,14 @@ class EventsTest {
|
|||
number = "5.3.3",
|
||||
text = "Handlers attached after the provider is already in the associated state, MUST run immediately.")
|
||||
void matchingStaleEventsMustRunImmediately() {
|
||||
final String name = "matchingEventsMustRunImmediately";
|
||||
final String name = "matchingStaleEventsMustRunImmediately";
|
||||
final Consumer<EventDetails> handler = mockHandler();
|
||||
|
||||
// provider which is already stale
|
||||
TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
|
||||
Client client = api.getClient(name);
|
||||
api.setProviderAndWait(name, provider);
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderStale(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.STALE);
|
||||
|
||||
// should run even though handler was added after stale
|
||||
|
@ -618,14 +618,14 @@ class EventsTest {
|
|||
number = "5.3.3",
|
||||
text = "Handlers attached after the provider is already in the associated state, MUST run immediately.")
|
||||
void matchingErrorEventsMustRunImmediately() {
|
||||
final String name = "matchingEventsMustRunImmediately";
|
||||
final String name = "matchingErrorEventsMustRunImmediately";
|
||||
final Consumer<EventDetails> handler = mockHandler();
|
||||
|
||||
// provider which is already in error
|
||||
TestEventsProvider provider = new TestEventsProvider(INIT_DELAY);
|
||||
Client client = api.getClient(name);
|
||||
api.setProviderAndWait(name, provider);
|
||||
provider.emitProviderError(ProviderEventDetails.builder().build());
|
||||
provider.emitProviderError(ProviderEventDetails.builder().build()).await();
|
||||
assertThat(client.getProviderState()).isEqualTo(ProviderState.ERROR);
|
||||
|
||||
verify(handler, never()).accept(any());
|
||||
|
|
|
@ -20,7 +20,6 @@ class LockingSingeltonTest {
|
|||
private static OpenFeatureAPI api;
|
||||
private OpenFeatureClient client;
|
||||
private AutoCloseableReentrantReadWriteLock apiLock;
|
||||
private AutoCloseableReentrantReadWriteLock clientContextLock;
|
||||
private AutoCloseableReentrantReadWriteLock clientHooksLock;
|
||||
|
||||
@BeforeAll
|
||||
|
@ -36,10 +35,7 @@ class LockingSingeltonTest {
|
|||
apiLock = setupLock(apiLock, mockInnerReadLock(), mockInnerWriteLock());
|
||||
OpenFeatureAPI.lock = apiLock;
|
||||
|
||||
clientContextLock = setupLock(clientContextLock, mockInnerReadLock(), mockInnerWriteLock());
|
||||
clientHooksLock = setupLock(clientHooksLock, mockInnerReadLock(), mockInnerWriteLock());
|
||||
client.contextLock = clientContextLock;
|
||||
client.hooksLock = clientHooksLock;
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
@ -137,50 +133,6 @@ class LockingSingeltonTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void addHooksShouldWriteLockAndUnlock() {
|
||||
client.addHooks(new Hook() {});
|
||||
verify(clientHooksLock.writeLock()).lock();
|
||||
verify(clientHooksLock.writeLock()).unlock();
|
||||
|
||||
api.addHooks(new Hook() {});
|
||||
verify(apiLock.writeLock()).lock();
|
||||
verify(apiLock.writeLock()).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHooksShouldReadLockAndUnlock() {
|
||||
client.getHooks();
|
||||
verify(clientHooksLock.readLock()).lock();
|
||||
verify(clientHooksLock.readLock()).unlock();
|
||||
|
||||
api.getHooks();
|
||||
verify(apiLock.readLock()).lock();
|
||||
verify(apiLock.readLock()).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void setContextShouldWriteLockAndUnlock() {
|
||||
client.setEvaluationContext(new ImmutableContext());
|
||||
verify(clientContextLock.writeLock()).lock();
|
||||
verify(clientContextLock.writeLock()).unlock();
|
||||
|
||||
api.setEvaluationContext(new ImmutableContext());
|
||||
verify(apiLock.writeLock()).lock();
|
||||
verify(apiLock.writeLock()).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getContextShouldReadLockAndUnlock() {
|
||||
client.getEvaluationContext();
|
||||
verify(clientContextLock.readLock()).lock();
|
||||
verify(clientContextLock.readLock()).unlock();
|
||||
|
||||
api.getEvaluationContext();
|
||||
verify(apiLock.readLock()).lock();
|
||||
verify(apiLock.readLock()).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void setTransactionalContextPropagatorShouldWriteLockAndUnlock() {
|
||||
api.setTransactionContextPropagator(new NoOpTransactionContextPropagator());
|
||||
|
@ -195,13 +147,6 @@ class LockingSingeltonTest {
|
|||
verify(apiLock.readLock()).unlock();
|
||||
}
|
||||
|
||||
@Test
|
||||
void clearHooksShouldWriteLockAndUnlock() {
|
||||
api.clearHooks();
|
||||
verify(apiLock.writeLock()).lock();
|
||||
verify(apiLock.writeLock()).unlock();
|
||||
}
|
||||
|
||||
private static ReentrantReadWriteLock.ReadLock mockInnerReadLock() {
|
||||
ReentrantReadWriteLock.ReadLock readLockMock = mock(ReentrantReadWriteLock.ReadLock.class);
|
||||
doNothing().when(readLockMock).lock();
|
||||
|
|
|
@ -3,9 +3,14 @@ package dev.openfeature.sdk.providers.memory;
|
|||
import static dev.openfeature.sdk.Structure.mapToStructure;
|
||||
import static dev.openfeature.sdk.testutils.TestFlagsUtils.buildFlags;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import dev.openfeature.sdk.Client;
|
||||
|
@ -19,6 +24,7 @@ import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
|
|||
import dev.openfeature.sdk.exceptions.TypeMismatchError;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -34,10 +40,11 @@ class InMemoryProviderTest {
|
|||
@SneakyThrows
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
final var configChangedEventCounter = new AtomicInteger();
|
||||
Map<String, Flag<?>> flags = buildFlags();
|
||||
provider = spy(new InMemoryProvider(flags));
|
||||
api = OpenFeatureAPITestUtil.createAPI();
|
||||
api.onProviderConfigurationChanged(eventDetails -> {});
|
||||
api.onProviderConfigurationChanged(eventDetails -> configChangedEventCounter.incrementAndGet());
|
||||
api.setProviderAndWait(provider);
|
||||
client = api.getClient();
|
||||
provider.updateFlags(flags);
|
||||
|
@ -48,6 +55,11 @@ class InMemoryProviderTest {
|
|||
.variant("off", false)
|
||||
.defaultVariant("on")
|
||||
.build());
|
||||
|
||||
// wait for the two config changed events to be fired, otherwise they could mess with our tests
|
||||
while (configChangedEventCounter.get() < 2) {
|
||||
Thread.sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.15.1
|
||||
1.16.0
|
||||
|
|
Loading…
Reference in New Issue