From bfa6025d299a87110afef2951be4e773eb3b7e57 Mon Sep 17 00:00:00 2001 From: scottf Date: Fri, 22 Aug 2025 13:31:29 -0400 Subject: [PATCH] Initial Counter implementation --- .github/workflows/counter-main.yml | 47 ++++ .github/workflows/counter-pr.yml | 41 ++++ .github/workflows/counter-release.yml | 39 ++++ .github/workflows/cr-main.yml | 4 - .github/workflows/cr-pr.yml | 4 - .github/workflows/cr-release.yml | 4 - .github/workflows/db-main.yml | 4 - .github/workflows/db-pr.yml | 4 - .github/workflows/db-release.yml | 4 - .github/workflows/ekv-main.yml | 4 - .github/workflows/ekv-pr.yml | 4 - .github/workflows/ekv-release.yml | 4 - .github/workflows/pubx-main.yml | 4 - .github/workflows/pubx-pr.yml | 4 - .github/workflows/pubx-release.yml | 4 - .github/workflows/retrier-main.yml | 4 - .github/workflows/retrier-pr.yml | 4 - .github/workflows/retrier-release.yml | 4 - .github/workflows/rm-main.yml | 4 - .github/workflows/rm-pr.yml | 4 - .github/workflows/rm-release.yml | 4 - chaos-runner/build.gradle | 2 +- counter/.gitignore | 77 +++++++ counter/LICENSE | 201 +++++++++++++++++ counter/NOTICE | 5 + counter/README.md | 20 ++ counter/build.gradle | 207 ++++++++++++++++++ counter/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 + counter/gradlew | 183 ++++++++++++++++ counter/gradlew.bat | 103 +++++++++ counter/settings.gradle | 10 + .../examples/CounterContextExample.java | 114 ++++++++++ .../io/synadia/counter/CounterContext.java | 136 ++++++++++++ .../java/io/synadia/counter/CounterEntry.java | 112 ++++++++++ .../io/synadia/counter/CounterIterator.java | 87 ++++++++ .../java/io/synadia/counter/CounterUtils.java | 25 +++ counter/src/main/javadoc/images/favicon.ico | Bin 0 -> 1150 bytes .../src/main/javadoc/images/large-logo.png | Bin 0 -> 6533 bytes .../src/main/javadoc/images/synadia-logo.png | Bin 0 -> 19014 bytes counter/src/main/javadoc/overview.html | 13 ++ counter/src/main/resources/placeholder.txt | 1 + counter/src/test/resources/placeholder.txt | 1 + counter/test.bat | 5 + direct-batch/build.gradle | 2 +- .../io/synadia/examples/ExampleUtils.java | 8 +- .../io/synadia/direct/DirectBatchContext.java | 2 +- js-publish-extensions/build.gradle | 2 +- request-many/build.gradle | 2 +- retrier/build.gradle | 2 +- 50 files changed, 1443 insertions(+), 81 deletions(-) create mode 100644 .github/workflows/counter-main.yml create mode 100644 .github/workflows/counter-pr.yml create mode 100644 .github/workflows/counter-release.yml create mode 100644 counter/.gitignore create mode 100644 counter/LICENSE create mode 100644 counter/NOTICE create mode 100644 counter/README.md create mode 100644 counter/build.gradle create mode 100644 counter/gradle/wrapper/gradle-wrapper.jar create mode 100644 counter/gradle/wrapper/gradle-wrapper.properties create mode 100644 counter/gradlew create mode 100644 counter/gradlew.bat create mode 100644 counter/settings.gradle create mode 100644 counter/src/examples/java/io/synadia/examples/CounterContextExample.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterContext.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterEntry.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterIterator.java create mode 100644 counter/src/main/java/io/synadia/counter/CounterUtils.java create mode 100644 counter/src/main/javadoc/images/favicon.ico create mode 100644 counter/src/main/javadoc/images/large-logo.png create mode 100644 counter/src/main/javadoc/images/synadia-logo.png create mode 100644 counter/src/main/javadoc/overview.html create mode 100644 counter/src/main/resources/placeholder.txt create mode 100644 counter/src/test/resources/placeholder.txt create mode 100644 counter/test.bat diff --git a/.github/workflows/counter-main.yml b/.github/workflows/counter-main.yml new file mode 100644 index 0000000..02a7f0d --- /dev/null +++ b/.github/workflows/counter-main.yml @@ -0,0 +1,47 @@ +name: Counter Main Snapshot + +on: + push: + branches: + - main + paths: + - 'counter/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd counter + ./gradlew javadoc + popd + - name: Publish Snapshot + run: | + pushd counter + ./gradlew -i publishToSonatype + popd diff --git a/.github/workflows/counter-pr.yml b/.github/workflows/counter-pr.yml new file mode 100644 index 0000000..bc2c9f1 --- /dev/null +++ b/.github/workflows/counter-pr.yml @@ -0,0 +1,41 @@ +name: Counter Pull Request + +on: + pull_request: + types: [opened, synchronize, reopened] + paths: + - 'counter/**' + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: ${{ github.event_name }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify Javadoc + run: | + pushd counter + ./gradlew javadoc + popd diff --git a/.github/workflows/counter-release.yml b/.github/workflows/counter-release.yml new file mode 100644 index 0000000..912c303 --- /dev/null +++ b/.github/workflows/counter-release.yml @@ -0,0 +1,39 @@ +name: Counter Release + +on: + push: + tags: [ 'counter/*' ] + +jobs: + build: + runs-on: ubuntu-latest + env: + BUILD_EVENT: "release" + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + steps: + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + java-version: '8' + distribution: 'adopt' + - name: Install Nats Server + run: | + curl -sf https://binaries.nats.dev/nats-io/nats-server/v2@main | PREFIX=. sh + sudo mv nats-server /usr/local/bin + nats-server -v + - name: Check out code + uses: actions/checkout@v3 + - name: Compile and Test + run: | + pushd counter + chmod +x gradlew && ./gradlew clean test + popd + - name: Verify, Sign and Publish Release + run: | + pushd counter + ./gradlew -i signArchives signMavenJavaPublication publishToSonatype closeAndReleaseSonatypeStagingRepository + popd diff --git a/.github/workflows/cr-main.yml b/.github/workflows/cr-main.yml index a162f89..babbd7f 100644 --- a/.github/workflows/cr-main.yml +++ b/.github/workflows/cr-main.yml @@ -7,9 +7,6 @@ on: paths: - 'chaos-runner/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/cr-pr.yml b/.github/workflows/cr-pr.yml index 337c884..9d59f24 100644 --- a/.github/workflows/cr-pr.yml +++ b/.github/workflows/cr-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'chaos-runner/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/cr-release.yml b/.github/workflows/cr-release.yml index 47f7cdd..a01b18a 100644 --- a/.github/workflows/cr-release.yml +++ b/.github/workflows/cr-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'cr/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-main.yml b/.github/workflows/db-main.yml index 690cbd0..8ba10a6 100644 --- a/.github/workflows/db-main.yml +++ b/.github/workflows/db-main.yml @@ -7,9 +7,6 @@ on: paths: - 'direct-batch/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-pr.yml b/.github/workflows/db-pr.yml index 19c54c8..92f1b62 100644 --- a/.github/workflows/db-pr.yml +++ b/.github/workflows/db-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'direct-batch/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/db-release.yml b/.github/workflows/db-release.yml index 0d2fb92..b1db120 100644 --- a/.github/workflows/db-release.yml +++ b/.github/workflows/db-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'db/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-main.yml b/.github/workflows/ekv-main.yml index 26b6c59..30c4c98 100644 --- a/.github/workflows/ekv-main.yml +++ b/.github/workflows/ekv-main.yml @@ -7,9 +7,6 @@ on: paths: - 'encoded-kv/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-pr.yml b/.github/workflows/ekv-pr.yml index 15e0e9b..9b87465 100644 --- a/.github/workflows/ekv-pr.yml +++ b/.github/workflows/ekv-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'encoded-kv/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/ekv-release.yml b/.github/workflows/ekv-release.yml index 699bee7..2cc62dc 100644 --- a/.github/workflows/ekv-release.yml +++ b/.github/workflows/ekv-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'ekv/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-main.yml b/.github/workflows/pubx-main.yml index 4de4f09..dfe93d7 100644 --- a/.github/workflows/pubx-main.yml +++ b/.github/workflows/pubx-main.yml @@ -7,9 +7,6 @@ on: paths: - 'js-publish-extensions/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-pr.yml b/.github/workflows/pubx-pr.yml index d04ccf0..b9c9a82 100644 --- a/.github/workflows/pubx-pr.yml +++ b/.github/workflows/pubx-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'js-publish-extensions/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/pubx-release.yml b/.github/workflows/pubx-release.yml index 8f5c08e..efdf855 100644 --- a/.github/workflows/pubx-release.yml +++ b/.github/workflows/pubx-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'pubx/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-main.yml b/.github/workflows/retrier-main.yml index ef3df39..7d51520 100644 --- a/.github/workflows/retrier-main.yml +++ b/.github/workflows/retrier-main.yml @@ -7,9 +7,6 @@ on: paths: - 'retrier/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-pr.yml b/.github/workflows/retrier-pr.yml index 79b891f..31beed7 100644 --- a/.github/workflows/retrier-pr.yml +++ b/.github/workflows/retrier-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'retrier/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/retrier-release.yml b/.github/workflows/retrier-release.yml index 42a54ee..853e04c 100644 --- a/.github/workflows/retrier-release.yml +++ b/.github/workflows/retrier-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'retrier/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-main.yml b/.github/workflows/rm-main.yml index 7d1b0b8..c9e62ea 100644 --- a/.github/workflows/rm-main.yml +++ b/.github/workflows/rm-main.yml @@ -7,9 +7,6 @@ on: paths: - 'request-many/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -20,7 +17,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-pr.yml b/.github/workflows/rm-pr.yml index 8527a70..5938636 100644 --- a/.github/workflows/rm-pr.yml +++ b/.github/workflows/rm-pr.yml @@ -6,9 +6,6 @@ on: paths: - 'request-many/**' -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -19,7 +16,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/.github/workflows/rm-release.yml b/.github/workflows/rm-release.yml index e6ab31c..d186d36 100644 --- a/.github/workflows/rm-release.yml +++ b/.github/workflows/rm-release.yml @@ -4,9 +4,6 @@ on: push: tags: [ 'rm/*' ] -env: - GODEBUG: x509sha1=1 - jobs: build: runs-on: ubuntu-latest @@ -17,7 +14,6 @@ jobs: SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} SIGNING_KEY: ${{ secrets.SIGNING_KEY }} SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - GODEBUG: x509sha1=1 steps: - name: Set up JDK 8 uses: actions/setup-java@v3 diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 9a93d45..a36b7a1 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -40,7 +40,7 @@ repositories { dependencies { implementation 'io.nats:jnats-server-runner:2.0.2' - implementation 'io.nats:jnats:2.21.4' // this is only for the example and the uber jar won't include it + implementation 'io.nats:jnats:2.21.5' // this is only for the example and the uber jar won't include it testImplementation 'commons-codec:commons-codec:1.18.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000..b3e2ca5 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,77 @@ + +# NATS stuff # +############## +gnatsd.log +*.csv + +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so +/bin + +# Packages # +############ +*.7z +*.dmg +*.gz +*.iso +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log + +# OS generated files # +###################### +.DS_Store* +ehthumbs.db +Icon? +Thumbs.db + +# Editor Files # +################ +*~ +*.swp +.sts4-cache/* + +# Gradle Files # +################ +.gradle +.m2 + +# Build output directies +/target +*/target +/build +*/build + +# IntelliJ specific files/directories +out +.idea +*.ipr +*.iws +*.iml +atlassian-ide-plugin.xml + +# Eclipse specific files/directories +.classpath +.project +.settings +.metadata + +# NetBeans specific files/directories +.nbattrs + +# VSCode +.vscode/ + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ diff --git a/counter/LICENSE b/counter/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/counter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/counter/NOTICE b/counter/NOTICE new file mode 100644 index 0000000..ff3c8b4 --- /dev/null +++ b/counter/NOTICE @@ -0,0 +1,5 @@ +Orbit Java +Copyright (c) 2024-2025 Synadia Communications Inc. All Rights Reserved. + +This product includes software developed at +Synadia Communications Inc. \ No newline at end of file diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000..fb6ac8b --- /dev/null +++ b/counter/README.md @@ -0,0 +1,20 @@ +![Synadia](src/main/javadoc/images/synadia-logo.png)      ![NATS](src/main/javadoc/images/large-logo.png) + +# JetStream Distributed Counter CRDT + +https://github.com/nats-io/nats-architecture-and-design/blob/main/adr/ADR-49.md + + +**Current Release**: N/A +  **Current Snapshot**: 0.1.0-SNAPSHOT +  **Gradle and Maven** `io.synadia:counter` +[Dependencies Help](https://github.com/synadia-io/orbit.java?tab=readme-ov-file#dependencies) + +![Artifact](https://img.shields.io/badge/Artifact-io.synadia:counter-00BC8E?labelColor=grey&style=flat) +[![License Apache 2](https://img.shields.io/badge/License-Apache2-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.synadia/counter) +[![javadoc](https://javadoc.io/badge2/io.synadia/counter/javadoc.svg)](https://javadoc.io/doc/io.synadia/counter) + +--- +Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +See [LICENSE](LICENSE) and [NOTICE](NOTICE) file for details. diff --git a/counter/build.gradle b/counter/build.gradle new file mode 100644 index 0000000..8bbb6ff --- /dev/null +++ b/counter/build.gradle @@ -0,0 +1,207 @@ +import aQute.bnd.gradle.Bundle +import org.gradle.internal.os.OperatingSystem + +plugins { + id 'java' + id 'java-library' + id 'maven-publish' + id 'jacoco' + id 'com.github.kt3k.coveralls' version '2.12.0' + id 'biz.aQute.bnd.builder' version '5.1.2' + id "org.gradle.test-retry" version "1.1.9" + id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' + id 'signing' +} + +def jarVersion = "0.0.1" +group = 'io.synadia' + +def isMerge = System.getenv("BUILD_EVENT") == "push" +def isRelease = System.getenv("BUILD_EVENT") == "release" + +// version is the variable the build actually uses. +version = isRelease ? jarVersion : jarVersion + "-SNAPSHOT" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +repositories { + mavenCentral() + maven { + url "https://repo1.maven.org/maven2/" + } + maven { + url "https://central.sonatype.com/repository/maven-snapshots/" + } +} + +dependencies { + implementation 'io.nats:jnats:2.22.0.2_12-SNAPSHOT' + implementation 'io.synadia:direct-batch:0.1.2' + implementation 'org.jspecify:jspecify:1.0.0' + + testImplementation 'commons-codec:commons-codec:1.18.0' + testImplementation 'io.nats:jnats-server-runner:1.2.8' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' + testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' + testImplementation 'nl.jqno.equalsverifier:equalsverifier:3.12.3' +} + +sourceSets { + main { + java { + srcDirs = ['src/main/java','src/examples/java'] + } + } + test { + java { + srcDirs = ['src/test/java'] + } + } +} + +tasks.register('bundle', Bundle) { + from sourceSets.main.output + exclude("io/synadia/examples/**") +} + +jar { + manifest { + attributes('Automatic-Module-Name': 'io.synadia.counter') + } + bnd (['Implementation-Title': 'JetStream Distributed Counter', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io'] + ) + exclude("io/synadia/examples/**") +} + +test { + // Use junit platform for unit tests + useJUnitPlatform() +} + +javadoc { + options.overview = 'src/main/javadoc/overview.html' // relative to source root + source = sourceSets.main.allJava + title = "Synadia Communications Inc. JetStream Distributed Counter" + classpath = sourceSets.main.runtimeClasspath + doLast { + if (!OperatingSystem.current().isWindows()) { + exec { + println "Updating favicon on all html files" + workingDir 'build/docs/javadoc' + // Only on linux, mac at this point + commandLine 'find', '.', '-name', '*.html', '-exec', 'sed', '-i', '-e', 's###', '{}', ';' + } + copy { + println "Copying images to javadoc folder" + from 'src/main/javadoc/images' + into 'build/docs/javadoc' + } + } + } +} + +tasks.register('examplesJar', Jar) { + archiveClassifier.set('examples') + manifest { + attributes('Implementation-Title': 'JetStream Distributed Counters', + 'Implementation-Version': jarVersion, + 'Implementation-Vendor': 'synadia.io') + } + from(sourceSets.main.output) { + include "io/synadia/examples/**" + } +} + +tasks.register('javadocJar', Jar) { + archiveClassifier.set('javadoc') + from javadoc +} + +tasks.register('sourcesJar', Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource +} + +jacoco { + toolVersion = "0.8.6" +} + +jacocoTestReport { + reports { + xml.enabled = true // coveralls plugin depends on xml format report + html.enabled = true + } + afterEvaluate { // only report on main library not examples + classDirectories.setFrom(files(classDirectories.files.collect { + fileTree(dir: it, + exclude: ['**/examples**']) + })) + } +} + +artifacts { + archives javadocJar, sourcesJar, examplesJar +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = System.getenv('OSSRH_USERNAME') + password = System.getenv('OSSRH_PASSWORD') + } + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourcesJar + artifact examplesJar + artifact javadocJar + pom { + name = 'counter' + packaging = 'jar' + groupId = group + artifactId = archivesBaseName + description = 'Synadia Communications Inc. JetStream Distributed Counters' + url = 'https://github.com/synadia-io/orbit.java' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + developers { + developer { + id = "synadia" + name = "Synadia" + email = "info@synadia.com" + url = "https://synadia.io" + } + } + scm { + url = 'https://github.com/synadia-io/orbit.java' + } + } + } + } +} + +if (isRelease) { + signing { + def signingKeyId = System.getenv('SIGNING_KEY_ID') + def signingKey = System.getenv('SIGNING_KEY') + def signingPassword = System.getenv('SIGNING_PASSWORD') + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign configurations.archives + sign publishing.publications.mavenJava + } +} \ No newline at end of file diff --git a/counter/gradle/wrapper/gradle-wrapper.jar b/counter/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/counter/gradle/wrapper/gradle-wrapper.properties b/counter/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..442d913 --- /dev/null +++ b/counter/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/counter/gradlew b/counter/gradlew new file mode 100644 index 0000000..2fe81a7 --- /dev/null +++ b/counter/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/counter/gradlew.bat b/counter/gradlew.bat new file mode 100644 index 0000000..62bd9b9 --- /dev/null +++ b/counter/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/counter/settings.gradle b/counter/settings.gradle new file mode 100644 index 0000000..59f0439 --- /dev/null +++ b/counter/settings.gradle @@ -0,0 +1,10 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/6.3/userguide/multi_project_builds.html + */ + +rootProject.name = 'counter' diff --git a/counter/src/examples/java/io/synadia/examples/CounterContextExample.java b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java new file mode 100644 index 0000000..468ecad --- /dev/null +++ b/counter/src/examples/java/io/synadia/examples/CounterContextExample.java @@ -0,0 +1,114 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.examples; + +import io.nats.client.Connection; +import io.nats.client.JetStreamApiException; +import io.nats.client.JetStreamManagement; +import io.nats.client.Nats; +import io.nats.client.api.StreamConfiguration; +import io.synadia.counter.CounterContext; +import io.synadia.counter.CounterEntry; + +import java.math.BigInteger; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class CounterContextExample { + + private static final String STREAM_NAME = "counter-stream"; + private static final String STREAM_SUBJECT = "cs.*"; + private static final String SUBJECT_A = "cs.A"; + private static final String SUBJECT_B = "cs.B"; + private static final String SUBJECT_C = "cs.C"; + + public static void main(String[] args) throws Exception { + try (Connection nc = Nats.connect()) { + JetStreamManagement jsm = nc.jetStreamManagement(); + + // Set up a fresh counter stream + try { jsm.deleteStream(STREAM_NAME); } catch (JetStreamApiException ignore) {} + CounterContext counter = CounterContext.createCounterStream(nc, + StreamConfiguration.builder() + .name(STREAM_NAME) + .subjects(STREAM_SUBJECT) + .build()); + + System.out.println("1: Add to a subject..."); + System.out.println("add(\"" + SUBJECT_A + "\", 1) -> " + counter.add(SUBJECT_A, 1)); + System.out.println("add(\"" + SUBJECT_A + "\", 2) -> " + counter.add(SUBJECT_A, 2)); + System.out.println("add(\"" + SUBJECT_A + "\", 3) -> " + counter.add(SUBJECT_A, 3)); + System.out.println("add(\"" + SUBJECT_A + "\", -1) -> " + counter.add(SUBJECT_A, -1)); + + System.out.println("add(\"" + SUBJECT_B + "\", 10) -> " + counter.add(SUBJECT_B, 10)); + System.out.println("add(\"" + SUBJECT_B + "\", 20) -> " + counter.add(SUBJECT_B, 20)); + System.out.println("add(\"" + SUBJECT_B + "\", 30) -> " + counter.add(SUBJECT_B, 30)); + System.out.println("add(\"" + SUBJECT_B + "\", -10) -> " + counter.add(SUBJECT_B, -10)); + + System.out.println("add(\"" + SUBJECT_C + "\", 100) -> " + counter.add(SUBJECT_C, 100)); + System.out.println("add(\"" + SUBJECT_C + "\", 200) -> " + counter.add(SUBJECT_C, 200)); + System.out.println("add(\"" + SUBJECT_C + "\", 300) -> " + counter.add(SUBJECT_C, 300)); + System.out.println("add(\"" + SUBJECT_C + "\", -100) -> " + counter.add(SUBJECT_C, -100)); + + System.out.println("\n2.1: Get the value for existing subjects"); + System.out.println("get(\"" + SUBJECT_A + "\") -> " + counter.get(SUBJECT_A)); + System.out.println("get(\"" + SUBJECT_B + "\") -> " + counter.get(SUBJECT_B)); + System.out.println("get(\"" + SUBJECT_C + "\") -> " + counter.get(SUBJECT_C)); + + System.out.println("\n2.2 Get the value if subject not found"); + try { + counter.get("cs.X"); + } + catch (JetStreamApiException e) { + System.out.println("get(\"X\") -> " + e); + } + + System.out.println("\n3: Get the full entry for a subject, notice the last increment..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n4: Get multiples entries - maybe to total them up"); + LinkedBlockingQueue q = counter.getEntries(SUBJECT_A, SUBJECT_B, SUBJECT_C); + BigInteger total = BigInteger.ZERO; + CounterEntry entry = q.poll(1, TimeUnit.SECONDS); + while (entry != null && entry.isEntry()) { + System.out.println("Entry: " + entry); + total = total.add(entry.value); + entry = q.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println("The last entry was: " + entry); + System.out.println("Entries Totaled: " + total); + + System.out.println("\n5.1: Set the value for a subject"); + System.out.println("set(\"" + SUBJECT_A + "\", 9) -> " + counter.set(SUBJECT_A, 9)); + System.out.println("set(\"" + SUBJECT_B + "\", 99) -> " + counter.set(SUBJECT_B, 99)); + System.out.println("set(\"" + SUBJECT_C + "\", 999) -> " + counter.set(SUBJECT_C, 999)); + + System.out.println("\n5.2: Get the full entry again, notice the last increment after a set..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n6.1: Zero is a shortcut to set the value for a subject to 0"); + System.out.println("zero(\"" + SUBJECT_A + "\") -> " + counter.zero(SUBJECT_A)); + System.out.println("zero(\"" + SUBJECT_B + "\") -> " + counter.zero(SUBJECT_B)); + System.out.println("zero(\"" + SUBJECT_C + "\") -> " + counter.zero(SUBJECT_C)); + + System.out.println("\n6.2: Get the full entry again, notice the last increment after a zero..."); + System.out.println("getEntry(\"" + SUBJECT_A + "\") -> " + counter.getEntry(SUBJECT_A)); + System.out.println("getEntry(\"" + SUBJECT_B + "\") -> " + counter.getEntry(SUBJECT_B)); + System.out.println("getEntry(\"" + SUBJECT_C + "\") -> " + counter.getEntry(SUBJECT_C)); + + System.out.println("\n7: Get multiples entries - but subject doesn't have any counters"); + q = counter.getEntries("not-no counters"); + entry = q.poll(1, TimeUnit.SECONDS); + while (entry != null && entry.isEntry()) { + System.out.println("Entry: " + entry); + entry = q.poll(10, TimeUnit.MILLISECONDS); + } + System.out.println("The last entry was: " + entry); + } + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterContext.java b/counter/src/main/java/io/synadia/counter/CounterContext.java new file mode 100644 index 0000000..87c9f06 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterContext.java @@ -0,0 +1,136 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.*; +import io.nats.client.api.*; +import io.nats.client.impl.Headers; +import io.synadia.direct.DirectBatchContext; +import io.synadia.direct.MessageBatchGetRequest; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; + +import static io.synadia.counter.CounterUtils.INCREMENT_HEADER; +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterContext { + + public static CounterContext createCounterStream(Connection conn, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + return createCounterStream(conn, null, userConfig); + } + + public static CounterContext createCounterStream(Connection conn, JetStreamOptions jso, StreamConfiguration userConfig) throws JetStreamApiException, IOException { + if (userConfig.getRetentionPolicy() != RetentionPolicy.Limits) { + throw new IllegalArgumentException("Retention Policy - Limits is the only allowed limit for counter streams."); + } + if (userConfig.getDiscardPolicy() == DiscardPolicy.New) { + throw new IllegalArgumentException("Discard Policy - New is not allowed for counter streams."); + } + StreamConfiguration config = StreamConfiguration.builder(userConfig) + .allowDirect(true) + .allowMessageCounter(true) + .build(); + + JetStreamManagement jsm = conn.jetStreamManagement(jso); + jsm.addStream(config); + + return new CounterContext(config.getName(), conn, jso, jsm); + } + + private final String streamName; + private final JetStreamManagement jsm; + private final JetStream js; + private final DirectBatchContext dbCtx; + + public CounterContext(String streamName, Connection conn) throws IOException, JetStreamApiException { + this(streamName, conn, null, null); + } + + public CounterContext(String streamName, Connection conn, JetStreamOptions jso) throws IOException, JetStreamApiException { + this(streamName, conn, jso, null); + } + + private CounterContext(@NonNull String streamName, @NonNull Connection conn, @Nullable JetStreamOptions jso, @Nullable JetStreamManagement jsm) throws IOException, JetStreamApiException { + this.streamName = streamName; + this.jsm = jsm == null ? conn.jetStreamManagement(jso) : jsm; + js = this.jsm.jetStream(); + dbCtx = new DirectBatchContext(conn, jso, streamName); + } + + private BigInteger _add(String subject, String sv) throws IOException, JetStreamApiException { + Headers h = new Headers(); + h.put(INCREMENT_HEADER, sv); + PublishAck pa = js.publish(subject, h, null); + String val = pa.getVal(); + if (val == null) { + throw new IOException("Publish Failed"); + } + return new BigInteger(val); + } + + public BigInteger add(String subject, int value) throws JetStreamApiException, IOException { + return _add(subject, Integer.toString(value)); + } + + public BigInteger add(String subject, long value) throws JetStreamApiException, IOException { + return _add(subject, Long.toString(value)); + } + + public BigInteger add(String subject, BigInteger value) throws JetStreamApiException, IOException { + return _add(subject, value.toString()); + } + + public BigInteger increment(String subject) throws JetStreamApiException, IOException { + return _add(subject, "1"); + } + + public BigInteger decrement(String subject) throws JetStreamApiException, IOException { + return _add(subject, "-1"); + } + + public BigInteger set(String subject, int value) throws JetStreamApiException, IOException { + return set(subject, BigInteger.valueOf(value)); + } + + public BigInteger set(String subject, long value) throws JetStreamApiException, IOException { + return set(subject, BigInteger.valueOf(value)); + } + + public BigInteger set(String subject, BigInteger value) throws JetStreamApiException, IOException { + // 100 -> 200 = add(200 - 100) | 100 -> 0 = add(0 - 100) | -100 -> 0 = add(0 - -100) | -100 -> 200 = add(200 - -100) + BigInteger bi = get(subject); + return _add(subject, value.subtract(bi).toString()); + } + + public BigInteger zero(String subject) throws JetStreamApiException, IOException { + return set(subject, BigInteger.ZERO); + } + + public BigInteger get(String subject) throws JetStreamApiException, IOException { + MessageInfo mi = jsm.getLastMessage(streamName, subject); + return new BigInteger(extractVal(mi.getData())); + } + + public CounterEntry getEntry(String subject) throws JetStreamApiException, IOException { + MessageInfo mi = jsm.getLastMessage(streamName, subject); + return new CounterEntry(mi); + } + + public LinkedBlockingQueue getEntries(String... subjects) { + return getEntries(Arrays.asList(subjects)); + } + + public LinkedBlockingQueue getEntries(List subjects) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + MessageBatchGetRequest mbgr = MessageBatchGetRequest.multiLastForSubjects(subjects); + dbCtx.requestMessageBatch(mbgr, mi -> queue.add(new CounterEntry(mi))); + return queue; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterEntry.java b/counter/src/main/java/io/synadia/counter/CounterEntry.java new file mode 100644 index 0000000..a547885 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterEntry.java @@ -0,0 +1,112 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.nats.client.support.Status; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import static io.synadia.counter.CounterUtils.extractIncrement; +import static io.synadia.counter.CounterUtils.extractVal; + +public class CounterEntry { + public final String subject; + public final BigInteger value; + public final BigInteger lastIncrement; + public final Map sources; + public final Status status; + + CounterEntry(MessageInfo mi) { + this.status = mi.getStatus(); + this.sources = new HashMap<>(); + if (mi.isMessage()) { + this.subject = mi.getSubject(); + this.value = new BigInteger(extractVal(mi.getData())); + this.lastIncrement = new BigInteger(extractIncrement(mi.getHeaders())); + } + else { + this.subject = ""; + this.value = BigInteger.ZERO; + this.lastIncrement = BigInteger.ZERO; + } + } + + /** + * Whether this CounterEntry is a regular entry + * @return true if the CounterEntry is a regular entry + */ + public boolean isEntry() { + return status == null; + } + + /** + * Whether this CounterEntry is a status message + * @return true if this CounterEntry is a status message + */ + public boolean isStatus() { + return status != null; + } + + /** + * Whether this CounterEntry is a status message and is a direct EOB status + * @return true if this CounterEntry is a status message and is a direct EOB status + */ + public boolean isEobStatus() { + return status != null && status.isEob(); + } + + /** + * Whether this CounterEntry is a status message and is an error status + * @return true if this CounterEntry is a status message and is an error status + */ + public boolean isErrorStatus() { + return status != null && !status.isEob(); + } + + @NonNull + public String getSubject() { + return subject; + } + + @NonNull + public BigInteger getValue() { + return value; + } + + @NonNull + public BigInteger getLastIncrement() { + return lastIncrement; + } + + @NonNull + public Map getSources() { + return sources; + } + + @Nullable + public Status getStatus() { + return status; + } + + @Override + public String toString() { + if (isEntry()) { + return "CounterEntry{" + + "subject='" + subject + '\'' + + ", value=" + value + + ", lastIncrement=" + lastIncrement + + ", sources=" + sources + + '}'; + } + + return "CounterEntry{" + + "status=" + status + + '}'; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterIterator.java b/counter/src/main/java/io/synadia/counter/CounterIterator.java new file mode 100644 index 0000000..4889c04 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterIterator.java @@ -0,0 +1,87 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.api.MessageInfo; +import io.synadia.direct.MessageInfoHandler; + +import java.math.BigInteger; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class CounterIterator implements Iterator, MessageInfoHandler { + private final LinkedBlockingQueue queue; + private final AtomicBoolean isFinished; + private BigInteger nextElement; + private boolean hasNextComputed; + + public CounterIterator() { + this.queue = new LinkedBlockingQueue<>(); + this.isFinished = new AtomicBoolean(false); + this.nextElement = null; + this.hasNextComputed = false; + } + + @Override + public void onMessageInfo(MessageInfo messageInfo) { +// if (endMarker.equals(value)) { +// isFinished.set(true); +// } else { +// try { +// queue.put(value); +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// throw new RuntimeException("Interrupted while adding value", e); +// } +// } + } + + @Override + public boolean hasNext() { + if (!hasNextComputed) { + computeNext(); + } + return nextElement != null; + } + + @Override + public BigInteger next() { + if (!hasNext()) { + throw new NoSuchElementException("No more elements"); + } + BigInteger result = nextElement; + hasNextComputed = false; + nextElement = null; + return result; + } + + private void computeNext() { + if (hasNextComputed) { + return; + } + + try { + // If we've already seen the end marker, no more elements + if (isFinished.get() && queue.isEmpty()) { + nextElement = null; + } else { + // Wait for next element with timeout to avoid infinite blocking + nextElement = queue.poll(100, TimeUnit.MILLISECONDS); + + // If queue is empty but not finished, keep polling + while (nextElement == null && !isFinished.get()) { + nextElement = queue.poll(100, TimeUnit.MILLISECONDS); + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for next element", e); + } + + hasNextComputed = true; + } +} diff --git a/counter/src/main/java/io/synadia/counter/CounterUtils.java b/counter/src/main/java/io/synadia/counter/CounterUtils.java new file mode 100644 index 0000000..2584697 --- /dev/null +++ b/counter/src/main/java/io/synadia/counter/CounterUtils.java @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Synadia Communications Inc. All Rights Reserved. +// See LICENSE and NOTICE file for details. + +package io.synadia.counter; + +import io.nats.client.impl.Headers; + +public final class CounterUtils { + + public static final String INCREMENT_HEADER = "Nats-Incr"; + + public static String extractVal(byte[] data) { + String s = new String(data); + // {"val":"-123"} + // don't want to assume anything about how the json is formatted/spaced + int colonAt = s.indexOf(':'); + int numberStart = s.indexOf('"', colonAt + 1) + 1; + int lastQuote = s.lastIndexOf('"'); + return s.substring(numberStart, lastQuote).trim(); + } + + public static String extractIncrement(Headers h) { + return h == null ? "0" : h.getFirst(INCREMENT_HEADER); + } +} diff --git a/counter/src/main/javadoc/images/favicon.ico b/counter/src/main/javadoc/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9464855b4b53e64a0b464a138e21734ece9a878b GIT binary patch literal 1150 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 + + + + +JetStream Distributed Counter +

Synadia Logo

+ + + + diff --git a/counter/src/main/resources/placeholder.txt b/counter/src/main/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/counter/src/main/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/counter/src/test/resources/placeholder.txt b/counter/src/test/resources/placeholder.txt new file mode 100644 index 0000000..ca5fd64 --- /dev/null +++ b/counter/src/test/resources/placeholder.txt @@ -0,0 +1 @@ +This is just a placeholder. \ No newline at end of file diff --git a/counter/test.bat b/counter/test.bat new file mode 100644 index 0000000..22c2cd1 --- /dev/null +++ b/counter/test.bat @@ -0,0 +1,5 @@ +call gradlew clean build jacocoTestReport +taskkill /F /IM nats-server.exe +start chrome file:///C:/nats/orbit.java/counter/build/reports/jacoco/test/html/index.html +start chrome file:///C:/nats/orbit.java/counter/build/reports/tests/test/index.html + diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index 6fe666d..470e189 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java index 7da03a0..ac53044 100644 --- a/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java +++ b/direct-batch/src/examples/java/io/synadia/examples/ExampleUtils.java @@ -25,7 +25,8 @@ public static void printMessageInfo(MessageInfo mi, Number listId) { System.out.println("[" + listId + "] Message" + " | subject: " + mi.getSubject() + " | sequence: " + mi.getSeq() - + " | time: " + DateTimeUtils.toRfc3339(mi.getTime())); + + " | time: " + (mi.getTime() == null ? "null" : DateTimeUtils.toRfc3339(mi.getTime())) + ); } else { if (mi.isEobStatus()) { @@ -34,13 +35,14 @@ public static void printMessageInfo(MessageInfo mi, Number listId) { else if (mi.isErrorStatus()) { System.out.print("[" + listId + "] MI Error"); } - else if (mi.isErrorStatus()) { + else if (mi.isStatus()) { System.out.print("[" + listId + "] MI Status"); } System.out.println(" | isStatus? " + mi.isStatus() + " | isEobStatus? " + mi.isEobStatus() + " | isErrorStatus? " + mi.isErrorStatus() - + " | status code: " + mi.getStatus().getCode()); + + " | status code: " + (mi.getStatus() == null ? "null" : mi.getStatus().getCode()) + ); } } diff --git a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java index bc5d0b1..2dbcebc 100644 --- a/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java +++ b/direct-batch/src/main/java/io/synadia/direct/DirectBatchContext.java @@ -23,7 +23,7 @@ public class DirectBatchContext { private final Connection conn; private final JetStreamOptions jso; private final String streamName; - final Duration timeout; + private final Duration timeout; /** * Construct a DirectBatchContext instance. diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index ff76be2..10ce07a 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -45,7 +45,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' implementation 'io.synadia:retrier:0.2.1' testImplementation 'io.nats:jnats-server-runner:1.2.8' diff --git a/request-many/build.gradle b/request-many/build.gradle index 94f7aa6..f862338 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' diff --git a/retrier/build.gradle b/retrier/build.gradle index 5d7e45b..7049817 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -38,7 +38,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats:2.21.1' + implementation 'io.nats:jnats:2.21.5' testImplementation 'io.nats:jnats-server-runner:1.2.8' testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0'